IAT Hook


IAT hook

一丶IAT

1.什么是 IAT表.

熟悉PE結構的應該知道.IAT 是導入表.
其IAT表如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;       指向INT表 4個字節一組.是RVA指向名字跟序號  
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;           
       

    DWORD   ForwarderChain;                 
    DWORD   Name;
    DWORD   FirstThunk;                  在文件中跟INT表一樣.這是IAT                 
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

我們知道PE有兩種狀態.第一種.在文件中的狀態. 所以才有 VA 轉 FOA等等的互相轉換.
扯多了.
在文件狀態. IAT表(firstThunk)跟 INT表一樣.都是指向一個很大的表.這個表里面是4個字節進行存儲.存儲的是Rva. 這些RVA分別指向 導入序號以及以0結尾的字符串.

如果在內存狀態.則INT表跟上面說的文件狀態一樣指向 導入序號.以及導入的函數名字.
而IAT此時不同了.IAT此時就是保存着INT指向的導入函數的地址了.

如果還不理解.看下以前關於IAT博客.
https://www.cnblogs.com/iBinary/p/9740757.html

如下圖所示:

其實IAT就是保存函數地址.

2.怎么進行HOOK

熟悉了IAT 那么HOOK就很簡單了.首先你要會解析PE.
原理就是:
1.編寫DLL.注入到你想HOOK的程序中.
2.編寫DLL,DLL里面獲取你HOOK程序的 ImageBase以及各種頭(DOS,NT,FILE,OPT)
3.DLL 里面通過OPT的數據目錄第一項.得到導入表RVA.加上ImageBase定位到導入表
4.循環遍歷導入表.導入表是一行04個字節.最后一項為0
5.通過導入表找到IAT表.繼續遍歷IAT表.
6.判斷IAT中的函數地址,是否是你要進行HOOK的函數地址.
是: 則進行替換函數地址.比如替換為你的.一定要注入調用約定.
不是: 繼續循環.
在IAT表中沒找到.說明沒在這個導入表中.導入表+1(一個導入表結構大小)
繼續循環 4 5 6步.
說的比較復雜,其實原理很簡單.

首先第一步.准備一個測試程序.測試程序調用MessageBoxA.
且加載我們的DLL(當然你編寫的DLL一般是注入的別的進程中.我這里演示就直接加載自己進行HOOK自己).

測試程序如下:



#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
	getchar();
	::MessageBoxA(NULL, "沒有HOOK", NULL, NULL);
	LoadLibrary("IAThook.dll");      //加載HOOK的DLL
	getchar();
	
//HOOK后進行測試的程序.
	__asm
	{
		push 0
		push 0
		push 0
		push 0
		call dword ptr ds : [MessageBoxA] ; 
	}

	__asm
	{
		push 0
		push 0
		push 0
		push 0
		call dword ptr ds : [MessageBoxA] ;
	}

	__asm
	{
		push 0
		push 0
		push 0
		push 0
		call dword ptr ds : [MessageBoxA] ;
	}
	
}

上面的內斂匯編是因為我自己HOOK自己了可以.但是都在第一次調用MessageBox函數的時候編譯器會保存這個MessageBox函數的地址.就算我HOOK完畢了.它再次調用還是調用的以前的.所以我先改成下面這樣.

HOOK的DLL

// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "framework.h"
#include <windows.h>


//創建相同函數指針.
typedef int (WINAPI *PfnMsgA)(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType);

PfnMsgA g_OldPfnMsgA = nullptr;


int WINAPI MyMessageBox(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType)
{

	char szHookText[] = "IBinary -> Iat Hook";
	if (g_OldPfnMsgA != nullptr)
	{
		return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//調用以前的
	}
	return 0;
}
void SetIatHook()
{
	MessageBoxA(NULL, "開始進行HOOK", NULL, NULL);
	PVOID pHookAddress = nullptr;
	pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //你要HOOK的函數.
	if (nullptr == pHookAddress)
	{
		OutputDebugString(TEXT("獲取函數地址失敗"));
		MessageBoxA(NULL, "獲取函數地址失敗HOOK", NULL, NULL);

		return;
	}
	g_OldPfnMsgA =(PfnMsgA) pHookAddress; //保存舊的函數指針.
	//解析PE頭.尋找IAT.

	HMODULE hModImageBase = GetModuleHandle(NULL);//獲取當前的ImagBase
	PIMAGE_DOS_HEADER pDosHead =(PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //獲取DOS頭
	DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
	PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
	PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
	PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;

	//尋找導出表的位置.
	DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到導出表偏移.
	//定位到導出表
	dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
	PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
	DWORD *pFirstThunk; //導入表子表,也就是IAT存儲函數地址的表.
	//遍歷導入表

	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
	{
		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到導入表
		pFirstThunk = (DWORD *)dwTemp; //加上偏移才是真正的導入表.
		while (*(DWORD*)pFirstThunk != NULL)
		{
			//遍歷子表
			if (*(DWORD*)pFirstThunk == (DWORD)g_OldPfnMsgA)
			{
				//找到要修改的導入表了//修改內存保護屬性.寫入我們新的函數地址.
				DWORD oldProtected;
				VirtualProtect(pFirstThunk,0x1000, PAGE_EXECUTE_READWRITE,&oldProtected);
				dwTemp = (DWORD)MyMessageBox;
				memcpy(pFirstThunk, (DWORD *)&dwTemp,4); //將變量中保存的函數地址拷貝到導入表中.
				VirtualProtect(pFirstThunk,0x1000,oldProtected,&oldProtected);
			}
			pFirstThunk++; //繼續遍歷.
		}
		pCurrent++; //每次是加一個導入表結構.
	}

}

void UnIatHook()
{
	/*
	  1.遍歷導入表.恢復導入表即可.
	*/

	MessageBoxA(NULL, "開始進行HOOK", NULL, NULL);
	PVOID pHookAddress = nullptr;
	pHookAddress = MyMessageBox;
	if (nullptr == pHookAddress)
	{
		OutputDebugString(TEXT("獲取函數地址失敗"));
		MessageBoxA(NULL, "恢復函數地址失敗HOOK", NULL, NULL);
		return;
	}
	
	//解析PE頭.尋找IAT.

	HMODULE hModImageBase = GetModuleHandle(NULL);//獲取當前的ImagBase
	PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //獲取DOS頭
	DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
	PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
	PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
	PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;

	//尋找導出表的位置.
	DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到導出表偏移.
	//定位到導出表
	dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
	PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
	DWORD* pFirstThunk; //導入表子表
	//遍歷導入表

	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
	{
		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
		pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的導入表.
		while (*(DWORD*)pFirstThunk != NULL)
		{
			//遍歷子表
			if (*(DWORD*)pFirstThunk == (DWORD)MyMessageBox) //如果是我們的函數地址.則進行恢復.
			{
				//找到要修改的導入表了//修改內存保護屬性.寫入我們新的函數地址.
				DWORD oldProtected;
				VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
				dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
				memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //將變量中保存的函數地址拷貝到導入表中.
				VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
			}
			pFirstThunk++; //繼續遍歷.
		}
		pCurrent++; //每次是加一個導入表結構.
	}


}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		SetIatHook();
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:

		break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


測試代碼如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM