昨天提到了如何用Hook Ole32 提供的兩個API來實現Hook Drag & Drop的過程。算是給EasyHook庫做一個廣告吧。今天給大家講講EasyHook的實現原理。
API Hooking有兩種,內核態級別的和用戶態級別的。內核態級別的API Hooking也是很多病毒的實現原理,而且在微軟不斷強化其PatchGuard技術之后,已經越來越困難。做為建設四化的四有青年,我們還是把精力集中在用戶態級別的API Hooking上好了。用戶態的API Hooking其實就是為了在應用程序和Windows之間插一腳,從而達到用戶調用的不再直接是Windows提供的函數,而是調用我們提供的函數。我們可以選擇把調用轉交給真正的Windows提供的函數,也可以選擇自己處理掉,如果我們知道怎么處理的話。API Hooking非常強大可以應用在很多地方,比如監測(非安全領域,因為很容易繞過),虛擬化,軟件破解等。要實現API Hooking有很多選擇,基本上可以分為:
1、木馬DLL
2、修改導出表
3、修改導入表
4、代碼重寫
實現的難易程度也基本上是上述的順序。木馬DLL是最好實現的,前年我因為需要解碼nellymoser編碼的音頻,但是只有Flash播放器才有它的解碼器。所以我就把winmm.dll這個系統提供多媒體功能的dll做了一個木馬,實現了攔截wav輸出的目的。木馬DLL只需要你寫一個同名的dll,導出相同名字和簽名的函數即可。不過它的限制也最大,最大的原因是windows核心的DLL是不會從當前目錄加載的(比如user32.dll),同時它還要求木馬DLL必須預先放置在exe的同級目錄下。
修改導出和導入表限制也很大。真正的王道是代碼重寫,不過難度陡增。在這個方面比較出名的有Detours(微軟研究院的,不過已經沒更新了),和madshi(DELPHI領域的,用戶眾多)。不過最近新出了一種非常強大的庫,叫EasyHook。它具有非常獨特的特性,支持.NET。也就是說,你可以用C#來寫API鉤子。這在以前是無法想象的。它的項目地址是:http://www.codeplex.com/easyhook。EasyHook其實不光是一個API Hook的庫,它還提供了Remote Injection(遠程注入)的功能。也就是說,你不但能夠在Hook當前進程的API,還能去Hook其他進程的API。
所謂代碼重寫,是指修改DLL映射到當前進程的內存中的代碼(二進制的機器指令)。每個API都會在運行時解析出一個地址,然后程序會用call指令去調用這個地址所包含的機器指令。代碼重寫就是把這個地址所在位置的機器指令重寫了,從而達到從中插一腳的目的。簡化后的流程為:
1、調用API地址所在處的指令
2、被改寫的代碼被執行(一般就是一個jmp指令,跳轉到一個更開闊的地方去做更多的事情)
3、執行Hook
4、執行被覆蓋的指令(一般是5個字節)
5、跳轉回(API地址所在處+5個字節)的位置繼續執行原代碼
要完成這樣一個流程已經很不容易。它需要你懂得80x86的匯編語言,而且要有很強的debug能力。但是這只是一個簡化的流程,真正復雜的地方在于:
1、不可覆蓋的代碼的監測(比如jne就無法被覆蓋)
2、Hook的可靠卸載
3、被覆蓋指令長度的確定(一般是5個字節,但是那只是一般)
4、不同CPU指令集的支持(64位?)
5、Hook中調用了被Hook的API怎么辦?
對于上述問題考慮的周到與否是衡量一個API Hook庫的標準。很高興的告訴你,EasyHook把上述問題都考慮進去了。而且依靠CLR Hosting API,EasyHook還實現了.NET的API鉤子。原理就是EasyHook自己先把API調用攔截下來,然后用CLR Hosting API啟動一個CLR的runtime,然后用runtime去加載一個GAC中的Assembly,然后用反射去調用你的代碼,并且給你提供了足夠的RuntimeInfo,使得你可以知道當前的環境,以及回調原始的API。不過仍然在Hook系統的API的時候,仍然要非常小心死鎖的問題。
EasyHook也不是完美的。據我個人測試,它遠程Hook .NET產生出來的exe有問題。不過這是它的Remote Injection部分的問題(很可能是CreateRemoteThread與CLR沖突導致的)。而且EasyHook確定指令長度那塊內嵌的反匯編器來路不明(作者自述是從一個德文論壇上下載的程序反匯編來的),其實可以考慮用distorm代替。
其內部實現有一段關鍵的匯編代碼叫trampoline(蹦床)。這段代碼就是那被覆蓋的5個字節要jmp到的一段代碼,它負責調用你的Hook和善后。所以說如果你用的是.NET寫的鉤子,這是一個三級跳的過程:
1、應用程序調用API
2、API被調用(其實是我們的jmp指令被調用,因為頭5個字節被覆蓋了)
3、那個5個字節的jmp,跳轉到了trampoline,這蹦床被jmp了,哈哈,所以彈了回去
4、trampoline跳轉到了EasyHook的Thread Deadlock Barrier
5、EasyHook再用CLR Hosting API跳轉到了我們的.NET代碼
非常Hard Core。有機會給大家注釋這段代碼(我沒告訴你EasyHook是開源的嗎?),不過今天就寫到這里了。
浙公網安備 33010602011771號