IAThook的原理學習帖子有很多,我需要復現一下幾個基本概念先:
1. PE結構下IID數組相關指針(放假太久都快忘了各個指針位置了)
這個之前我寫過簡單的分析工具,參考:https://bbs.pediy.com/thread-255851.htm 結構總覽:
2. 主要思路:
雖然r3層的鈎子是對進程局部hook,但是實現iat hook為什么就一定要外注入DLL呢(手動狗頭)
程序運行有一個小彈窗,然后開始hook,利用GetModuleHandle(NULL)獲取當前進程模塊句柄,然后確定目標 dll名字和目標函數名稱:(char*)"user32.dll", (char*)"MessageBoxA" ,LoadLibrary動態加載起來目標dll,獲得相應的基質和GetProcAddress目標函數指針(這里對PE的結構處理函數可參考我之前的帖子https://bbs.pediy.com/thread-255851.htm 對PE的基本處理函數應有盡有)。找到目標函數之后,需要進行權限修改,因為目標只有讀權限,這里借鑒了加密與解密。 找到之后獲得目標函數地址后賦給我們之前定義好的Detor,就可以對源messagebox就行修改了。
3.完整代碼如下:
vs2019: 如果提示const char[]轉 char* 就對函數形參加const, 項目屬性改成多字符集
#include <windows.h> #include <stdio.h> #include <imagehlp.h> #pragma comment(lib,"imagehlp.lib") //以MessageBoxA的原型定義一個函數指針類型 typedef int (WINAPI* PMessageBoxA)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); //以MessageBoxA的原型定義一個函數來替代原始的MessageBoxA Detor函數 int WINAPI My_MessageBoxA(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,UINT uType); //存在以下關系 //*(*pThunkPointer) == *pOriginalFuncAddr ; BOOL InstallHook( HMODULE hModToHook, char* szModuleName, char* szFuncName, PVOID ProxyFunc, PULONG_PTR* pThunkPointer, ULONG_PTR* pOriginalFuncAddr ); VOID ShowMsgBox(const char* szMsg); BOOL IAT_InstallHook(); VOID IAT_UnInstallHook(); //保存原始MessageBoxA的地址 PMessageBoxA OldMessageBox = NULL; //指向IAT中pThunk的地址 PULONG_PTR g_PointerToIATThunk = NULL; int main(int argc, char* argv[]) { ShowMsgBox("1+1=2 ? 對嗎"); IAT_InstallHook(); ShowMsgBox("八對"); IAT_UnInstallHook(); ShowMsgBox("hook卸載完成"); return 0; } //之所以把這個調用單獨放在一個函數中,是因為Release模式下對調用進行了優化,第二次調用時直接采用了寄存器尋址而不是導入表 //因此,單獨放在一個函數中可以避免這個情況。 VOID ShowMsgBox(const char* szMsg) { MessageBoxA(NULL, szMsg, "Test", MB_OK); } int WINAPI My_MessageBoxA( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ) { int ret; char newText[1024] = { 0 }; char newCaption[256] = "jentle"; printf("MessageBox丟失!\n"); //在調用原函數之前,可以對IN(輸入類)參數進行干涉 lstrcpy(newText, lpText);//為防止原函數提供的緩沖區不夠,這里復制到我們自己的一個緩沖區中再進行操作 lstrcat(newText, "\t MessageBox Hacked by jentle");//篡改的消息框內容 ret = OldMessageBox(hWnd, newText, newCaption, uType);//調用原MessageBox,並保存返回值 return ret;//這里你還可以干涉原始函數的返回值 } BOOL IAT_InstallHook() { BOOL bResult = FALSE; HMODULE currectExe = GetModuleHandle(NULL); PULONG_PTR pt; ULONG_PTR OrginalAddr; bResult = InstallHook(currectExe, (char*)"user32.dll", (char*)"MessageBoxA", (PVOID)My_MessageBoxA, &pt, &OrginalAddr); if (bResult) { printf("************************Hook完畢! pThunk=0x%p OriginalAddr = 0x%p*********************************\n", pt, OrginalAddr); g_PointerToIATThunk = pt; OldMessageBox = (PMessageBoxA)OrginalAddr; } return bResult; } VOID IAT_UnInstallHook() { DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; if (g_PointerToIATThunk) { //查詢並修改內存頁的屬性 VirtualQuery((LPCVOID)g_PointerToIATThunk, &mbi, sizeof(mbi)); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD); //將原始的MessageBoxA地址填入IAT中 *g_PointerToIATThunk = (ULONG)OldMessageBox; //恢復內存頁的屬性 VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0); } } BOOL InstallHook( HMODULE hModToHook, //待Hook的模塊基址 char* szModuleName, //目標DLL名字 char* szFuncName, //目標函數名字 PVOID DetourFunc, //Detour函數 PULONG_PTR* pThunkPointer, // ULONG_PTR* pOriginalFuncAddr ) { PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; PIMAGE_THUNK_DATA pThunkData; ULONG ulSize; HMODULE hModule = 0; ULONG_PTR TargetFunAddr; PULONG_PTR lpAddr; char* szModName; BOOL result = FALSE; BOOL bRetn = FALSE; hModule = LoadLibrary(szModuleName);//動態加載目標DLL TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName); //目標函數指針 printf("目標%s函數地址 :0x%p\n", szFuncName, TargetFunAddr); printf("hook模塊基質(此exe):0x%p\n", hModToHook); //需要hook的程序,這個是此程序 pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);; //IID指針 printf("IID數組地址:0x%p\n", pImportDescriptor); while (pImportDescriptor->FirstThunk) { szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name); printf("當前模塊(DLL)名稱:%s\n", szModName); if (stricmp(szModName, szModuleName) != 0) { printf("DLL名字不對下一個\n"); pImportDescriptor++; continue; } //程序的導入表處理完畢后OriginalFirstThunk可能是無效的,不能再根據名稱來查找,而是遍歷FirstThunk直接根據地址判斷 pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk); while (pThunkData->u1.Function) { lpAddr = (ULONG_PTR*)pThunkData; //找到了地址 if ((*lpAddr) == TargetFunAddr) { printf("目標函數找到!\n"); //通常情況下導入表所在內存頁都是只讀的,因此需要先修改內存頁的屬性為可寫 DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(lpAddr, &mbi, sizeof(mbi)); bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); if (bRetn) { //內存頁屬性修改成功,繼續下一步操作,先保存原始數據 if (pThunkPointer != NULL) { *pThunkPointer = lpAddr; } if (pOriginalFuncAddr != NULL) { *pOriginalFuncAddr = *lpAddr; } //修改地址 *lpAddr = (ULONG_PTR)DetourFunc; result = TRUE; //恢復內存頁的屬性 VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0); } break; } pThunkData++; } pImportDescriptor++; } FreeLibrary(hModule); return result; }
4. 運行情況:
確定之后返回:
區別正常邏輯: