IAThook的原理学习帖子有很多,我需要复现一下几个基本概念先:
1. PE结构下IID数组相关指针(放假太久都快忘了各个指针位置了)
这个之前我写过简单的分析工具,参考:https://bbs.pediy.com/thread-255851.htm 结构总览:
2. 主要思路:
虽然r3层的钩子是对进程局部hook,但是实现iat hook为什么就一定要外注入DLL呢(手动狗头)
程序运行有一个小弹窗,然后开始hook,利用GetModuleHandle(NULL)获取当前进程模块句柄,然后确定目标 dll名字和目标函数名称:(char*)"user32.dll", (char*)"MessageBoxA" ,LoadLibrary动态加载起来目标dll,获得相应的基质和GetProcAddress目标函数指针(这里对PE的结构处理函数可参考我之前的帖子https://bbs.pediy.com/thread-255851.htm 对PE的基本处理函数应有尽有)。找到目标函数之后,需要进行权限修改,因为目标只有读权限,这里借鉴了加密与解密。 找到之后获得目标函数地址后赋给我们之前定义好的Detor,就可以对源messagebox就行修改了。
3.完整代码如下:
vs2019: 如果提示const char[]转 char* 就对函数形参加const, 项目属性改成多字符集
#include <windows.h> #include <stdio.h> #include <imagehlp.h> #pragma comment(lib,"imagehlp.lib") //以MessageBoxA的原型定义一个函数指针类型 typedef int (WINAPI* PMessageBoxA)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); //以MessageBoxA的原型定义一个函数来替代原始的MessageBoxA Detor函数 int WINAPI My_MessageBoxA(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT uType); //存在以下关系 //*(*pThunkPointer) == *pOriginalFuncAddr ; BOOL InstallHook( HMODULE hModToHook, char* szModuleName, char* szFuncName, PVOID ProxyFunc, PULONG_PTR* pThunkPointer, ULONG_PTR* pOriginalFuncAddr ); VOID ShowMsgBox(const char* szMsg); BOOL IAT_InstallHook(); VOID IAT_UnInstallHook(); //保存原始MessageBoxA的地址 PMessageBoxA OldMessageBox = NULL; //指向IAT中pThunk的地址 PULONG_PTR g_PointerToIATThunk = NULL; int main(int argc, char* argv[]) { ShowMsgBox("1+1=2 ? 对吗"); IAT_InstallHook(); ShowMsgBox("八对"); IAT_UnInstallHook(); ShowMsgBox("hook卸载完成"); return 0; } //之所以把这个调用单独放在一个函数中,是因为Release模式下对调用进行了优化,第二次调用时直接采用了寄存器寻址而不是导入表 //因此,单独放在一个函数中可以避免这个情况。 VOID ShowMsgBox(const char* szMsg) { MessageBoxA(NULL, szMsg, "Test", MB_OK); } int WINAPI My_MessageBoxA( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ) { int ret; char newText[1024] = { 0 }; char newCaption[256] = "jentle"; printf("MessageBox丢失! "); //在调用原函数之前,可以对IN(输入类)参数进行干涉 lstrcpy(newText, lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作 lstrcat(newText, " MessageBox Hacked by jentle");//篡改的消息框内容 ret = OldMessageBox(hWnd, newText, newCaption, uType);//调用原MessageBox,并保存返回值 return ret;//这里你还可以干涉原始函数的返回值 } BOOL IAT_InstallHook() { BOOL bResult = FALSE; HMODULE currectExe = GetModuleHandle(NULL); PULONG_PTR pt; ULONG_PTR OrginalAddr; bResult = InstallHook(currectExe, (char*)"user32.dll", (char*)"MessageBoxA", (PVOID)My_MessageBoxA, &pt, &OrginalAddr); if (bResult) { printf("************************Hook完毕! pThunk=0x%p OriginalAddr = 0x%p********************************* ", pt, OrginalAddr); g_PointerToIATThunk = pt; OldMessageBox = (PMessageBoxA)OrginalAddr; } return bResult; } VOID IAT_UnInstallHook() { DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; if (g_PointerToIATThunk) { //查询并修改内存页的属性 VirtualQuery((LPCVOID)g_PointerToIATThunk, &mbi, sizeof(mbi)); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD); //将原始的MessageBoxA地址填入IAT中 *g_PointerToIATThunk = (ULONG)OldMessageBox; //恢复内存页的属性 VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0); } } BOOL InstallHook( HMODULE hModToHook, //待Hook的模块基址 char* szModuleName, //目标DLL名字 char* szFuncName, //目标函数名字 PVOID DetourFunc, //Detour函数 PULONG_PTR* pThunkPointer, // ULONG_PTR* pOriginalFuncAddr ) { PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; PIMAGE_THUNK_DATA pThunkData; ULONG ulSize; HMODULE hModule = 0; ULONG_PTR TargetFunAddr; PULONG_PTR lpAddr; char* szModName; BOOL result = FALSE; BOOL bRetn = FALSE; hModule = LoadLibrary(szModuleName);//动态加载目标DLL TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName); //目标函数指针 printf("目标%s函数地址 :0x%p ", szFuncName, TargetFunAddr); printf("hook模块基质(此exe):0x%p ", hModToHook); //需要hook的程序,这个是此程序 pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);; //IID指针 printf("IID数组地址:0x%p ", pImportDescriptor); while (pImportDescriptor->FirstThunk) { szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name); printf("当前模块(DLL)名称:%s ", szModName); if (stricmp(szModName, szModuleName) != 0) { printf("DLL名字不对下一个 "); pImportDescriptor++; continue; } //程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断 pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk); while (pThunkData->u1.Function) { lpAddr = (ULONG_PTR*)pThunkData; //找到了地址 if ((*lpAddr) == TargetFunAddr) { printf("目标函数找到! "); //通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写 DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(lpAddr, &mbi, sizeof(mbi)); bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); if (bRetn) { //内存页属性修改成功,继续下一步操作,先保存原始数据 if (pThunkPointer != NULL) { *pThunkPointer = lpAddr; } if (pOriginalFuncAddr != NULL) { *pOriginalFuncAddr = *lpAddr; } //修改地址 *lpAddr = (ULONG_PTR)DetourFunc; result = TRUE; //恢复内存页的属性 VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0); } break; } pThunkData++; } pImportDescriptor++; } FreeLibrary(hModule); return result; }
4. 运行情况:
确定之后返回:
区别正常逻辑: