大家好,我們又見面啦,今天我將為各位講述一個新故事,那就是IAT HOOK。再觀看這個故事之前,需要觀眾確定具備兩個基本能力:
1.對簡單的數(shù)據(jù)結(jié)構(gòu)在內(nèi)存中的樣子能有個宏觀的理解。
2.理解運(yùn)行在windows環(huán)境程序的工作原理。
導(dǎo)入地址表(IAT):Import Address Table 由于導(dǎo)入函數(shù)就是被程序調(diào)用但其執(zhí)行代碼又不在程序中的函數(shù),這些函數(shù)的代碼位于一個或者多個DLL 中.當(dāng)PE 文件被裝入內(nèi)存的時候,Windows 裝載器才將DLL 裝入,并將調(diào)用導(dǎo)入函數(shù)的指令和函數(shù)實際所處的地址聯(lián)系起來(動態(tài)連接),這操作就需要導(dǎo)入表完成.其中導(dǎo)入地址表就指示函數(shù)實際地址。
比如我們想對目標(biāo)程序的PeekMessage這個API函數(shù)進(jìn)行HOOK,那么只需要找到他的IAT表,并把這個API的實際地址修改,這就完成了一個API HOOK。按常理,想執(zhí)行此過程需要對PE格式有一定的理解,但是我認(rèn)為并沒有這個必要,畢竟這是一個很簡單的工作。接下來我將用我的方法帶領(lǐng)大家來實現(xiàn)它,那就是角色扮演。
首先幻想自己是30年代,藍(lán)衣社的一名特務(wù)。今天接到上級的任務(wù),去某地殺掉一名為敵對勢力工作的人,然后經(jīng)過易容,偽裝成他來執(zhí)行潛伏。那么目標(biāo)是誰呢?他又住在哪里呢?拿起MSDN情報科送來的文件看看吧,目標(biāo)叫PeekMessage,是姓“USER32.dll”家族的成員,它住在:
xxx.exe市
IMAGE_DOS_HEADER區(qū)
IMAGE_OPTIONAL_HEADER街
IMAGE_IMPORT_DESCRIPTOR小區(qū)
還有一張目標(biāo)的相片
BOOL PeekMessage
(LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
)
資料就這么多啦,但是并沒有說明目標(biāo)住在幾號樓幾層幾號啊。不過沒有關(guān)系我們是特務(wù)嘛。這難不倒我們的,Let's begin
坐著藍(lán)衣社為我們準(zhǔn)備的專機(jī)SetWindowsHookEx把我們(DLL)注入到xxx.exe市,哈哈~下了飛機(jī)來到一個陌生的城市真是倆眼一摸黑呀,怎么找我們的目的地呢?最簡單的辦法:打車,于是我們攔了一輛GetModuleHandle(NULL)牌的出租車,誰知道一上車。司機(jī)看到我給他的地址后告訴我要去的地方需要過海,沒辦法直接到,只能先送我到IMAGE_DOS_HEADER區(qū),然后再換船過海才行。聽得我暈頭轉(zhuǎn)向的,沒辦法,走吧。。。閑來打量一下這個城市,滿眼盡是一些由0和1搭建黑白2色的高樓大廈,顏色和款式是多么的單調(diào)枯燥哇,算了,還是閉目養(yǎng)神吧。不一會我們來到了IMAGE_DOS_HEADER區(qū)的水路碼頭,該下車換乘船過海啦。于是買了(ModuleAddress+ImageDosHearderPointer->e_lfanew+24)號渡輪的船票,繼續(xù)這次無聊的旅程。暈船從頭吐到尾,好不容易熬到了地方,原來一出了碼頭就來到了IMAGE_OPTIONAL_HEADER街,抬頭一望在我們12點鐘方向,有個小巴站臺,走過去這么一看才知道原來坐
ModuleAddress+ImageOptionalHeaderPointer->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress線小巴,可以直達(dá)我們的終點:IMAGE_IMPORT_DESCRIPTOR小區(qū)。二話不說,走著您嘚~~~~~馬上要到目的地啦。趁坐小巴的這段時間我們先來整理下思緒,因為不知道具體的樓號和門牌號,所以我們要先想個什么辦法,于是找同車的一位美麗小姐打聽得知,IMAGE_IMPORT_DESCRIPTOR小區(qū)的每座樓都是同一樣式的,那就是ImageImportDescriptorPointer->FirstThunk風(fēng)格,又知道一樓大廳有個收信箱,上面登記著住戶的姓名。有了這些線索我們就下車一棟樓,一棟樓的找吧,雖然這個方案毫無創(chuàng)意,但是記得有位大師曾經(jīng)說過:往往通過復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和華麗的算法并不是解決問題的好方法,因為它加大了維護(hù)和調(diào)試的難度。所以我們就用這種直接而有效的土辦法來老老實實的找吧:先逐樓搜索
while(ImageImportDescriptorPointer->FirstThunk!=0),然后找到一樓大廳的收信箱
TargetName=(LPCTSTR)((DWORD)ModuleAddress+ImageImportDescriptorPointer->Name),再逐戶的查找姓“USER32.dll”的家族
if(TargetName.Compare(_T("USER32.dll"))==0),沒過多久我們終于找到了,登記簿上寫明了具體的門牌號,哈哈~這表示我們離成功只有一步之遙,于是迫不及待的走進(jìn)ModuleAddress+ImageImportDescriptorPointer->FirstThunk號電梯,走向“USER32.dll”家,來到門口,我停住啦,靜靜地站在那里平復(fù)一下心緒,10秒后我飛起一腳踹開了大門走了進(jìn)去,映入眼簾的是:坐在屋里男女老少,大大小小10幾口子,全部一臉驚愕的盯著我這位不速之客。我拿出相片while(ImageThunkDataPointer->u1.Function)迅速搜索,
FunctionAddress=(PDWORD)&(ImageThunkDataPointer->u1.Function)定位目標(biāo),
if(*FunctionAddress==(DWORD)PeekMessageAddress)經(jīng)過一輪的搜索,我找到了目標(biāo),他正傻傻的坐在角落里的電腦桌前,我走到它面前
VirtualQuery(FunctionAddress, &InforMation, sizeof(InforMation))細(xì)細(xì)的打量著他:凌亂的頭發(fā)如同雜草,一架高度數(shù)眼鏡戴在蒼白消瘦的臉頰上,駝背,纖細(xì)卻小腹異常隆起的身材,套著10幾年前流行的服飾,HOHO~做技術(shù)典型的形象,我冷漠的從懷中掏出了槍:
VirtualProtect(FunctionAddress, sizeof(DWORD),PAGE_READWRITE,&BeforeProtect)頂在目標(biāo)的眉心。此時此刻,在場的所有人都清楚了我的來意。死亡的恐懼籠罩在每個人的心頭,我不在多做停留,無情的勾動了扳機(jī),這就是一個特務(wù)的專業(yè)操守。嘿嘿
::WriteProcessMemory((HANDLE)-1,FunctionAddress,&FunctionOfSelf,sizeof(DWORD), NULL)目標(biāo)應(yīng)聲倒地。在手槍的淫威下,其他人也只好無奈的接受這痛苦的事實,我也就成功的完成了此次任務(wù)。開始潛伏。
接下來換種語言再來講述一下這個故事:C++
#include <winternl.h>
#include "DialogMain.h"
typedef BOOL (WINAPI* PEEKMESSAGE)(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);
PEEKMESSAGE FakePeekMessage=(PEEKMESSAGE)PeekMessage;
BOOL WINAPI MinePeekMessage(LPMSG lpMsg,HWND hWnd, UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg)
{
AfxMessageBox(_T("你TM是不是調(diào)用我啦?"));
return ((PEEKMESSAGE)FakePeekMessage)(lpMsg,hWnd,wMsgFilterMin,wMsgFilterMax,wRemoveMsg);
}
BOOL CDialogMain::ImportAddressTableHook(HMODULE ModuleAddress,LPCTSTR Library,LPCVOID TargetAddress,LPCVOID ReplaceAddress)
{
IMAGE_DOS_HEADER* ImageDosHearderPointer=NULL;
IMAGE_OPTIONAL_HEADER* ImageOptionalHeaderPointer=NULL;
IMAGE_IMPORT_DESCRIPTOR* ImageImportDescriptorPointer=NULL;
IMAGE_THUNK_DATA* ImageThunkDataPointer=NULL;
CString TargetName;
DWORD Value=0;
LPDWORD FunctionAddress=NULL;
MEMORY_BASIC_INFORMATION InforMation;
DWORD BeforeProtect=0;
ImageDosHearderPointer=(IMAGE_DOS_HEADER*)ModuleAddress;
ImageOptionalHeaderPointer=(IMAGE_OPTIONAL_HEADER*)((DWORD)ModuleAddress+ImageDosHearderPointer->e_lfanew+24);
ImageImportDescriptorPointer=(IMAGE_IMPORT_DESCRIPTOR*)
((DWORD)ModuleAddress+ImageOptionalHeaderPointer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while(ImageImportDescriptorPointer->FirstThunk!=0)
{
TargetName=(LPCTSTR)((DWORD)ModuleAddress+ImageImportDescriptorPointer->Name);
if(TargetName.Compare(Library)==0)
{
Value=(DWORD)ModuleAddress+ImageImportDescriptorPointer->FirstThunk;
break;
}
ImageImportDescriptorPointer++;
}
if (Value==0)
{
AfxMessageBox(_T("獲取導(dǎo)入地址表失敗!"));
return FALSE;
}
else
{
ImageThunkDataPointer=(IMAGE_THUNK_DATA*)Value;
while(ImageThunkDataPointer->u1.Function)
{
FunctionAddress=(LPDWORD)&(ImageThunkDataPointer->u1.Function);
if(*FunctionAddress==(DWORD)TargetAddress)
{
DebugInfo.Format(_T("%x"),*FunctionAddress);
EditHeroBlood.SetWindowText(DebugInfo);
VirtualQuery(FunctionAddress,&InforMation,sizeof(InforMation));
VirtualProtect(FunctionAddress, sizeof(DWORD),PAGE_READWRITE,&BeforeProtect);
if (::WriteProcessMemory((HANDLE)-1,FunctionAddress,&ReplaceAddress,sizeof(DWORD),NULL)==FALSE)
{
AfxMessageBox(_T("修改導(dǎo)入地址表失敗!"));
return FALSE;
}
else
{
VirtualProtect(FunctionAddress,sizeof(DWORD),BeforeProtect,0);
return TRUE;
}
}
ImageThunkDataPointer++;
}
}
return FALSE;
}
我再換一種語言來講述:
大家都知道其實我上述我說地名都是一些結(jié)構(gòu)體,而Windows就是靠無數(shù)個這樣的結(jié)構(gòu)體連接搭建起來的,你中有我,我中有你,這就好比一個生物由無數(shù)個細(xì)胞組成的一樣。因為今天我們不是講述windows體系,所以我也不做過多解釋。
那么通過IAT修改來實現(xiàn)API HOOK的實現(xiàn)原理是什么呢?關(guān)鍵就在于如何在茫茫的內(nèi)存中找到我們的目標(biāo),聽起來好像很恐怖,像大海撈針,其實沒那么難,因為windows就是通過那些結(jié)構(gòu)體把數(shù)據(jù)在內(nèi)存中全部線形連接起來啦。這就像玩大富翁,或者一種挖寶藏游戲,從起點出發(fā)走幾步就會得到一個提示,告訴我們下一步該怎么走?,F(xiàn)在我就帶領(lǐng)大家來玩一次這種游戲
先來說明游戲規(guī)則
WORD=2步
DWORD=4步
指針=進(jìn)入下一房間
數(shù)組=翻卡片得到答案
RVA=地圖寶箱
IMAGE_DOS_HEADER STRUCT
{
這里就是我們的起點,可以通過GetModuleHandle(NULL)獲得,比如起點地址是0x00000000,我們得到的第一個提示
(ModuleAddress+ImageDosHearderPointer->e_lfanew+24),怎么理解這個提示呢?就是從起點開始走36步,找到一個叫e_lfanew的門(因為它是指針,所以他就是通往下一個房間的大門),然后推門進(jìn)去后再走24步,我們就找到了第二個將要給我們提示的地方
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 WORD
e_oemid WORD
e_oeminfo WORD
e_res2 WORD
e_lfanew DWORD
}
IMAGE_DOS_HEADER ENDS
typedef struct _IMAGE_OPTIONAL_HEADER
{
我們通過上一個提示來到了這個房間,同樣我們得到了又一個提示
ModuleAddress+ImageOptionalHeaderPointer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
理解下這個提示。這代表我們從起點走到e_lfanew大門進(jìn)入這個房間再走96步找到DataDirectory開始翻卡片(他是一個結(jié)構(gòu)體形數(shù)組),來找答案,而答案就在第IMAGE_DIRECTORY_ENTRY_IMPORT(表示1,也就是DataDirectory[1])張卡片內(nèi)
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,
IMAGE_IMPORT_DESCRIPTOR STRUCT
{
通過上面的提示,我們來到了這個房間,那么下一個提示是什么呢?那就是while(ImageImportDescriptorPointer->FirstThunk!=0),再來理解下這個提示。它是說讓我們找到FirstThunk這個地圖寶箱(RAV)在里面找到我們需要的地圖才能找到下一個房間,那我們就一張一張的翻看吧
我們要找的地圖名就是“USER32.dll”
union
Characteristics
OriginalFirstThunk
ends
TimeDateStamp
ForwarderChain
Name1
FirstThunk
}
IMAGE_IMPORT_DESCRIPTOR ENDS
有了地圖我們就來到了下一個房間
IMAGE_THUNK_DATA32 STRUCT
哈哈。這就使傳說中的IAT表啦,就差一步就找到寶藏啦。我們看一下這次的提示while(ImageThunkDataPointer->u1.Function),它是說讓后找到u1.Function這個卡片商來得到寶藏位置的答案,只是這次我們不知道第幾張卡片里有我們要的答案。沒辦法挨個翻吧。指導(dǎo)找到
PeekMessage
union u1
ForwarderString
Function
Ordinal
AddressOfData
ends
IMAGE_THUNK_DATA32 ENDS
找到后,還用我教嗎?關(guān)閉他的保護(hù)屬性,然后改寫他的地址吧。。。
【小結(jié)】
這里我鉤了一個目標(biāo)程序接收消息的函數(shù)PeekMessage,然后把它傳進(jìn)來的參數(shù)輸出出來??纯此麜粫约航o自己發(fā)消息。HOHO。。挺無聊的
但是如果你想鉤點別的。比如目標(biāo)程序的socket,哈哈~那你覺得這像什么呢?
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。