| 
 | 
 
EAT(Export Address Table)用于修改动态链接库(DLL)中导出函数的调用。与IAT Hook不同,EAT Hook是在DLL自身中进行钩子操作,而不是修改应用程序的导入表。它的原理是通过修改DLL的导出函数地址,将原本要导出的函数指向另一个自定义的函数。这样,在应用程序调用DLL的导出函数时,实际上会执行自定义的函数。 
 
EAT Hook的步骤通常包括以下几个步骤: 
 
获取目标DLL的基址:通过模块加载和遍历PE文件的导出表,找到目标DLL的基址。 
定位导出函数:根据导出函数的名称或序号,在导出表中找到目标函数的位置。 
保存原始函数地址:将目标函数的地址保存下来,以便后续恢复。 
修改导出函数地址:将目标函数在导出表中对应的地址修改为自定义函数的地址。 
实现自定义函数:编写自定义的函数,该函数会在被钩子函数被调用时执行。 
调用原始函数:在自定义函数中,可以选择是否调用原始的被钩子函数。 
与IAT不同是EAT存放的不是函数地址,而是导出函数地址的偏移,使用时需要加上指定Dll的模块基地址,当Hook挂钩之后,所有试图通过导出表获取函数地址的行为都会受到影响,EATHook并不会直接生效,它只能影响Hook之后对该函数地址的获取。 
 
实现导出表劫持的详细流程如下所示: 
 
