Hook技術入門
0x00 前言
要說在Hook技術里面最基礎的,那就是IAT Hook,它的原理就是通過修改PE結構中的IAT表,將其替換成我們自己定義的函數,最終實現Hook,所以在進行Hook之前,我們得很清楚的PE結構,接下來我們先講解一下怎么索引到IAT表。
0x01 PE文件格式解析
隨便拖一個程序進入Uedit進行分析

這一部分就是PE文件的DOS頭,先看看DOS頭的結構體,咱邊看結構體邊分析:
typedef struct IMAGE_DOS_HEADER
{
WORD e_magic; WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
DWORD e_lfanew;
}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
在DOS頭里面最關鍵是兩個成員就是e_magic,e_lfanew(其他的可以忽略),e_magic就是DOS頭的標識,也就是MZ,為什么是MZ呢,其實就是Mark Zbikowski,他是MS-DOS主要開發者之一,為了紀念老人家,e_lfanew就是下一個頭的偏移(基于文件基址),可能有同學要有疑問了,為啥現在還要有這個DOS頭呢,因為微軟為了向下兼容,所以才沒有刪除DOS頭,那么加上偏移我們來看看:

PE頭也有它自己的結構體,Signature和e_magic是一個道理,這里的Signature是PE。
typedef struct IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;
選擇的區域是上面結構體中的FileHeader

它也有自己的結構體,感興趣的小伙伴可以自己去查查每個字段的具體含義,這里就不展開了:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //運行平臺
WORD NumberOfSections; //節表數目
DWORD TimeDateStamp; //時間戳
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols; //符號數
WORD SizeOfOptionalHeader; //可選部首長度
WORD Characteristics; //文件屬性
}
說完FileHeader,重點就來了,那就是OptionalHeade,除去Signature,FileHeader剩下的就是OptionalHeade:

結構體如下,我們關注其最重要的一個成員DataDirectory(數據目錄),它包含了我們要替換的導入表。
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
它妥妥的有十六張表,我們只關注導入表:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; //指向DLL名字的RVA
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
這里面最重要的就是OriginalFirstThunk,Name,FirstThunk
其中OriginalFirstThunk,FirstThunk分別指向INT和IAT表,心心念念的IAT表終于登場了,這兩個成員指向的都是同一個表,為什么這樣做等下說

當我們的函數加載完成后,我們IAT指向了函數真正的地址

在IAT在加載完真正的函數地址之后,如果沒有INT表來標識具體的函數名稱的話,操作系統就完全不知道它加載的這個函數名稱是啥,只有一個函數的實現偏移和實現,并不知道它是啥,所以才會有INT表的存在。
0x02 IAT Hook 代碼編寫
到此PE結構也就講解完了,也就可以開始寫IAT Hook
我們的代碼應該包含以下功能:
1.定義我們自己的Hook后的函數
int WNAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return OldMessageBoxA(hWnd, "Hello NSDA", lpCaption, uType);
}
我們自己的MessageBox必須和原來的MessageBox的參數一模一樣,這樣才能保證在調用自己的函數的時候不會報錯
2.接下來就是找到IAT表
HMODULE ImageBase = GetModuleHandle(NULL); //獲得模塊基地址 PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)ImageBase; DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew; PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp; PIMAGE_OPTIONAL_HEADER pOpHead = (PIMAGE_OPTIONAL_HEADER)&pNtHead->OptionalHeader; DWORD dwInputTable = pOpHead->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; DWORD dwTemp = (DWORD)GetModuleHandle(NULL) + dwInputTable; PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp; PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
通過GetModuleHandle獲得模塊基地址之后,我們就可以拿到程序的DOS頭,PE頭,文件頭,可選頭
3.接下來就是遍歷IAT將其替換成我們自己函數的地址
while (*(DWORD*)pFirstThunk != NULL)
{
if (*(DWORD*)pFirstThunk == (DWORD)OldMessageBoxA)
{
DWORD oldProtected;
VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
memcpy(pFirstThunk, (DWORD *)&dwTemp, 4);
VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
}
pFirstThunk++;
}
通過遍歷IAT表找到MessageBox的地址之后進行替換,有一點需要注意的,需要先修改文件的頁屬性才能進行替換(在XP系統上不用)
完整代碼就不貼了,大伙們可以自己去嘗試把完整的代碼寫出來,下面看看Hook完的效果:

文章作者: 廣軟NSDA安全團隊