大家好,,我們又見面啦,,今天我將為各位講述一個(gè)新故事,,那就是IAT HOOK。再觀看這個(gè)故事之前,,需要觀眾確定具備兩個(gè)基本能力: 1.對(duì)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)在內(nèi)存中的樣子能有個(gè)宏觀的理解,。 2.理解運(yùn)行在windows環(huán)境程序的工作原理,。 導(dǎo)入地址表(IAT):Import Address Table 由于導(dǎo)入函數(shù)就是被程序調(diào)用但其執(zhí)行代碼又不在程序中的函數(shù),這些函數(shù)的代碼位于一個(gè)或者多個(gè)DLL 中.當(dāng)PE 文件被裝入內(nèi)存的時(shí)候,,Windows 裝載器才將DLL 裝入,,并將調(diào)用導(dǎo)入函數(shù)的指令和函數(shù)實(shí)際所處的地址聯(lián)系起來(動(dòng)態(tài)連接),這操作就需要導(dǎo)入表完成.其中導(dǎo)入地址表就指示函數(shù)實(shí)際地址,。 比如我們想對(duì)目標(biāo)程序的PeekMessage這個(gè)API函數(shù)進(jìn)行HOOK,,那么只需要找到他的IAT表,并把這個(gè)API的實(shí)際地址修改,,這就完成了一個(gè)API HOOK,。按常理,想執(zhí)行此過程需要對(duì)PE格式有一定的理解,,但是我認(rèn)為并沒有這個(gè)必要,,畢竟這是一個(gè)很簡(jiǎn)單的工作。接下來我將用我的方法帶領(lǐng)大家來實(shí)現(xiàn)它,,那就是角色扮演,。 首先幻想自己是30年代,,藍(lán)衣社的一名特務(wù),。今天接到上級(jí)的任務(wù),去某地殺掉一名為敵對(duì)勢(shì)力工作的人,,然后經(jīng)過易容,,偽裝成他來執(zhí)行潛伏。那么目標(biāo)是誰呢,?他又住在哪里呢,?拿起MSDN情報(bào)科送來的文件看看吧,目標(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)住在幾號(hào)樓幾層幾號(hào)啊,。不過沒有關(guān)系我們是特務(wù)嘛,。這難不倒我們的,Let's begin 坐著藍(lán)衣社為我們準(zhǔn)備的專機(jī)SetWindowsHookEx把我們(DLL)注入到xxx.exe市,,哈哈~下了飛機(jī)來到一個(gè)陌生的城市真是倆眼一摸黑呀,,怎么找我們的目的地呢?最簡(jiǎn)單的辦法:打車,,于是我們攔了一輛GetModuleHandle(NULL)牌的出租車,,誰知道一上車。司機(jī)看到我給他的地址后告訴我要去的地方需要過海,,沒辦法直接到,,只能先送我到IMAGE_DOS_HEADER區(qū),然后再換船過海才行,。聽得我暈頭轉(zhuǎn)向的,,沒辦法,,走吧。,。,。閑來打量一下這個(gè)城市,滿眼盡是一些由0和1搭建黑白2色的高樓大廈,顏色和款式是多么的單調(diào)枯燥哇,算了,,還是閉目養(yǎng)神吧,。不一會(huì)我們來到了IMAGE_DOS_HEADER區(qū)的水路碼頭,該下車換乘船過海啦,。于是買了(ModuleAddress+ImageDosHearderPointer->e_lfanew+24)號(hào)渡輪的船票,,繼續(xù)這次無聊的旅程。暈船從頭吐到尾,,好不容易熬到了地方,,原來一出了碼頭就來到了IMAGE_OPTIONAL_HEADER街,抬頭一望在我們12點(diǎn)鐘方向,,有個(gè)小巴站臺(tái),,走過去這么一看才知道原來坐 ModuleAddress+ImageOptionalHeaderPointer->DataDirectory [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress線小巴,可以直達(dá)我們的終點(diǎn):IMAGE_IMPORT_DESCRIPTOR小區(qū),。二話不說,,走著您嘚~~~~~馬上要到目的地啦。趁坐小巴的這段時(shí)間我們先來整理下思緒,,因?yàn)椴恢谰唧w的樓號(hào)和門牌號(hào),,所以我們要先想個(gè)什么辦法,于是找同車的一位美麗小姐打聽得知,,IMAGE_IMPORT_DESCRIPTOR小區(qū)的每座樓都是同一樣式的,,那就是ImageImportDescriptorPointer->FirstThunk風(fēng)格,又知道一樓大廳有個(gè)收信箱,,上面登記著住戶的姓名,。有了這些線索我們就下車一棟樓,一棟樓的找吧,,雖然這個(gè)方案毫無創(chuàng)意,,但是記得有位大師曾經(jīng)說過:往往通過復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和華麗的算法并不是解決問題的好方法,因?yàn)樗哟罅司S護(hù)和調(diào)試的難度,。所以我們就用這種直接而有效的土辦法來老老實(shí)實(shí)的找吧:先逐樓搜索 while(ImageImportDescriptorPointer->FirstThunk!=0),,然后找到一樓大廳的收信箱 TargetName=(LPCTSTR)((DWORD)ModuleAddress+ImageImportDescriptorPointer->Name),再逐戶的查找姓“USER32.dll”的家族 if(TargetName.Compare(_T("USER32.dll"))==0),,沒過多久我們終于找到了,,登記簿上寫明了具體的門牌號(hào),哈哈~這表示我們離成功只有一步之遙,于是迫不及待的走進(jìn)ModuleAddress+ImageImportDescriptorPointer->FirstThunk號(hào)電梯,,走向“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)的眉心,。此時(shí)此刻,,在場(chǎng)的所有人都清楚了我的來意。死亡的恐懼籠罩在每個(gè)人的心頭,,我不在多做停留,,無情的勾動(dòng)了扳機(jī),這就是一個(gè)特務(wù)的專業(yè)操守,。嘿嘿 ::WriteProcessMemory((HANDLE)-1,FunctionAddress,&FunctionOfSelf,sizeof(DWORD), NULL)目標(biāo)應(yīng)聲倒地,。在手槍的淫威下,其他人也只好無奈的接受這痛苦的事實(shí),,我也就成功的完成了此次任務(wù),。開始潛伏。 接下來換種語言再來講述一下這個(gè)故事: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; } 我再換一種語言來講述: 大家都知道其實(shí)我上述我說地名都是一些結(jié)構(gòu)體,,而Windows就是靠無數(shù)個(gè)這樣的結(jié)構(gòu)體連接搭建起來的,你中有我,,我中有你,,這就好比一個(gè)生物由無數(shù)個(gè)細(xì)胞組成的一樣。因?yàn)榻裉煳覀儾皇侵v述windows體系,所以我也不做過多解釋,。 那么通過IAT修改來實(shí)現(xiàn)API HOOK的實(shí)現(xiàn)原理是什么呢,?關(guān)鍵就在于如何在茫茫的內(nèi)存中找到我們的目標(biāo),聽起來好像很恐怖,,像大海撈針,,其實(shí)沒那么難,因?yàn)閣indows就是通過那些結(jié)構(gòu)體把數(shù)據(jù)在內(nèi)存中全部線形連接起來啦,。這就像玩大富翁,,或者一種挖寶藏游戲,從起點(diǎn)出發(fā)走幾步就會(huì)得到一個(gè)提示,,告訴我們下一步該怎么走?,F(xiàn)在我就帶領(lǐng)大家來玩一次這種游戲 先來說明游戲規(guī)則 WORD=2步 DWORD=4步 指針=進(jìn)入下一房間 數(shù)組=翻卡片得到答案 RVA=地圖寶箱 IMAGE_DOS_HEADER STRUCT { 這里就是我們的起點(diǎn),可以通過GetModuleHandle(NULL)獲得,,比如起點(diǎn)地址是0x00000000,,我們得到的第一個(gè)提示 (ModuleAddress+ImageDosHearderPointer->e_lfanew+24),怎么理解這個(gè)提示呢?就是從起點(diǎn)開始走36步,,找到一個(gè)叫e_lfanew的門(因?yàn)樗侵羔?,所以他就是通往下一個(gè)房間的大門),然后推門進(jìn)去后再走24步,,我們就找到了第二個(gè)將要給我們提示的地方 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 { 我們通過上一個(gè)提示來到了這個(gè)房間,,同樣我們得到了又一個(gè)提示 ModuleAddress+ImageOptionalHeaderPointer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) 理解下這個(gè)提示。這代表我們從起點(diǎn)走到e_lfanew大門進(jìn)入這個(gè)房間再走96步找到DataDirectory開始翻卡片(他是一個(gè)結(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 { 通過上面的提示,,我們來到了這個(gè)房間,,那么下一個(gè)提示是什么呢?那就是while(ImageImportDescriptorPointer->FirstThunk!=0),,再來理解下這個(gè)提示,。它是說讓我們找到FirstThunk這個(gè)地圖寶箱(RAV)在里面找到我們需要的地圖才能找到下一個(gè)房間,那我們就一張一張的翻看吧 我們要找的地圖名就是“USER32.dll” union Characteristics OriginalFirstThunk ends TimeDateStamp ForwarderChain Name1 FirstThunk } IMAGE_IMPORT_DESCRIPTOR ENDS 有了地圖我們就來到了下一個(gè)房間 IMAGE_THUNK_DATA32 STRUCT 哈哈,。這就使傳說中的IAT表啦,,就差一步就找到寶藏啦。我們看一下這次的提示while(ImageThunkDataPointer->u1.Function),,它是說讓后找到u1.Function這個(gè)卡片商來得到寶藏位置的答案,,只是這次我們不知道第幾張卡片里有我們要的答案。沒辦法挨個(gè)翻吧,。指導(dǎo)找到 PeekMessage union u1 ForwarderString Function Ordinal AddressOfData ends IMAGE_THUNK_DATA32 ENDS 找到后,,還用我教嗎,?關(guān)閉他的保護(hù)屬性,然后改寫他的地址吧,。,。。 【小結(jié)】 這里我鉤了一個(gè)目標(biāo)程序接收消息的函數(shù)PeekMessage,,然后把它傳進(jìn)來的參數(shù)輸出出來,。看看他會(huì)不會(huì)自己給自己發(fā)消息,。HOHO,。。挺無聊的 但是如果你想鉤點(diǎn)別的,。比如目標(biāo)程序的socket,,哈哈~那你覺得這像什么呢? |
|