标签:
EAT与IAT比较类似,我相信会IAT的肯定很多,起初我想写在C#上面 不过与
C# 遍历DLL导出函数 的方法很相似,只是两者在内存中的映射方式不同而已
Heh,首先我们需要把DLL映射到地址内存空间去 否则没有办法去置换函数,
当然EAT有一些缺点,它必须在软件调用GetProcAddress函数之前替换DLL中
的函数,所以则出现了对GetProcAddress函数的一个Hook,否则只可改变
GetProcAddress返回的内容、是不是感到很惆怅
EAT全称为“Export address table”其核心则是利用PE与DLL的一些相关特性
实现的一个技术 首先我们通过LoadLibraryA函数把一个有效的DLL映射到内
存中,PE文件中第一个字节是MS-DOS信息头即IMAGE_DOS_HEADER
IMAGE_NT_HEADER = (pDosHearder + pDosHearder->e_lfanew);
// IMAGE_NT_HEADER::OptionalHeader
pOptionalHeader = (pDosHearder + pDosHearder->e_lfanew + 24);
上面是x86的一个OptionalHeader的对称,这里是不想再多做一次转换直接
跳过去会很快捷、所以加上24但恰好指向OptionalHeader、而我们只需要
它其它的对我们而言根本没有任何用、所以跳过就可以
#include "stdafx.h" #include <Windows.h> BOOL EATHook(LPCSTR strLibraryName, LPCSTR strMethodName, PVOID newMethodAddress) { HMODULE hRemoteModule = LoadLibraryA(strLibraryName); PIMAGE_DOS_HEADER pDosHearder = (PIMAGE_DOS_HEADER)hRemoteModule; PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)hRemoteModule + pDosHearder->e_lfanew + 24); PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pDosHearder + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames); LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions); PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals); for (int i = 0; i < pExportDirectory->NumberOfNames; i++) { LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]); if (_stricmp(strInsideName, strMethodName) == 0) { DWORD pflOldProtect; VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect); pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule; break; } pAddressOfNameOrdinals++; } return FALSE; }
既然是EAT,我们看第一个字母全名为Export那么我们肯定要去找包含导出的DLL函数的
PE中的数据目录,恰好有那么一个IMAGE_DIRECTORY_ENTRY_EXPORT但是拿出来
的地址是RVA / 相对虚拟地址所以你必须要ToVA / 到虚拟地址、但是DLL已经被映射到虚
拟内存中 所以我们不需要使用RvaToVa、我们只需要
(DWORD)pDosHearder + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
(DWORD)hRemoteModule + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
则可以得到,PIMAGE_EXPORT_DIRECTORY pExportDirectory函数导出目录,但是我
们得到了它却不可以耍花架子,我们还有继续下面的代码否则没法混、
LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames); LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions); PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals);
pAddressOfNames // 导出函数名
pAddressOfFunctions // 导出函数地址
pAddressOfNameOrdinals // 导出函数序列
上面定义三种不同的指针,但是都预先把地址进行了一个处理,这是为了方便后面的使用
而不必一直加上(DOWRD)hRemoteModule那样子会很累的、
pExportDirectory->NumberOfNames // 导出函数计数(Length)
LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);
上面是获取DLL导出的函数名,pAddressOfNames[i] 返回的地址为虚拟相对地址(Rva)
在这里只需要如下去做、则可以得到DLL导出的函数名是什么,当然这个是c_str风格字
符串,你懂的‘\0‘结尾,_strlen对其有效,不过在本代码中也用不到_strlen、
下面是首先比较两个字符串是否相等,主要看看需要具体去Hook那个函数
for (int i = 0; i < pExportDirectory->NumberOfNames; i++) { LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]); if (_stricmp(strInsideName, strMethodName) == 0) { DWORD pflOldProtect; VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect); pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule; break; } pAddressOfNameOrdinals++; }
&pAddressOfFunctions[*pAddressOfNameOrdinals] 从上面的代码你应该看出了端倪
对的,DLL函数导出的地址你不可以直接通过&pAddressOfFunctions[i]解决问题,因
为在PE中,它是通过Ordinals定位函数位置,但是它与i=0; i++是相序的、有点绕哈
VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals],
0x1000, PAGE_READWRITE, &pflOldProtect);
上面是修改函数的内存保护,0x1000没有太大意义、填4 / 8也是可以的
pAddressOfFunctions[*pAddressOfNameOrdinals] =
(DWORD)newMethodAddress - (DWORD)hRemoteModule;
好吧,这里填写相对虚拟地址、指的是新的函数地址与这个模块的地址做一个加减
不管返回的是负数还是正数都可以,没有那么多讲究、后面这句话也是必要的
pAddressOfNameOrdinals++; 你可以任性的把使用 *pAddressOfNameOrdinals
修改为pAddressOfNameOrdinals[i] 但是绝对不可以不写,这是原则性上的问题、
HANDLE WINAPI CB_GetModuleHandleW(LPCTSTR strLibraryName) { return NULL; }
我们写一个Hook的处理函数,GetModuleHandleW函数的一个处理函数,什么都不
用写,只需要打个断点看看有没有执行就可以了,因为没有太大的意义重点在这里
int _tmain(int argc, _TCHAR* argv[]) { EATHook("kernel32.dll", "GetModuleHandleW", CB_GetModuleHandleW); LPDWORD pfnGetModuleHandleW = (LPDWORD)GetProcAddress(GetModuleHandleA("kernel32"), "GetModuleHandleW"); _asm { push 0 call pfnGetModuleHandleW } return 0; }
上面是利用EAT先把GetModuleHandleW函数进行挂钩,然后通过GetProcAddress
函数得到pfnGetModuleHandleW,为了方便不想使用函数指针,直接嵌入两行汇编
代码进去直接解决问题,然后F5调试,如果CB_GetModuleHandleW函数被执行 那
么恭喜你成功了,如果没有说明你出错了、哈哈
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/u012395622/article/details/46984679