簡單的內存監視實現
前言:
前段時間學習了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