书上的一个例子,源码如下
// 09HookDemo.cpp文件
#include <windows.h>
#include <stdio.h>
// 挂钩指定模块hMod对MessageBoxA的调用
BOOL SetHook(HMODULE hMod);
// 定义MessageBoxA函数原型
typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType);
// 保存MessageBoxA函数的真实地址
PROC g_orgProc = (PROC)MessageBoxA;
int main()
{
// 调用原API函数
::MessageBox(NULL, "原函数", "09HookDemo", 0);
// 挂钩后再调用
SetHook(::GetModuleHandle(NULL));
::MessageBox(NULL, "原函数", "09HookDemo", 0);
return 0;
}
// 用于替换MessageBoxA的自定义函数
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return ((PFNMESSAGEBOX)g_orgProc)(hWnd, "新函数", "09HookDemo", uType);
}
BOOL SetHook(HMODULE hMod)
{
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hMod;
IMAGE_OPTIONAL_HEADER * pOptHeader =
(IMAGE_OPTIONAL_HEADER *)((BYTE*)hMod + pDosHeader->e_lfanew + 24);
IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)
((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// 在导入表中查找user32.dll模块。因为MessageBoxA函数从user32.dll模块导出
while(pImportDesc->FirstThunk)
{
char* pszDllName = (char*)((BYTE*)hMod + pImportDesc->Name);
if(lstrcmpiA(pszDllName, "user32.dll") == 0)
{
break;
}
pImportDesc++;
}
if(pImportDesc->FirstThunk)
{
// 一个IMAGE_THUNK_DATA就是一个双字,它指定了一个导入函数
// 调入地址表其实是IMAGE_THUNK_DATA结构的数组,也就是DWORD数组
IMAGE_THUNK_DATA* pThunk = (IMAGE_THUNK_DATA*)
((BYTE*)hMod + pImportDesc->FirstThunk);
while(pThunk->u1.Function)
{
// lpAddr指向的内存保存了函数的地址
DWORD* lpAddr = (DWORD*)&(pThunk->u1.Function);
if(*lpAddr == (DWORD)g_orgProc)
{
// 修改IAT表项,使其指向我们自定义的函数,相当于“*lpAddr = (DWORD)MyMessageBoxA;”
DWORD* lpNewProc = (DWORD*)MyMessageBoxA;
::WriteProcessMemory(::GetCurrentProcess(),
lpAddr, &lpNewProc, sizeof(DWORD), NULL);
return TRUE;
}
pThunk++;
}
}
return FALSE;
}
这个源码在Debug模式下可以成功,但是Release下就会失败,但是IAT也修改了~原因如下:
至于为何WriteProcessMemory成功了却没有勾住,我猜你的代码是这样的
MessageBox(....);
HookIAT(...);
MessageBox(....);
然后发现第二次调用MessageBox未受影响,这是因为release下编译器有优化
它发现你要调用两次MessageBox,因此在调用第一次MessageBox前把MessageBox的地址从IAT中读取到一个稳定寄存器中,一般是esi,因为调用约定是esi等在函数调用前后不变
你的HookIAT函数修改的仅仅是IAT中的地址,然而第二次MessageBox调用是call esi,不从IAT中读取了,因此这次调用不受影响
第二个VirtualProtect会失败,内存分配失败指的是lpflOldProtect为NULL
lpflOldProtect
[out] Pointer to a variable that receives the previous access protection value of the first page in the specified region of pages. If this parameter is NULL or does not point to a valid variable, the function fails.
函数没替换成功,找个调试器,在WriteProcessMemory后边写个int 3,然后一步步跟这看看..
贴完整代码上来~
程序代码是不可写的,DEBUG版可能采取了特殊措施,把代码拷贝了出来执行的。要修改代码段,用VirtualProtect赋与PAGE_WRITECOPY权限
如果你的代码安排是我所说的,换成
HookIAT(...);
MessageBox(....);
就应该有效
如果 debug 版本正确,把这段代码的编译全局优化关掉试一试
#pragma optimize("g", off)
...// 你的代码
#pragma optimize("", on)