0 PE文件(.exe)
a 一开始是一段Dos程序,就是那个“必须运行在XXX环境”的警告头(IMAGE_DOS_SIGNATURE)。
b 接着这个DOS头,首先是IMAGE_NT_HEADER,都是一些PE文件信息,在它的尾端是Data Directory数据表,能快速决定PE的段(section)地址。
c 下来是IMAGE_SECTION_HEADER的列表,记录着每一个段的信息。
d 再下来就是段数据,执行代码,数据资源那些东东了。
这些东西会放在地址0x400000的地方,就是Histance的值,程序开始的位置。
在这些段里,有一个".idata"段(输入数据段),该地方有着IAT,加载的DLL的API都在这里,大概像这个样子:
JWP DWORD PTR [XXXXXXXXX]
或者
CALL DWORD PTR [XXXXXXXXX]
这里面的XXXXXXXX,就是系统API所在的位置,所以我们要做的就是,把XXXXXXXX改成我们的函数地址,执行完我们的函数再杀回来就好了。
导入地址表(IAT):Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE文件被装入内存的时候,Windows装载器才将DLL装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成,其中导入地址表就指示函数实际地址。
1 程序指令内存空间的写入
因为指令空间有Windows的保护,不能直接指针指过去修改,需要如下操作
void MyHook::_writeProcessMemorySystem(LPVOID pDec, LPVOID pSrc, UINT nLength) { MEMORY_BASIC_INFORMATION mbi_thunk; VirtualQuery(pDec, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); // 查询页信息 VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect); // 改变页的保护信息为读写 memcpy(pDec, pSrc, nLength); DWORD dwOldProtect; VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect); // 恢复保护属性 }
2 查找DLL模块的IAT
PIMAGE_IMPORT_DESCRIPTOR MyHook::_locationIAT(string strImportMod) { // 检查是否为DOS程序,DOS程序没有IAT PIMAGE_DOS_HEADER pDosHeader = static_cast<PIMAGE_DOS_HEADER>(_handle); if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return NULL; } // 检查是否为NT程序 PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + (DWORD)(pDosHeader->e_lfanew)); if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) { return NULL; } // 检查是否有IAT if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0) { return NULL; } // 定位第一个IAT PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + (DWORD)(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while(pImportDesc->Name) { // 如果有名字,获取其名字 PSTR szCurrMod = (PSTR)((DWORD)pDosHeader + (DWORD)(pImportDesc->Name)); if(stricmp(szCurrMod, strImportMod.c_str()) == 0) { // 找到了就停下来 break; } pImportDesc++; } if(pImportDesc->Name == NULL) { return NULL; } return pImportDesc; }
3 查找IAT中的函数
OOL MyHook::hookAPIByName(std::string strImportMod, MyHook::HOOKAPI *pHookApi) { _handle = g_hInstance; if(_state == MYHOOK_NULL) { return FALSE; } PIMAGE_IMPORT_DESCRIPTOR pImportDesc = _locationIAT(strImportMod); if(pImportDesc == NULL) { return FALSE; } PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)_handle + (DWORD)(pImportDesc->OriginalFirstThunk)); // 第一个THUNK PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((DWORD)_handle + (DWORD)(pImportDesc->FirstThunk)); // 第一个IAT的THUNK // 查找IAT,替换函数 while(pOrigThunk->u1.Function) { // 检查此THUNK是否为IAT项 if((pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) { // 获取此IAT的函数名 PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)_handle + (DWORD)(pOrigThunk->u1.AddressOfData)); if(pByName->Name[0] == '/0') { return FALSE; } // 检测是否为所找的函数 if(stricmp(pHookApi->strFunctionName.c_str(), (char*)pByName->Name) == 0) { if(pHookApi->pOldFunc != NULL) { pHookApi->pOldFunc = (PROC)pRealThunk->u1.Function; } _writeProcessMemorySystem((LPVOID)&(pRealThunk->u1.Function), (LPVOID)&(pHookApi->pNewFunc), sizeof(DWORD)); Funs[pHookApi->strFunctionName] = *pHookApi; } } pOrigThunk++; pRealThunk++; } SetLastError(ERROR_SUCCESS); return TRUE; }
4 使用范例
typedef int(WINAPI *Mb)(HWND, LPCSTR, LPCSTR, UINT); static int WINAPI MessageBoxA1(HWND hWnd, LPCSTR p1, LPCSTR p2, UINT uType) { Mb func = (Mb)(MyHook::instance().Funs["MessageBoxA"].pOldFunc); return func(hWnd, "HOOK", "HOOK", uType); } VARIANT_BOOL STDMETHODCALLTYPE CMyRock::OnMouseClick(IHTMLEventObj* _pEvtObj) { // IHTMLEventObj里包括了当前所有操作信息,具体按F1吧 // 如果VARIANT_FALSE将忽略此事件 MyHook::HOOKAPI api; api.strFunctionName = std::string("MessageBoxA"); api.pNewFunc = (PROC)MessageBoxA1; MyHook::instance().hookAPIByName("user32.dll", &api); MessageBoxA(NULL, "1111111111111111", "1111111111111111111", 0); return VARIANT_TRUE; }
5 注意:通过IE测试发现,此种方法只能获取用Depneds看到在上面加载的DLL,而后来加载的,以及DLL加载的DLL都是无法获得的。