[轉]C/C++ HOOK API(原理深入剖析之-LoadLibraryA)

9月都快結束了,之前一直忙到寫自己的東西加上上班。基本沒有時間研究下彙編和C C++方面的感興趣的東西。再怎麼說嘛,9月還是得寫一篇撒,以後每月至少一篇吧。給自己定了,希望大家監督。嘿嘿!

這篇文章就來談談平常很常見的HOOK技術,這裏呢。寫得比較簡單,方法很多。只講原理!希望大鳥們別吐我口水哈 - -。好!切入正題。

首先是概念吧。什麼是鉤子(HOOK)?

鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達後,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。
這上面只是一個概念,對它有所瞭解而已。上面主要應用在Windows消息處理機制裏面的一個解釋。這裏我只是單純的談談攔截我們常用的LoadLibraryA加載這個函數。讓我們的程序或者目標程序在調用這個函數加載鏈接庫的時候,先執行我們自己寫的函數,然後在進行正常加載。通俗的說就是a----->b.  我們在中間加上一個c。 a-------->c----->b讓他先執行c然後再執行b。這裏的c就是我們自己的函數了。
呵呵,概念說得差不多了,開始行動寫代碼撒:


 

上面這個程序是我寫的一個測試。原理很簡單,也就是在調用LoadLibraryA 加載動態鏈接庫之前,先把LoadLibraryA的前16個代碼字節給替換成我們自己的HOOK攔截代碼,原理跟我之前的一篇Shell Code原理類似!改變了前16個字節後,這時就是已經HOOK了的LoadLibraryA了。然後在程序調用這個函數,進入後。將先調用我們自己寫的函數。這裏我們自己的函數是myLoadLibrary。這裏面我就隨便寫了個測試。彈一個MessageBox顯示DLL的名稱!然後再執行正常的LoadLibraryA。看到這裏,或許大家會產生兩個疑問。
1.爲什麼替換的是16個字節?
2.在調用了我們的函數後,再調用正常的LoadLibraryA。這裏的LoadLibraryA不是已經被我們給替換了嗎?怎麼正常呢?
首先,第一個問題。這裏就得看上方藍色的函數__InlineHOOK_Base 了。先是這個結構體:
    DWORD _argsBytes;           // 參數所佔的字節數
    void* _lawFunc;                  // 指向老的Hook前的LoadLibraryA函數的一個指針
    void* _newFunc;                 // 指向我們自己的中間函數的指針
    char  _lawByteCode[16];    // 保存正常的LoadLibraryA前16個代碼字節,用於UnHook,不然怎麼還原呢。呵呵!
    char  _newByteCode[16];   // 我們替換給LoadLibraryA的16個代碼字節,用於Hook,不然怎麼執行我們自己的函數呢,呵呵!
所以在我們調用LoadLibraryA之前會調用__InlineHOOK_Base構造函數。因爲是全局對象。如:DECLARE_REGISTER ( __inline_hook , LoadLibraryA , myLoadLibrary );

 

_newByteCode[ 0 ] = 0xB9;                               // mov ecx, ...
    ( DWORD& )_newByteCode[ 1 ] = ( DWORD )this;
    _newByteCode[ 5 ] = 0xB8;                              // mov eax, ...
    ( DWORD& )_newByteCode[ 6 ] = ( DWORD )__Inline_Hook_Func;
    ( WORD& )_newByteCode[ 10 ] = 0xD0FF;       // call eax
    _newByteCode[ 12 ] = 0x000000C3;               // ret

    if ( _newByteCode > 0 )
    {
        _newByteCode[ 12 ] = 0xC2;        // ret ...
        ( WORD& )_newByteCode[ 13 ] = ( WORD )_argsBytes;
        _newByteCode[ 15 ] = 0;
    }

 

上面這段代碼的功能就是將語句轉化成字節碼,存到_newByteCode數組中,然後在HOOK的時候會拷貝到正常的LoadLibraryA中。將其前16字節替換成這裏的。爲什麼是16字節,原因含簡單,那就是這裏的字節碼就只用得到15個。哈哈!將上面的字節碼翻譯成C++就是:
__Inline_Hook_Func();  //一句!
其他的就是爲了將this保存到ECX中,返回如果有參數,且這個LoadLibraryA只有一個參數,在我們替換的字節碼中手工給保持堆棧平衡,就會ret _argBytes這麼字節數!調用外面就不用ADD ESP了保持堆棧平衡了!這也是LoadLibraryA原先的返回方式!這裏這些指令的用法和爲什麼把this保存到ECX中,就不多說了!我的SHELL CODE那篇文章裏有提到!
 
    memcpy( _lawByteCode, _lawFunc, 16 ); // 保存正常的字節碼,用於還原!
 
這樣把我們要替換進去的字節碼給準備好了,下一步就是拷貝進去的過程了。Inline_Hook 構造函數是調用了HOOK函數的。再看HOOK函數的實現是:
// It's saved.
        if ( memcmp( _lawByteCode, _lawFunc, 16 ) == 0 )  // 看看是否保存了,否則還原不了沒辦法unhook。
        {
            DWORD dwOldFlag;
            VirtualProtect( _lawFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldFlag );  //這個函數就不用說了,呵呵,查資料吧!
            memcpy( _lawFunc, _newByteCode, 16 );    // 拷貝我們的HOOK代碼進LoadLibraryA中!
            VirtualProtect( _lawFunc, 8, dwOldFlag, &dwOldFlag );
            return true;    // 拷貝成功!
        }
unhook原理類似,也就是將正常的拷貝進去!
好了!到了現在,功能函數都差不多了,現在就差__Inline_Hook_Func 這個函數的實現了!
這個函數全是彙編。嘿嘿!我用的英文註釋了的哈。大致能看明白,英語太差了!
這裏只說它的功能:
1.這個函數的調用代碼字節我們已經拷貝到了LoadLibraryA中。如果我們調用LoadLibraryA( "XXXX.DLL" );將會調用__Inline_Hook_Func 這個函數。這個函數的前綴是不是很奇怪?大鳥別笑。呵呵!我前面的文章也提到了這些前綴的意思。這裏不用多說。現在看看我們拷貝進LoadLibraryA後,大家可以看看LoadLibraryA的字節碼的前後對比:
替換前:
7C801D7B 8B FF                                  mov         edi,edi
7C801D7D 55                                     push        ebp 
7C801D7E 8B EC                                mov         ebp,esp
7C801D80 83 7D 08 00                       cmp         dword ptr [ebp+8],0
7C801D84 53                                     push        ebx 
7C801D85 56                                      push        esi 
7C801D86 74 14                                 je          7C801D9C
7C801D88 68 60 E1 80 7C                push        7C80E160h
7C801D8D FF 75 08                          push        dword ptr [ebp+8]
7C801D90 FF 15 AC 13 80 7C           call        dword ptr ds:[7C8013ACh]
7C801D96 85 C0                               test        eax,eax
7C801D98 59                                    pop         ecx 
7C801D99 59                                    pop         ecx 
7C801D9A 74 12                               je          7C801DAE
7C801D9C 6A 00                               push        0   
7C801D9E 6A 00                               push        0   
7C801DA0 FF 75 08                           push        dword ptr [ebp+8]
7C801DA3 E8 AB FF FF FF                 call        7C801D53
7C801DA8 5E                                    pop         esi 
7C801DA9 5B                                    pop         ebx 
7C801DAA 5D                                   pop         ebp 
7C801DAB C2 04 00                         ret         4 
上面紅色的字節碼就是將要被替換的。 藍色的ret 4可以看出開始的疑問,爲什麼是ret 4.
替換後:
7C801D7B B9 E0 A5 42 00               mov         ecx,offset __Obj (42A5E0h)
7C801D80 B8 BC 12 41 00               mov         eax,offset __Inline_Hook_Func (4112BCh)
7C801D85 FF D0                               call        eax 
7C801D87 C2 04 00                         ret         4   
7C801D8A 00 80 7C FF 75 08          add         byte ptr [eax+875FF7Ch],al
7C801D90 FF 15 AC 13 80 7C          call        dword ptr ds:[7C8013ACh]
7C801D96 85 C0                              test        eax,eax
7C801D98 59                                   pop         ecx 
7C801D99 59                                   pop         ecx 
7C801D9A 74 12                               je          7C801DAE
7C801D9C 6A 00                               push        0   
7C801D9E 6A 00                               push        0   
7C801DA0 FF 75 08                           push        dword ptr [ebp+8]
7C801DA3 E8 AB FF FF FF                 call        7C801D53
7C801DA8 5E                                    pop         esi 
7C801DA9 5B                                    pop         ebx 
7C801DAA 5D                                   pop         ebp 
7C801DAB C2 04 00                         ret         4 
上面的字節碼,相信一看就明白了!呵呵!替換後就會在0x7C801D87這裏返回了,下面的代碼就作廢了! - -


2. __Inline_Hook_Func 這個函數在進入之後,先是unhook,因爲下面要調用myLoadLibrary函數。肯定要正常的LoadLibraryA函數咯。之後就是一系列的參數準備。知道調用了myLoadLibrary函數,彈出了對話框。之後回到 __Inline_Hook_Func 函數。保存返回值(句柄)。之後再hook掉LoadLibraryA函數。讓第二次還能進來先調用我們的函數。之後就是返回值給上層主調函數了!
上面的2問題也就解開了。那就是因爲 __Inline_Hook_Func一開始就給我們unhook了。 myLoadLibrary函數使用的一直都是正常的 LoadLibraryA 函數!


其他:
這裏只是講了原理,練下手可以。呵呵!一般稍微有水平一點的程序是檢測了這個 LoadLibraryA函數是否被修改的。技術這東西就是你有招我也有招!呵呵!


好了。這篇文章終於寫完了,累!有什麼不對的地方還望各位批評!在此感謝。。。

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/masefee/archive/2009/09/18/4566121.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章