简单的内存监视实现
前言:
前段时间学习了API HOOK,对这技术也略知一二,决定利用这技术实现个小功能。
以前有用过某工具,可以偷取别人外挂的功能地址,当时想想觉得挺不可思议的,如今了解API HOOK后,觉得这功能也就那样。
废话不多说,进入正题。
一、 HOOK WriteProcessMemory
首先看看WriteProcessMemory()原型:
BOOL WriteProcessMemory(
HANDLEhProcess, // handle to process
LPVOIDlpBaseAddress, // base of memory area
LPVOIDlpBuffer, // data buffer
DWORDnSize, // number of bytes to write
LPDWORDlpNumberOfBytesWritten // number of bytes written
);
如何实现呢?其实很简单,我们只需是获取hProcess/ lpBaseAddress/ nSize/ lpBuffer这四个参数。
我们定义一个结构体:
typedef struct HookData { HANDLE hProcess, // handle to process LPVOID lpBaseAddress, // base of memory area LPVOID lpBuffer, // data buffer DWORD nSize, // number of bytes to write }HookData,*pHookData;
并定义一个全局变量HookData g_hookdata;
在我们自己的函数体里保存四个参数,然后返回原函数:
BOOL MyWriteProcessMemory( HANDLE hProcess, // handle to process LPVOID lpBaseAddress, // base of memory area LPVOID lpBuffer, // data buffer DWORD nSize, // number of bytes to write LPDWORD lpNumberOfBytesWritten // number of bytes written ) { g_hookdata.hProcess = hProcess; g_hookdata.lpBaseAddress = lpBaseAddress; g_hookdata.lpBuffer = lpBuffer; g_hookdata.nSize =nSize; return WriteProcessMemory(hProcess,lpBaseAddress,lpBuffer,nSize,lpNumberOfBytesWritten); }
获取这四个参数后,如何将这些数据取出来,我这里抛砖引玉,用一种笨方法,把数据以文件方式保存,然后外部程序读取:
BOOL SaveHookData(HANDLE hpro,pHookData phd) { if (g_hookdata.hProcess != hpro)//判断是否对目标进程HOOK成功 return FALSE; FILE *fp = fopen("hookdata","r+"); if (!fp) return FALSE; //打开文件失败 if (!fwrite(phd,sizeof(HookData),sizeof(phd),fp)) { fclose(fp); return FALSE; } fclose(fp); return TRUE; } BOOL TakeHookData(pHookData phd) { FILE *fp = fopen("c:\\hookdata","r+"); if (!fp) return FALSE; //打开文件失败 if (!fread(phd,sizeof(HookData),1,fp)) { fclose(fp); return FALSE; } fclose(fp); return TRUE; }
二、HOOK ReadProcessMemory
ReadProcessMemory()原型:
BOOL ReadProcessMemory(
HANDLE hProcess, // handle to the process
LPCVOID lpBaseAddress, // base of memory area
LPVOID lpBuffer, // data buffer
DWORD nSize, // number of bytes to read
LPDWORD lpNumberOfBytesRead // number of bytes read
);
同HOOK WriteProcessMemory()类似,需要获取 hProcess/ lpBaseAddress/ nSize/ lpBuffer这四个参数,不过有一点不同,ReadProcessMemory()的lpBuffer参数不是在一开始就有数据。我们先看看ReadProcessMemory 的反汇编代码:
751A9982 > 8BFF mov edi, edi ; 751A9984 . 55 push ebp 751A9985 . 8BEC mov ebp, esp 751A9987 . 8D45 14 lea eax, dword ptr [ebp+14] 751A998A . 50 push eax ; 751A998B . FF75 14 push dword ptr [ebp+14] ; 地址 751A998E . FF75 10 push dword ptr [ebp+10] ; data buffer 751A9991 . FF75 0C push dword ptr [ebp+C] ; nSize 751A9994 . FF75 08 push dword ptr [ebp+8] ; 751A9997 . FF15 B4111A75 call dword ptr [<&ntdll.NtReadVirtualMemory>] ; ntdll.ZwReadVirtualMemory 751A999D . 8B4D 18 mov ecx, dword ptr [ebp+18] 751A99A0 . 85C9 test ecx, ecx 751A99A2 . 75 0F jnz short 751A99B3 751A99A4 > 85C0 test eax, eax 751A99A6 . 0F8C 789E0100 jl 751C3824 751A99AC . 33C0 xor eax, eax 751A99AE . 40 inc eax 751A99AF > 5D pop ebp 751A99B0 . C2 1400 retn 14 751A99B3 > 8B55 14 mov edx, dword ptr [ebp+14] ; LPCVOID lpBaseAddress, // base of memory area 751A99B6 . 8911 mov dword ptr [ecx], edx ; lpBuffer, // data buffer 751A99B8 .^ EB EA jmp short 751A99A4
我们可以得出当程序执行到751A99B3才开始读取lpBaseAddress的值到lpBuffer,所以如果HOOK函数头,那么截取到的内容肯定为空。
那到底如何实现呢?其实有个很取巧的方法,我们同样HOOK函数头,截取lpBaseAddress参数后再自己手动将该地址的内容读取出来,用memcpy()函数就能实现。
实现方法与上面的HOOK WriteProcessMemory类似,没必要重复写了。
三、总结
相信看完篇文章,你可以揭开“偷取外挂功能地址”的神秘面纱。由于本人也是菜鸟,只能完成这些,代码没有经过测试,有什么地方有错误,可以给我留言,大家一起探讨,大神们看完可以指点指点。
本文所实现的方法对做了反监视、非远程内存读写式外挂无效,对于后者,只要弄清楚外挂的实现方式,理论上按照本方法,HOOK关键函数,是可以完成的。
JJHUANG
2013/8/28