首先获取到DOS头,并加上偏移得到NT头,再通过Nt头得到数据目录表基地址。 
数据目录表DataDirectory中的第0个成员指向导出表的首地址,直接拿到导出表的虚拟地址。 
循环查找导出表的导出函数是否与我们的函数名称一致,一致则取出导出函数地址。 
设置导出函数位置读写属性,将新的导出函数地址写入到该位置 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
- <火山程序 类型 = "通常" 版本 = 1 />
 
  
- 类 类_EATHOOK <公开 注释 = "EATHOOK类" 折叠 @输出名 = "L_EATHOOK"
 
 -         @视窗.外部头文件 = "<windows.h>\r\n<cstdio>\r\n<psapi.h>\r\n<type_traits>">
 
 - {
 
 -     变量 集_跳板函数 <类型 = 变整数>
 
  
-     #
 
 -     # @begin
 
 -     # // 根据架构选择正确的NT头结构
 
 -     # #ifdef _WIN64
 
 -     # using PIMAGE_NT_HEADERS_EX = PIMAGE_NT_HEADERS64;
 
 -     # using PIMAGE_OPTIONAL_HEADER_EX = PIMAGE_OPTIONAL_HEADER64;
 
 -     # // DWORD IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64;
 
 -     # #else
 
 -     # using PIMAGE_NT_HEADERS_EX = PIMAGE_NT_HEADERS32;
 
 -     # using PIMAGE_OPTIONAL_HEADER_EX = PIMAGE_OPTIONAL_HEADER32;
 
 -     # //constexpr DWORD IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32;
 
 -     # #endif
 
 -     # struct HookContext {
 
 -     #     DWORD_PTR* pFuncAddress;  // 导出表中函数地址的指针
 
 -     #     DWORD_PTR originalRva;    // 原始RVA值
 
 -     #     HMODULE hModule;          // 模块基址
 
 -     # };
 
 -     # HookContext g_hookContext; 
 
 -     # INT_P oldFunc = 0;
 
 -     # // 原始函数指针类型
 
 -     # @end
 
  
-     方法 写入长JMP <类型 = 字节集类 折叠>
 
 -     参数 目标地址 <类型 = 变整数>
 
 -     {
 
 -         变量 局_代码 <类型 = 字节集类>
 
 -         局_代码 = 创建字节集 (0xFF, 0x25, 0x0, 0x0, 0x0, 0x0)
 
 -         局_代码.添加字节集 (到字节集 (目标地址))
 
 -         返回 (局_代码)
 
 -     }
 
  
-     方法 申请附近的内存 <类型 = 变整数 折叠 "">
 
 -     参数 参_开始地址 <类型 = 变整数>
 
 -     参数 参_大小 <类型 = 整数>
 
 -     参数 参_是否附近 <类型 = 逻辑型 @默认值 = 真>
 
 -     {
 
 -         变量 局_申请的地址 <类型 = 变整数>
 
 -         变量 局_指针 <类型 = 变整数>
 
 -         局_指针 = 参_开始地址
 
 -         循环判断首 ()
 
 -         {
 
 -             如果 (参_是否附近)
 
 -             {
 
 -                 @ @<局_申请的地址> = (INT_P)VirtualAlloc((LPVOID)@<局_指针>, @<参_大小>, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
 
 -             }
 
 -             否则
 
 -             {
 
 -                 @ @<局_申请的地址> = (INT_P)VirtualAlloc(NULL, @<参_大小>, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
 
 -             }
 
 -             变量 局_方向 <类型 = 字节 值 = 0>
 
 -             变量 局_增加量 <类型 = 变整数>
 
  
-             如果 (局_申请的地址 == 0)  // 申请附近的内存失败了
 
 -             {
 
 -                 如果 (局_方向 == 0)
 
 -                 {
 
 -                     如果 (参_开始地址 + 2147483642 >= 局_指针)  // 不能跳转了 2147483647-5
 
 -                     {
 
 -                         局_增加量 = 局_增加量 + 10240
 
 -                     }
 
 -                     否则
 
 -                     {
 
 -                         局_增加量 = 0
 
 -                         局_方向 = 1
 
 -                     }
 
 -                 }
 
 -                 否则
 
 -                 {
 
 -                     如果 (参_开始地址 - 2147483642 >= 局_指针)  // 不能跳转了 2147483647-5
 
 -                     {
 
 -                         局_增加量 = 局_增加量 - 10240
 
 -                     }
 
 -                     否则
 
 -                     {
 
 -                         返回 (0)
 
 -                     }
 
 -                 }
 
 -                 局_指针 = 局_指针 + 局_增加量
 
 -             }
 
  
-         }
 
 -         循环判断尾 (局_申请的地址 == 0)
 
  
-         返回 (局_申请的地址)
 
 -     }
 
  
-     方法 开始HOOK <公开 类型 = 逻辑型 折叠 @输出名 = "StartHook" @禁止流程检查 = 真>
 
 -     参数 参_DLL名字 <类型 = 文本型 @输出名 = "wDll">
 
 -     参数 参_函数名 <类型 = 文本型 @输出名 = "wFunc">
 
 -     参数 参_回调函数 <类型 = 变整数 @输出名 = "lpCallBak">
 
 -     {
 
 -         变量 局_函数 <类型 = 字节集类>
 
 -         局_函数 = 文本到多字节 (参_函数名, 假)
 
 -         // 获取模块基址(不增加引用计数)
 
 -         @     HMODULE hModule = GetModuleHandleW(@<参_DLL名字>);
 
 -         @     if (!hModule)
 
 -         @     {
 
 -         调试输出 ("错误: 无法获取模块 %ls\n", 参_DLL名字)
 
 -         @         return false;
 
 -         @     }
 
 -         变量 局_DLL句柄 <类型 = 变整数>
 
 -         @  @<局_DLL句柄>= (INT_P)hModule;
 
 -         如果 (为64位程序 ())
 
 -         {
 
 -             集_跳板函数 = 申请附近的内存 (局_DLL句柄, 0x32, 真)
 
 -             变量 局_跳转2 <类型 = 字节集类>
 
 -             局_跳转2 = 写入长JMP (参_回调函数)
 
 -             内存复制 (集_跳板函数, 局_跳转2.取字节集指针 (), 局_跳转2.取字节集长度 ())
 
 -         }
 
 -         否则
 
 -         {
 
 -             集_跳板函数 = 参_回调函数
 
 -         }
 
  
 
-         @     // 验证PE头
 
 -         @     auto* pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(hModule);
 
 -         @     if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
 
 -         @     {
 
 -         调试输出 ("错误: 无效的DOS头\n")
 
 -         @         return false;
 
 -         @     }
 
 -         @ 
 
 -         @     auto* pNtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS_EX>(
 
 -         @         reinterpret_cast<BYTE*>(hModule) + pDosHeader->e_lfanew);
 
 -         @ 
 
 -         @     if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
 
 -         @     {
 
 -         调试输出 ("错误: 无效的NT头\n")
 
 -         @         return false;
 
 -         @     }
 
 -         @ 
 
 -         @     // 获取导出表
 
 -         @     auto& exportDir = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
 
 -         @     if (exportDir.VirtualAddress == 0 || exportDir.Size == 0)
 
 -         @     {
 
 -         调试输出 ("错误: 模块没有导出表\n")
 
 -         @         return false;
 
 -         @     }
 
 -         @ 
 
 -         @     auto* pExportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
 
 -         @         reinterpret_cast<BYTE*>(hModule) + exportDir.VirtualAddress);
 
 -         @ 
 
 -         @     // 获取导出表数组
 
 -         @     DWORD* pNames = reinterpret_cast<DWORD*>(
 
 -         @         reinterpret_cast<BYTE*>(hModule) + pExportDir->AddressOfNames);
 
 -         @     WORD* pOrdinals = reinterpret_cast<WORD*>(
 
 -         @         reinterpret_cast<BYTE*>(hModule) + pExportDir->AddressOfNameOrdinals);
 
 -         @     DWORD* pFunctions = reinterpret_cast<DWORD*>(
 
 -         @         reinterpret_cast<BYTE*>(hModule) + pExportDir->AddressOfFunctions);
 
 -         @ 
 
 -         @     // 遍历导出表
 
 -         @     for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i)
 
 -         @     {
 
 -         @         const char* name = reinterpret_cast<const char*>(
 
 -         @             reinterpret_cast<BYTE*>(hModule) + pNames[i]);
 
 -         @ 
 
 -         @         if (strcmp(name, (const char *)@<局_函数>.GetPtr()) == 0)
 
 -         @         {
 
 -         @             WORD ordinal = pOrdinals[i];
 
 -         @             if (ordinal >= pExportDir->NumberOfFunctions)
 
 -         @             {
 
 -         调试输出 ("错误: 无效的导出序号\n")
 
 -         @                 return false;
 
 -         @             }
 
 -         @ 
 
 -         @             // 计算函数地址指针
 
 -         @             DWORD_PTR* pFuncAddr = reinterpret_cast<DWORD_PTR*>(&pFunctions[ordinal]);
 
 -         @ 
 
 -         @             // 修改内存保护
 
 -         @             DWORD oldProtect = 0;
 
 -         @             if (!VirtualProtect(pFuncAddr, sizeof(DWORD_PTR), PAGE_READWRITE, &oldProtect))
 
 -         @             {
 
 -         调试输出 ("错误: VirtualProtect失败 (0x%X)\n")
 
 -         @                 return false;
 
 -         @             }
 
 -         @ 
 
 -         @             // 保存原始地址
 
 -         @             oldFunc = (INT_P)hModule+*(int*)pFuncAddr;
 
 -         @            g_hookContext.pFuncAddress = pFuncAddr;
 
 -         @            g_hookContext.originalRva = *(int*)pFuncAddr;
 
 -         @            g_hookContext.hModule = hModule;
 
  
-         @             // 计算新地址的RVA 如果64位的情况下,会出现问题,导致偏移错误
 
 -         @             *pFuncAddr = static_cast<DWORD_PTR>(
 
 -         @                 reinterpret_cast<BYTE*>(@<集_跳板函数>) - reinterpret_cast<BYTE*>(hModule));
 
 -         @ 
 
 -         @             // 恢复内存保护
 
 -         @             VirtualProtect(pFuncAddr, sizeof(DWORD_PTR), oldProtect, &oldProtect);
 
 -         @ 
 
 -         调试输出 ("成功")
 
 -         @             return true;
 
 -         @         }
 
 -         @     }
 
 -         @ 
 
 -         调试输出 ("错误: 未找到函数 %s\n", 参_函数名)
 
 -         @     return false;
 
  
-     }
 
  
-     方法 卸载HOOK <公开 类型 = 逻辑型 折叠 @输出名 = "UnhookEAT" @禁止流程检查 = 真>
 
 -     {
 
 -         @     if (!g_hookContext.pFuncAddress) return false;
 
 -         @     DWORD oldProtect = 0;
 
 -         @     if (!VirtualProtect(g_hookContext.pFuncAddress, sizeof(DWORD_PTR), PAGE_READWRITE, &oldProtect)) {
 
 -         @         return false;
 
 -         @     }
 
 -         @     // 恢复原始RVA
 
 -         @ *g_hookContext.pFuncAddress = g_hookContext.originalRva;
 
 -         @     VirtualProtect(g_hookContext.pFuncAddress, sizeof(DWORD_PTR), oldProtect, &oldProtect);
 
 -         @     return true;
 
  
-     }
 
  
-     方法 取原函数地址 <公开 类型 = 变整数 折叠 @禁止流程检查 = 真>
 
 -     {
 
 -         @ return  oldFunc;
 
 -     }
 
  
-     方法 调用原函数 <公开 注释 = "调用所指定的类静态方法" 折叠 @嵌入式方法 = "">
 
 -     参数 所欲调用的方法地址 <类型 = 变整数 注释 = "提供所欲调用类静态方法的地址,该地址可以使用"取静态方法地址"方法获取." "">
 
 -     参数 返回值类型 <注释 = "  该返回值数据类型必须与被调用方法的返回值数据类型保持一致," 注释 = "否则将导致不可意料的问题." @需求类型 = 数据类型 @匹配类型 = 通用型
 
 -             @返回值类型 = 0>
 
 -     参数 调用参数表 <注释 = "  调用参数表的格式务必和被调用方法的参数表一致,否则将导致不" 注释 = "可意料的问题." @可扩展 = "" @匹配类型 = 通用型>
 
 -     {
 
 -         @ ((@<返回值类型> (CALLBACK *) (@pdt_list<调用参数表>))@<所欲调用的方法地址>) (@<调用参数表>)
 
 -     }
 
  
-     #
 
 - }
 
 
  复制代码 
 
 |   
 
 
 
 |