63] 接下來只需要使用LoadLibraryW主動(dòng)加載保護(hù)dll,然后執(zhí)行上面提及的0ΕΡ(入 口函數(shù)),就能運(yùn)行被保護(hù)的程序了:
[0064] module->exeEntry()
[0065] 這個(gè)函數(shù)是不會(huì)立刻放回的,除非程序執(zhí)行完畢。
[0066] 這樣做是為了反dump,如果使用dump軟件,dump出的內(nèi)存鏡像只是加載器的,和受 保護(hù)的exe鏡像無關(guān)。這樣加載器只是一個(gè)外殼,而真正的執(zhí)行部分對(duì)操作系統(tǒng)來說是不可 見的。
[0067] 3、檢查exe模塊的IAT是否被劫持
[0068] 該步驟包括以下兩個(gè)子步驟:3.1)獲取IAT中的dll帶入列表,對(duì)其中每一個(gè)API地 址做一次檢查,看記錄的API地址和實(shí)際的API是否一致,如果有不同的,則可以認(rèn)為該exe 模塊的IAT被做了改動(dòng)(可能是被Hook過的),接著,3.2)對(duì)每個(gè)導(dǎo)入的系統(tǒng)dl 1做一次鏡像 Hash檢查,使用的Hash函數(shù)是zlib中的Adler32函數(shù),如果內(nèi)存鏡像的Hash值和本地文件的 Hash不同,則可以認(rèn)為這個(gè)dl 1的內(nèi)存鏡像被修改過,
[0069] 如果發(fā)現(xiàn)上面幾個(gè)子步驟存在問題,則返回至步驟3.1)的初始狀態(tài)或返回到步驟 2,如果上面子步驟都沒有問題,則執(zhí)行第4步。
[0070] API是至關(guān)重要的,程序和系統(tǒng)的交互全靠API調(diào)用。如果API被劫持,程序的很多 功能都能被隨意修改。API方面的Hook分為兩種,第一種是IAT Hook,第二種則是 InlineHook〇
[0071] 進(jìn)過第二步以后,除了延遲加載的dll,其他的dll文件都已經(jīng)加載好了。
[0072] 然后程序代碼中使用LoadLibrary所加載的dll都是在保護(hù)dll加載之后才注入程 序空間的。這個(gè)時(shí)候?yàn)榱朔乐筫xe的IAT做了修改,嵌入了別的惡意dll,保護(hù)系統(tǒng)會(huì)做如下 檢查:
[0073] 針對(duì)IAT Hook,對(duì)IAT做一次遍歷,先檢查每個(gè)API的的地址是否和備份中的值相 同,如果不同的話,可以認(rèn)為被保護(hù)的進(jìn)程受到了IAT Hook。
[0074] 主要的檢查對(duì)象為系統(tǒng)dll(如:kernel32.dlUGdi32.dll)
[0075]對(duì)于每一個(gè)表項(xiàng)都是如此獲取的。
[0076] char*pszDllName=(char*)((BYTE*)hModule+pImportDesc->Name);
[0077] 獲取當(dāng)前dll名字。
[0078] IMAGE_THUNK_DATA*pThunk= (IMAGE_THUNK_DATA*) ((BYTE*)hModule + pImportDesc->OriginalFirstThunk);
[0079] char*pszFuncName=(char*)((BYTE*)hModule+pThunk_>ul.AddressOfData+2);
[0080] pszFunName就是當(dāng)前API的名字
[0081 ] PDWORD lpAddr=(DWORD*)((BYTE*)hModule+pImportDesc->FirstThunk)+n;
[0082] DWORD Addr = *(DWORD*)((DWORD)IpAddr);
[0083] Addr記錄的是當(dāng)前API的入口地址
[0084] 其中hModule是exe內(nèi)存鏡像偏移量的開始。
[0085]然后保護(hù)程序自動(dòng)獲取到API本來的地址:
[0086] HMODULE hTemp=GetModuleHandleA(pszFuncName);
[0087] PDWORD 0riAddr=(PDWORD)GetProcAddress(hTemp,pszFuncName);
[0088] 如果OriAddr和Addr的值相同,我們就可以認(rèn)為,API沒有被IAT Hook。
[0089] 針對(duì)InlineHook,需要到對(duì)用的系統(tǒng)dll段做一次檢查。
[0090]因?yàn)橛脩糇约旱膁ll動(dòng)態(tài)鏈接庫可能做了smc或者代碼虛擬化加密,故不做檢查。 [0091]在上述的IAT檢查中,對(duì)每次遍歷到一個(gè)dll導(dǎo)入的時(shí)候,先判斷是否是系統(tǒng)dll。
[0092] 使用zlib中的Adler32Hash算法,對(duì)整個(gè)dll內(nèi)存鏡像模塊做一次Hash計(jì)算,最后 使用GetModuleFileNameW獲取該dl 1的完整路徑,然后對(duì)這個(gè)文件做一次Hash,如果二者 Hash值不同的話,可以認(rèn)為這個(gè)dl 1鏡像被惡意修改。
[0093] 4、主動(dòng)劫持1^〇&(11^13瓜^和1^〇&(11^13瓜15^1(包含多字節(jié)版本和1]11;[(30(16版本)
[0094] 劫持1^〇&北:[13抑^和1^0&(11^1^&15^1(這兩個(gè)4?1都包含多字節(jié)版本和1]11;[(30(16版 本,所以實(shí)際上是4個(gè)API),對(duì)加載成果的dll鏡像執(zhí)行上述步驟3中的操作,如果判定有異 常,則返回步驟2或步驟3,如果判定沒有異常,則執(zhí)行第5步。
[0095] Exe模塊每主動(dòng)調(diào)用一個(gè)dll的時(shí)候,必定會(huì)使用到LoadLibrary或者是 LoadLibraryEx,對(duì)這兩個(gè)API做劫持,每當(dāng)加載一個(gè)dll鏡像的時(shí)候,使用步驟3中的方法, 對(duì)dll的IAT做一次檢查,如果有導(dǎo)入函數(shù)的地址和原本的地址不同,就可以認(rèn)為這個(gè)dll的 IAT被做了修改。
[0096] 5、劫持 CreateProcess 函數(shù)
[0097] 劫持CreateProcess函數(shù),并且在創(chuàng)建子進(jìn)程同時(shí),注入本保護(hù)dll。如果該進(jìn)程創(chuàng) 建了子進(jìn)程,本保護(hù)dll是和子進(jìn)程無關(guān)的,這樣一來,子進(jìn)程是未受保護(hù)的狀態(tài)。所以本保 護(hù)劫持了CreateProcess函數(shù),在創(chuàng)建子進(jìn)程的時(shí)候,同時(shí)注入本保護(hù)dll。主要的方式就是 在創(chuàng)建進(jìn)程的時(shí)候,先保存原來的創(chuàng)建flag,為創(chuàng)建flag中添加CREATE_SUSPEND的flag進(jìn) 行創(chuàng)建。
[0098] 原理就是在建立子進(jìn)程的時(shí)候,劫持EIP,保存CPU的Context,注入ShellCode來加 載內(nèi)存保護(hù)dl 1,最后釋放Shel ICode的內(nèi)存,并且恢復(fù)CPU的Context。這樣注入之后,不僅 子進(jìn)程得到了保護(hù),并且不會(huì)因?yàn)榧虞d了保護(hù)dll而在子進(jìn)程的內(nèi)存鏡像中留下痕跡。
[0099] 如果該進(jìn)程創(chuàng)建了子進(jìn)程,本保護(hù)dll是和子進(jìn)程無關(guān)的,這樣一來,子進(jìn)程是未 受保護(hù)的狀態(tài)。所以本保護(hù)劫持了 CreateProcess函數(shù),在創(chuàng)建子進(jìn)程的時(shí)候,同時(shí)注入本 保護(hù)dll。
[0100] 具體而言,主要的方式就是在創(chuàng)建進(jìn)程的時(shí)候,先保存原來的創(chuàng)建flag,為創(chuàng)建 flag中添加CREATE_SUSPEND的flag進(jìn)行創(chuàng)建:
[0101 ] dwCreaFlags = dwCreationFlags | CREATE_SUSPENDED;
[0102] 保存當(dāng)前的Context值。
[0103] 將CPU寄存器的值依次保存到CONTEXT里面。
[0104] 開辟在子進(jìn)程中,使用VirutalAlloc開辟一段長度大小為0x1000的內(nèi)存,清空為 0.
[0105] 劫持EIP,注入本保護(hù)dll:
[0106] 將EIP的值改為之前VirtualAlloc的分配內(nèi)存的首部。
[0107] -次輸入以下的ShellCode
[0108] Push offset
[0109] Call LoadLibraryff
[0110] 其中offset是欲注入的dll名字的字符串地址,因?yàn)橐陨系闹噶钪恍枰ㄙM(fèi)10字 節(jié)的空間,所以在
[0111] 分配的內(nèi)存0x20處寫入字符串,而off set就是這個(gè)字符串的地址。
[0112] 而LoadLibraryW就是LoadLibraryW的地址,這樣就能保證子進(jìn)程能加載到保護(hù) dll〇
[0113] 執(zhí)行 ShellCode:
[0114] 使用ResumeThread(hThread)恢復(fù)主線程,并執(zhí)行這段 ShellCode。
[0115] 恢復(fù)當(dāng)前Context值:
[0116] 將CPU寄存器的值恢復(fù),因?yàn)閃in32API都是stdcall,所有加載保護(hù)dll之后不需要 主動(dòng)去清理堆棧。然后使用SuspendThread這個(gè)API暫停主線程。
[0?17] 做清理工作,釋放用VirtualAl loc分配出來的內(nèi)存。
[0118] 如果當(dāng)前原始的創(chuàng)建f 1 ag中就有CREATE_SUSPEND,則直接返回結(jié)果;如果沒有這 個(gè)flag,在返回結(jié)果之前需要恢復(fù)主線程。
[0119] 由于步驟5不是立刻能獲取到結(jié)果的,就繼續(xù)執(zhí)行第六步。因此,上述方法還可進(jìn) 一步包括以下步驟
[0120] 6、防止線程注入
[0121] 通過遠(yuǎn)程線程注入,在Ring3環(huán)境下注入被保護(hù)的目標(biāo)進(jìn)程,防止惡意加載器的線 程注入。
[0122] 也就是說,到此為止,如果還想在Ring3環(huán)境下注入被保護(hù)的目標(biāo)進(jìn)程,方法只有 一種,那就是遠(yuǎn)程線程注入。惡意加載器在遠(yuǎn)程調(diào)用以下三種&63丨61^1]10丨61'11^3(1、 VirtualAl loc和LoadLibrary相關(guān)的API (如LoadLibraryEx)就行完成線程注入。
[0123] CreateRemoteThread注入dl 1的時(shí)候,這個(gè)時(shí)候,在內(nèi)核會(huì)為被注入的進(jìn)程創(chuàng)建一 個(gè)線程。自然而然,所有已加載的dll會(huì)在DllMain入口點(diǎn)收到DLL_THREAD_ATTACH的通知, 此時(shí)調(diào)用GetCurrentThreadld的時(shí)候就能獲取當(dāng)前創(chuàng)建的線程id。接著保護(hù)dll從ntdll處 Hook ZwCreateThreadExZwTe