IAT Hook
IAT hook
一丶IAT
1.什么是 IAT表.
熟悉PE结构的应该知道.IAT 是导入表.
其IAT表如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; 指向INT表 4个字节一组.是RVA指向名字跟序号 } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; 在文件中跟INT表一样.这是IAT } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
我们知道PE有两种状态.第一种.在文件中的状态. 所以才有 VA 转 FOA等等的互相转换.
扯多了.
在文件状态. IAT表(firstThunk)跟 INT表一样.都是指向一个很大的表.这个表里面是4个字节进行存储.存储的是Rva. 这些RVA分别指向 导入序号以及以0结尾的字符串.
如果在内存状态.则INT表跟上面说的文件状态一样指向 导入序号.以及导入的函数名字.
而IAT此时不同了.IAT此时就是保存着INT指向的导入函数的地址了.
如果还不理解.看下以前关于IAT博客.
https://www.cnblogs.com/iBinary/p/9740757.html
如下图所示:
其实IAT就是保存函数地址.
2.怎么进行HOOK
熟悉了IAT 那么HOOK就很简单了.首先你要会解析PE.
原理就是:
1.编写DLL.注入到你想HOOK的程序中.
2.编写DLL,DLL里面获取你HOOK程序的 ImageBase以及各种头(DOS,NT,FILE,OPT)
3.DLL 里面通过OPT的数据目录第一项.得到导入表RVA.加上ImageBase定位到导入表
4.循环遍历导入表.导入表是一行04个字节.最后一项为0
5.通过导入表找到IAT表.继续遍历IAT表.
6.判断IAT中的函数地址,是否是你要进行HOOK的函数地址.
是: 则进行替换函数地址.比如替换为你的.一定要注入调用约定.
不是: 继续循环.
在IAT表中没找到.说明没在这个导入表中.导入表+1(一个导入表结构大小)
继续循环 4 5 6步.
说的比较复杂,其实原理很简单.
首先第一步.准备一个测试程序.测试程序调用MessageBoxA.
且加载我们的DLL(当然你编写的DLL一般是注入的别的进程中.我这里演示就直接加载自己进行HOOK自己).
测试程序如下:
#include <stdio.h> #include <stdlib.h> #include <windows.h> int main() { getchar(); ::MessageBoxA(NULL, "没有HOOK", NULL, NULL); LoadLibrary("IAThook.dll"); //加载HOOK的DLL getchar(); //HOOK后进行测试的程序. __asm { push 0 push 0 push 0 push 0 call dword ptr ds : [MessageBoxA] ; } __asm { push 0 push 0 push 0 push 0 call dword ptr ds : [MessageBoxA] ; } __asm { push 0 push 0 push 0 push 0 call dword ptr ds : [MessageBoxA] ; } }
上面的内敛汇编是因为我自己HOOK自己了可以.但是都在第一次调用MessageBox函数的时候编译器会保存这个MessageBox函数的地址.就算我HOOK完毕了.它再次调用还是调用的以前的.所以我先改成下面这样.
HOOK的DLL
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "framework.h" #include <windows.h> //创建相同函数指针. typedef int (WINAPI *PfnMsgA)( _In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType); PfnMsgA g_OldPfnMsgA = nullptr; int WINAPI MyMessageBox(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType) { char szHookText[] = "IBinary -> Iat Hook"; if (g_OldPfnMsgA != nullptr) { return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//调用以前的 } return 0; } void SetIatHook() { MessageBoxA(NULL, "开始进行HOOK", NULL, NULL); PVOID pHookAddress = nullptr; pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //你要HOOK的函数. if (nullptr == pHookAddress) { OutputDebugString(TEXT("获取函数地址失败")); MessageBoxA(NULL, "获取函数地址失败HOOK", NULL, NULL); return; } g_OldPfnMsgA =(PfnMsgA) pHookAddress; //保存旧的函数指针. //解析PE头.寻找IAT. HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase PIMAGE_DOS_HEADER pDosHead =(PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头 DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew; PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp; PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader; //寻找导出表的位置. DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移. //定位到导出表 dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal; PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp; PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport; DWORD *pFirstThunk; //导入表子表,也就是IAT存储函数地址的表. //遍历导入表 while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL) { dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到导入表 pFirstThunk = (DWORD *)dwTemp; //加上偏移才是真正的导入表. while (*(DWORD*)pFirstThunk != NULL) { //遍历子表 if (*(DWORD*)pFirstThunk == (DWORD)g_OldPfnMsgA) { //找到要修改的导入表了//修改内存保护属性.写入我们新的函数地址. DWORD oldProtected; VirtualProtect(pFirstThunk,0x1000, PAGE_EXECUTE_READWRITE,&oldProtected); dwTemp = (DWORD)MyMessageBox; memcpy(pFirstThunk, (DWORD *)&dwTemp,4); //将变量中保存的函数地址拷贝到导入表中. VirtualProtect(pFirstThunk,0x1000,oldProtected,&oldProtected); } pFirstThunk++; //继续遍历. } pCurrent++; //每次是加一个导入表结构. } } void UnIatHook() { /* 1.遍历导入表.恢复导入表即可. */ MessageBoxA(NULL, "开始进行HOOK", NULL, NULL); PVOID pHookAddress = nullptr; pHookAddress = MyMessageBox; if (nullptr == pHookAddress) { OutputDebugString(TEXT("获取函数地址失败")); MessageBoxA(NULL, "恢复函数地址失败HOOK", NULL, NULL); return; } //解析PE头.寻找IAT. HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头 DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew; PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp; PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader; //寻找导出表的位置. DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移. //定位到导出表 dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal; PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp; PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport; DWORD* pFirstThunk; //导入表子表 //遍历导入表 while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL) { dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL); pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的导入表. while (*(DWORD*)pFirstThunk != NULL) { //遍历子表 if (*(DWORD*)pFirstThunk == (DWORD)MyMessageBox) //如果是我们的函数地址.则进行恢复. { //找到要修改的导入表了//修改内存保护属性.写入我们新的函数地址. DWORD oldProtected; VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected); dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中. VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected); } pFirstThunk++; //继续遍历. } pCurrent++; //每次是加一个导入表结构. } } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: SetIatHook(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; }
测试代码如下:
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/10975839.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能.

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具