IAT HOOK簡介
API HOOK估計沒必要多介紹了,簡單的來講就是通過某種方式來改變API函數(shù)的工作流程.一般來講有兩種方法:IAT
HOOK和INLINE
HOOK.前一種應用較為廣泛,一方面因為簡單,還一方面因為穩(wěn)定.他的原理就是改寫進程空間中要HOOK的API所在模塊的函數(shù)引入表,使之指向替換原
API函數(shù)的函數(shù)地址(某些木馬就是利用IAT
HOOK的方式,掛鉤NtQuerySystemInformation的方式來實現(xiàn)進程隱藏).這里感覺還是有必要再說一點INLINE
HOOK,這個復雜點,直接進入被HOOK的API函數(shù)內(nèi)部去修改他,采用指令call或者jmp等,迫使API改變流程,跳到自己的替換函數(shù)中.通常都
是在函數(shù)頭部前10個字節(jié)內(nèi)修改.
如何搞定IAT HOOK
注意這里說的是搞定,而不是修復IAT HOOK,搞定所指的就是只要不讓那個IAT HOOK起作用就行了.
方案1:通過LoadLibrary和GetProcAddress來動態(tài)獲取API地址.
忘了是在哪看到過這個方法,實際上這個方法可以說根本無效,Jeffrey
Richter在核心編程里面給出的例子就提到過這個問題,為了在動態(tài)獲取API調(diào)用的情況下也能讓HOOK生效,首先就應該把
LoadLibraryA,LoadLibraryW......等等那幾個可以實現(xiàn)動態(tài)獲取的函數(shù)全部都HOOK住.這種方案被直接否決了.
方案2:直接硬編碼,從ntdll.dll里面調(diào)用NativeAPI.
雖然聽起來有點恐怖,但這種方式確實比方案1要有效,只是ntdll.dll有500多函數(shù),全部應編碼有點天方夜譚,如果單純了為了對付某幾個特定的HOOK還是可以的.
方案3:直接從PE文件入手,自己讀取導出表獲取API地址
這也是我認為對付IAT HOOK最有效的方式,無論是檢測IAT HOOK還是繞過IAT HOOK或者是修復IAT HOOK都必須走這一步.
實現(xiàn)方式
既然從PE文件入手,就不能不熟悉PE文件格式了,這里著重介紹一下導出表,因為這個表里面dll所導出的API函數(shù)地址,先來看一看導出表的數(shù)據(jù)結(jié)構(gòu):
typedef struct _IMAGE_EXPORT_DIRECTORY {
????? DWORD???? Characteristics;
????? DWORD???? TimeDateStamp;
????? WORD????? MajorVersion;
????? WORD????? MinorVersion;
????? DWORD???? Name;
????? DWORD???? Base;
????? DWORD???? NumberOfFunctions;
????? DWORD???? NumberOfNames;
????? DWORD???? AddressOfFunctions;?????? // RVA from base of image,函數(shù)地址相對于鏡像基址的偏移
????? DWORD???? AddressOfNames;?????????? // RVA from base of image,函數(shù)名表相對于鏡像基址的偏移
????? DWORD???? AddressOfNameOrdinals;??? // RVA from base of image,函數(shù)名和序號的對應表相對于鏡像基址的偏移
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
重要的就是那三個加了中文注釋的地方,這里存著沒有被HOOK踐踏過的真真切切的API信息,但是問題來了,這里不能直接用,因為是內(nèi)存鏡像偏移,
先要轉(zhuǎn)化為文件偏移才可以,如何轉(zhuǎn)換呢?羅sir的<WIN32匯編語言程序設(shè)計>里面寫的很清楚了,首先看虛擬偏移RAV落在內(nèi)存鏡像中的
哪一個節(jié)中,然后減去內(nèi)存鏡像中這個節(jié)的起始值得到一個RAV',然后在節(jié)表中看這個節(jié)在文件中所在的偏移地址PointToRawData,然后用
PointToRawData+RAV'就是在文件中的偏移了.
算法看起來好像有點繁,但實際上不要忘了我們是為了對抗IAT
HOOK的,這樣一來可以簡化不少,看一看那幾個dll,ntdll.dll,kernel32.dll等等等等包含常用API的dll,我們要的東西都
在.text節(jié)里面,上述轉(zhuǎn)換過程可以直接變?yōu)?
dwRav-m_pOptionHeader->BaseOfCode+m_pOptionHeader->SizeOfHeaders.
轉(zhuǎn)換問題解決了,就來看看如何獲取我們想要的地址,廢話少說,上代碼:
//m_pBuffer是將dll用ReadFile讀入內(nèi)存后的緩沖區(qū)地址
FARPROC CLoadDll::SearchProcAddress(LPCTSTR strFunctionName)
{
DWORD dwIndex = 0;
DWORD dwRawNameAddr;??? //函數(shù)名表地址
DWORD dwRawAddrIndex; //函數(shù)名與編號轉(zhuǎn)換表
DWORD dwRawFuncAddr;??? //函數(shù)表地址
DWORD dwFuncOffset;???? //函數(shù)的文件偏移地址
dwRawNameAddr = RavToOffset(m_pExportDesc->AddressOfNames);????? //獲取PE文件中函數(shù)名表的偏移
dwRawAddrIndex = RavToOffset(m_pExportDesc->AddressOfNameOrdinals);???? //獲取PE文件中函數(shù)名-序號對應表的偏移
dwRawFuncAddr = RavToOffset(m_pExportDesc->AddressOfFunctions);??? //獲取PE文件中函數(shù)地址表偏移
for(dwIndex = 0; dwIndex < m_pExportDesc->NumberOfFunctions; dwIndex++){
??? if(strcmp(strFunctionName, (TCHAR *)(m_pBuffer + RavToOffset(*(DWORD *)(m_pBuffer+dwRawNameAddr+dwIndex*4)))) == 0){
???? dwFuncOffset = *(DWORD *)(m_pBuffer + dwRawFuncAddr + (*(WORD *)(m_pBuffer + dwRawAddrIndex + 2*dwIndex))*4);
???? dwFuncOffset += m_dwImageBase;
???? return (FARPROC)dwFuncOffset;
??? }
}
MessageBox(NULL, "導出表中無此函數(shù)!", "提示", MB_OK | MB_ICONINFORMATION);
return NULL;
}
這樣以來我們就可以隨心所欲的繞過R3態(tài)IAT HOOK了,直接去ntdll.dll導出native api來用,怎一個爽字了得.
最后來看看效果截圖,用自己的CDllLoad類做了一個進程管理器,直接去ntdll.dll中獲取NtQuerySystemInformation來看看進程,另外還開了一個IAT HOOK來隱藏進程,再拿優(yōu)化大師的進程管理器對比一下效果:
結(jié)束語
原理已經(jīng)知道了,實際我們還可以做更多的事情,比如枚舉dll中的導出函數(shù)獲取地址,可以檢查系統(tǒng)中安裝的IAT HOOK,或者再走遠一點,直接用獲取的未污染的API地址,把HOOK修復吧.