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个成员指向导出表的首地址,直接拿到导出表的虚拟地址。 - 循环查找导出表的导出函数是否与我们的函数名称一致,一致则取出导出函数地址。
- 设置导出函数位置读写属性,将新的导出函数地址写入到该位置
根据上述流程,读者可以很容易的写出导出表劫持代码,如下所示则是完整的代码片段,当运行EATHook
时,进程内如果再次获取MessageBox
函数的内存地址时则会返回MyMessageBox
自定函数地址,此时功能将被替换。
#include <windows.h>
#include <cstdio>
#include <tchar.h>
// 导出表劫持
BOOL EATHook(LPCTSTR szDllName, LPCTSTR szFunName, LPVOID NewFun)
{
DWORD addr = 0, index = 0, dwProtect = 0;
// 装入模块
HMODULE DllBase = LoadLibrary(szDllName);
if (NULL == DllBase)
{
return(FALSE);
}
// 得到Dos头NT头数据目录表
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)DllBase;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptHeader = (PIMAGE_OPTIONAL_HEADER)(&pNtHeader->OptionalHeader);
// 得到导出表的虚拟地址
PIMAGE_EXPORT_DIRECTORY pExpDes = (PIMAGE_EXPORT_DIRECTORY)
((PBYTE)DllBase pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
// 获取导出表的函数地址,函数名称,函数序号
PULONG pAddressOfFunctions = (PULONG)((PBYTE)DllBase pExpDes->AddressOfFunctions);
PULONG pAddressOfNames = (PULONG)((PBYTE)DllBase pExpDes->AddressOfNames);
PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)DllBase pExpDes->AddressOfNameOrdinals);
// 循环查找
for (int i = 0; i < pExpDes->NumberOfNames; i)
{
index = pAddressOfNameOrdinals[i];
// 得到导出函数名
LPCTSTR pFuncName = (LPTSTR)((PBYTE)DllBase pAddressOfNames[i]);
// 对比函数名
if (!_tcscmp((LPCTSTR)pFuncName, szFunName))
{
// 返回地址
addr = pAddressOfFunctions[index];
break;
}
}
// 设置读写属性
VirtualProtect(&pAddressOfFunctions[index], 0x1000, PAGE_READWRITE, &dwProtect);
// 设置导出函数
pAddressOfFunctions[index] = (DWORD)NewFun - (DWORD)DllBase;
// 写出替换
WriteProcessMemory(GetCurrentProcess(), &pAddressOfFunctions[index],
(LPCVOID)((DWORD)NewFun - (DWORD)DllBase), sizeof(NewFun), &dwProtect);
return(TRUE);
}
// 自定义导出函数
int __stdcall MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
printf("hello lyshark n");
return(0);
}
typedef int (WINAPI* LPFNMESSAGEBOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
int main(int argc, char *argv[])
{
// 对MessageBoxA进行Eat Hook
EATHook("USER32.dll", "MessageBoxA", MyMessageBox);
// 模拟下次调用后就是执行我们的Hook代码
LoadLibrary("USER32.dll");
HMODULE hDll = GetModuleHandle("USER32.dll");
LPFNMESSAGEBOX lpMessageBox = (LPFNMESSAGEBOX)GetProcAddress(hDll, "MessageBoxA");
lpMessageBox(NULL, "Hello, EAT Hook", "Info", MB_OK);
system("pause");
return(0);
}
上述代码被运行后,针对外部调用MessageBoxA
函数都会转向到MyMessageBox
函数上,至此传入的参数将会失效,如下图所示;
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/b550753d.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!