鉤子:(Hook)是Windows中提供的一種用以替換DOS下“中斷”的系統(tǒng)機制,中文譯為“掛鉤”或“鉤子”。鉤子在軟件中的應用可以說是很廣泛了,想當年學習鉤子那會兒,什么IAT鉤子,INLINE鉤子......真是有點老??心瞎?.....
看多了網(wǎng)上的其他一些關于此話題的文章,感覺對于剛接觸的人不適用,所以今天把經驗與大家分享(高手就不用看了......呵呵.)
WINDOWS
HOOK有什么用呢?說的明白點就是給特定的某一個事件,說的具體點是給一個函數(shù)掛上一個鉤子(我們自己的函數(shù)),讓它在執(zhí)行前先執(zhí)行我們掛的鉤子(我們掛接的函數(shù)),從而達到攔截事件和函數(shù)調用等的目的.
IAT鉤子:IAT(Import
Address
Table)意思是導入地址表,有的朋友看不懂了,!什么是導入地址表.你可以去下載一個exescope,然后用它打開一個EXE文件看看導入欄,一個程序在執(zhí)行時,它所(靜態(tài)調用,即不用loadlibrary法)調用的函數(shù),在它被編譯成為一個可執(zhí)行文件時,這些模塊的函數(shù)的地址就會被寫入導入表,IAT鉤子即是替換IAT表中的這些地址為自己的函數(shù)地址,那么當程序調用到這個地址的時候就會跳到我們的函數(shù)里面來,在這里替換并不是說我們要去替換文件中的IAT表,而是要去替換內存中的IAT表,一個可執(zhí)行文件運行之后,它的程序主體會做為一個模塊被加載到內存里,在需要調用函數(shù)的時候它會去IAT表中查詢所調用的函數(shù)地址,我們只需修改內存中IAT表地址即可,具體修改方法很簡單,這里不作介紹,那么怎么樣去修改呢,換句話說,我們怎么知道什么時候去修改?這個時候就需要用到一個windows系統(tǒng)函數(shù)SetWindowsHookEx(方法當然不只這一種,比如用遠程注入也可以,有興趣可以去網(wǎng)上查詢),這個函數(shù)會在運行時負責把我們的DLL文件帶到與我們所注冊的鉤子有關的所有程序中(注意進程的用戶權限),這就是為什么用戶態(tài)的全局鉤要做成DLL(動態(tài)鏈接庫的原因).
比如,我們用SetWindowsHookEx注冊一個鼠標鉤子,并用一個A.DLL的模塊與之掛鉤,那么在WINDOWS系統(tǒng)中響應鼠標事件的所有程序會先加載A.DLL,在加載DLL初始化時(執(zhí)行DLLMAIN函數(shù))我們就可以先在內存中找出IAT中的函數(shù)地址,再用WriteProcessMemory或memcpy修改為A.DLL模塊內的某個函數(shù)(我們掛鉤的函數(shù)),那么這個程序在響應鼠標事件時會先執(zhí)行我們掛好的那個在A.DLL的某個函數(shù),這個時候大權就在我們手里了,我們可以讓它繼續(xù)執(zhí)行,也可以讓它立即返回無效.同理我們可以掛鉤所有響應鍵盤事件的程序加載我們的模塊以達到監(jiān)視鍵盤記錄的作用,也可以掛鉤所有響應消息的程序來掛鉤NtopenProcess和NtTerminateProcess來防止我們的程序被其他程序結束.也可以掛鉤諸多的API函數(shù)來監(jiān)視系統(tǒng)API的調用,不過在這里有個誤區(qū),SetWindowsHookEx只會負責把你的DLL加載到其他的執(zhí)行程序(不會執(zhí)行),所以我們的替換動作必須在DLLMAIN函數(shù)中(或調用)完成.還有一點要注意IAT
HOOK修改的只是IAT表而已,并不是函數(shù)本身,而下面這中方法就是直接修改函數(shù)本身實現(xiàn)HOOK,同理,都可以用SetWindowsHookEx注冊一個鉤子把我們的鉤子模塊帶到其他進程。那么它是怎么實現(xiàn)HOOK的呢?我們需要準備一個5個字節(jié)的BYTE(或char)變量,然后把它的第一個字節(jié)設為0xE9(16進制),然后把(我們的函數(shù)地址-原函數(shù)地址-5)(long類型)寫入從第2個字節(jié)開始后的4個字節(jié),我們都知道LONG類型所占的空間剛好為4個字節(jié)(在這眾多類型里面,不管它的類型怎么變,它都有以字節(jié)為單位的大小,在我看來,把一個變量叫做int
也好 long 也好,純粹是為了編寫程序方便和好記,因為我們完全可以把int x 定義為 BYTE
x[2]),再把這5個字節(jié)復制到內存中以原函數(shù)(要掛鉤的那個函數(shù))地址為起始點的5個字節(jié),這么做的意義是什么呢?可能有的人不知道0xE9
相當于匯編里的Jmp指令,(當然有點反匯編基礎的人都知道如0xC3 = ret,
0xE9=Jmp,、、很多)在匯編里jmp是一個跳轉指令,在這里就是讓它跳轉至后面的4個字節(jié)指示的位置(我們的函數(shù)地址),從而達到HOOK的目的,這就是為什么叫INLINE
HOOK為5字節(jié)跳轉法,同樣的這種方法不僅僅適用于用戶態(tài),它還適用于核心態(tài),這就是SDT INLINE
HOOK。
在系統(tǒng)內核有一個內核文件的加載的鏡像(內核模塊),NTOSKRNL或ntkrnlpa及ntkrnlmp、ntkrnlup、ntkrpamp都是OS內核文件,它提供了一整套核心態(tài)函數(shù),供用戶態(tài)和核心態(tài)調用,我們在用戶態(tài)調用的函數(shù)大多數(shù)在最后都進入了核心態(tài)如kernel32.dll的OpenProcess,在調用OpenProcess,以后會進入Ntdll.dll中的NtOpenProcess然后調用系統(tǒng)函數(shù)調用號進入核心態(tài)的NtOpenProcess,所以核心態(tài)的鉤子才是王道,核心態(tài)的鉤子有SSDT
HOOK和SDT INLINE
HOOK,上面說了,內核導出了一系列的函數(shù)供用戶態(tài)調用,那么導出的這個函數(shù)表就是SSDT(系統(tǒng)服務描述表),它的地址可由內核導出的一個變量
KeServiceDescriptorTable 查詢,每4個字節(jié)(long 大?。橐粋€地址,在SSDT
HOOK時我們只需要修改SSDT表中的地址為我們的地址即可,相當簡單.
例:修改NtOpenProcess:
*(ULONG*)(KeServiceDescriptorTable+(0x7A*4))=(ULONG)MyOpenProcess
0x7A為NtOpenProcess的系統(tǒng)調用號,每個調用號占4個字節(jié),KeServiceDescriptorTable+(0x7A*4)就代表了NtOpenProcess地址的存放位置,MyOpenProcess為我們自定義的函數(shù),這樣系統(tǒng)在執(zhí)行NtOpenProcess的時候就會跳到MyOpenProcess里。還有一種就是Inline
Hook它和用戶態(tài)的一樣直接修改函數(shù)前5個字節(jié)即可,這里不再贅述。但是在核心態(tài)的inline
hook比較危險,這涉及到多線程調用,有興趣的朋友可以去查閱該方面資料。
已經講了這幾種HOOK
的實現(xiàn)原理,那么我們怎么來預防Hook呢。在用戶態(tài)我們可以掛接(inlinehook)LoadLibraryExW來判斷,這個資料在網(wǎng)上很多,朋友們可以自己去找,還有一點就是,網(wǎng)上的方法不能防止遠程注入HOOK,在LoadLibrary的時候加上判斷TED或PEB就可以防止了。上述原理有不懂的或具體實現(xiàn)方法可聯(lián)系我。(2008)