EAT HOOK与IAT HOOK原理是一样的,都是通过修改PE来达到HOOK的目的。只是EAT HOOK是从来源入手而IAT HOOK则是从自身入手。但有一点值得注意的就是EAT HOOK只能对后面通过动态加载进来的API有效,对原本已经在IAT表中获取的API无法产生效果。
最简单的解释是exe进程大部分都没有导出表
详细的原因在于进程被加载到内存后PE Loader就逐个将导入表的IAT表中的各项从对应dll中获取并且硬编码地址,而进程调用API就是通过查询导入表中的IAT表中的硬编码API地址。这部分是EAT无法影响的因为在进程被加载到内存时就已经决定。
试想一下,EAT HOOK的时机时必须是进程启动后才能注入并影响对应DLL中的导出表。而IAT表早就已经在Loader加载时期就查询对应dll后被硬编码。在那时候EAT HOOK的动态库根本还没有被注入进来。所以只有当用户进行动态加载API时,才有机会。
讲一下大体思路。
1. 通过IMAGE_OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress找到导出表的地址(注意这里是RVA要加上ImageBase才能得到VA,下面都是一样)。与导入表不同,导出表只有一张!
2. 遍历IMAGE_EXPORT_DESCRIPTOR.AddressOfNames(一共IMAGE_EXPORT_DESCRIPTOR.NumberOfNames个)找到被HOOK的API名称。记得与此同时要保存IMAGE_EXPORT_DESCRIPTOR.AddressOfNameOrdinals的值
3. 如果找到了API名称则把IMAGE_EXPORT_DESCRIPOTR.AddressOfFunctions[IMAGE_EXPORT_DESCRIPTOR.AddressOfNameOrdinals]即原函数的RVA地址替换成新函数地址。
#include <windows.h>
#include <cstdio>
#include <tchar.h>
BOOL EATHook(LPCTSTR szDllName, LPCTSTR szFunName, LPVOID NewFun) {
DWORD addr = 0;
DWORD index = 0;
DWORD dwProtect;
HMODULE hMod = LoadLibrary(szDllName);
if (NULL == hMod)
return(FALSE);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod;
PIMAGE_OPTIONAL_HEADER pOptHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)hMod + pDosHeader->e_lfanew + 24);
PIMAGE_EXPORT_DIRECTORY pExpDes = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PULONG pAddressOfFunctions = (PULONG)((PBYTE)hMod + pExpDes->AddressOfFunctions);
PULONG pAddressOfNames = (PULONG)((PBYTE)hMod + pExpDes->AddressOfNames);
PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)hMod + pExpDes->AddressOfNameOrdinals);
for (int i = 0; i < pExpDes->NumberOfNames; ++i) {
index = pAddressOfNameOrdinals[i];
LPCTSTR pFuncName = (LPTSTR)((PBYTE)hMod + pAddressOfNames[i]);
if (!_tcscmp((LPCTSTR)pFuncName, szFunName)) {
addr = pAddressOfFunctions[index];
break;
}
}
VirtualProtect(&pAddressOfFunctions[index], 0x1000, PAGE_READWRITE, &dwProtect);
pAddressOfFunctions[index] = (DWORD)NewFun - (DWORD)hMod;
WriteProcessMemory(GetCurrentProcess(), &pAddressOfFunctions[index], (LPCVOID)((DWORD)NewFun - (DWORD)hMod), sizeof(NewFun), &dwProtect);
return(TRUE);
}
int __stdcall MyMessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
) {
_tprintf("Hello, world!\n");
return(0);
}
typedef int (WINAPI* LPFNMESSAGEBOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
int _tmain() {
EATHook("USER32.dll", "MessageBoxA", MyMessageBox);
HMODULE hDll = GetModuleHandle("USER32.dll");
LPFNMESSAGEBOX lpMessageBox = (LPFNMESSAGEBOX)GetProcAddress(hDll, "MessageBoxA");
if (NULL == lpMessageBox)
return(-1);
lpMessageBox(NULL, "Hello, EAT Hook", "Info", MB_OK);
return(0);
}
可以将EAT HOOK写成DLL并通过远程线程注入或者利用全局HOOK注入到目标进程中执行。
(完)