HOOK技術(鉤子)

一、技術簡介
鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達後,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。
二、原理介紹
簡單形象的說,windows一直都是有自己處理各種消息的函數,Hook其實就能夠做到程序員自己處理自己感興趣的事情。這樣說,假設Windows的消息就是馬路上的車輛,一般情況下是Windows自己派人在檢查,然後呢,Hook是擁有這個能力能在Windows自己安排的檢查站之前也進行抽查,Hook根據程序員的需求可以變化,比如我就感興趣100萬以上的車(可能是走私的(^__^)),Hook就能在檢查的時候專門找100萬以上的車,至於其他不上檔次的車Hook就放行,交還給Windows自己的檢查站。同樣Hook可以“爲所欲爲”,可以擅自設立一個檢查站,也可以兩個,三個···換成程序來說,鉤子函數的工作原理是:當我們創建一個鉤子時,WINDOWS會先在內存中創建一個數據結構,該數據結構包含了鉤子的相關信息,然後把該結構體加到已經存在的鉤子鏈表中去。新的鉤子將加到老的前面。當一個事件發生時,如果我們安裝的是一個局部鉤子(下面有解釋,暫時理解爲你程序本身中的),我們進程中的鉤子函數將被調用。
三、HOOK技術的使用
在使用HOOK(鉤子)技術時可以分爲以下三步:
(1)定義鉤子函數;
(2)安裝鉤子;
(3)卸載鉤子。
<1> 定義鉤子函數
每一個Hook都有一個與之相關聯的指針列表,稱之爲鉤子鏈表,由系統來維護。被Hook子程調用的回調函數,也就是該鉤子的各個處理子程。當與指定的Hook類型關聯的消息發生時,系統就把這個消息傳遞到Hook子程。一些Hook子程可以只監視消息,或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個Hook子程或者目的窗口。最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最後,也就是後加入的先獲得控制權。鉤子子程是一個應用程序定義的回調函數(CALLBACKFunction),不能定義成某個類的成員函數,只能定義爲普通的C函數。用以監視系統或某一特定類型的事件,這些事件可以是與某一特定線程關聯的,也可以是系統中所有線程的事件。鉤子子程必須按照以下的語法:

LRESULT CALLBACK HookProc
(
  int nCode, //指定是否需要處理該消息 
WPARAM wParam, 
LPARAM lParam  //包含該消息的附加消息 ,
);

注:這個回調函數的名字可以隨你取,但形式可一定要滿足以上要求,其實鉤子的回調函數和Windows的差不多一樣的過程。看看鉤子函數的返回值,若是返回非0值,表示我們已經自己處理了該消息,則消息就不被傳遞到目標窗口過程。
拓展:如何把鉤子消息傳遞給下一個鉤子

LRESULT CallNextHookEx
( HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;)

這個函數把鉤子信息傳遞給下一個鉤子函數,也就是可以理解成把車輛放行到下一個檢查站,這個可以根據自己的需要進行調用。若是我們只設定了一個鉤子函數,那麼我們假設把鉤子消息用CallNextHookEx傳給下個鉤子函數,因爲不存在所以就傳遞迴了目標窗口函數。
<2> 安裝鉤子
調用SetWindowHookEx函數,該函數的原型如下:

HHOOK SetWindowsHookEx( int idHook,
                        HOOKPROC lpfn,
                        HINSTANCE hMod,
                        DWORD dwThreadId );

返回值是一個hook的句柄。
第一個參數idHook是我們感興趣的消息類型,比如我們對鼠標消息感興趣就是WH_MOUSE,再者比如鍵盤消息WH_KEYBOARD,我們可以通過查找Win32 API使用手冊來找到自己感興趣的消息。
第二個參數是鉤子函數的地址,這裏就有兩種情況:其實鉤子有兩種,一種是局部鉤子,這種鉤子只能關注自己所在的進程的事件,另一種鉤子叫做遠程鉤子,這裏又有兩種:1.基於線程的它將捕獲其它進程中某一特定線程的事件。簡言之,就是可以用來觀察其它進程中的某一特定線程將發生的事件。2.系統範圍的 將捕捉系統中所有進程將發生的事件消息。 看上去局部鉤子的功能沒有遠程鉤子的給力,但是凡事都是要付出代價的,遠程鉤子會影響系統的性能,特別是監視系統範圍的鉤子,因爲要監視系統範圍的消息,明顯就會影響系統的速度。
第三個參數和第四個參數相關,所以一起解釋。
如果第四個參數是NULL,則說明是全局鉤子,那麼就是鉤子子程與所有的線程關聯,此時第三個參數是程序實例句柄;
如果第三個參數是NULL,則說明鉤子是局部鉤子,說明子程代碼位於當前進程,這時候第四個參數就是當前進程的ID,可以用GetCurrentThreadID()填充,或者可以保存實例來填充,再做介紹···
<3> 卸載鉤子
鉤子函數的卸載,用UnHookWindowsHookEx(HHOOK hhk);參數就是SetWindowsHookEx返回的句柄。
四、實例程序
大概的功能就是在目標窗口截獲了所有鼠標的消息,這裏實現的是屏蔽鼠標消息,只能通過按回車鍵恢復鼠標功能。若是點擊了“LockKeyBoard”按鈕,那麼只能在編輯框輸入0或者1,但是在沒有點擊的情況下是正常的編輯框。這點可是比窗口子類化更加簡單。

#include "Windows.h"  
#include "tchar.h"  
#include "resource.h"  


HINSTANCE g_hInstance ;  
static HHOOK hHook = NULL;  


INT_PTR CALLBACK ProcWinMain(HWND hWnd, UINT Msg, WPARAM wParam ,LPARAM lParam);  
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam,LPARAM lParam);       
LRESULT CALLBACK BoardProc(int nCode, WPARAM wParam,LPARAM lParam);       


int WINAPI WinMain(     HINSTANCE hInstance,  
                   HINSTANCE hPrevInstance,  
                   LPSTR lpCmdLine,  
                   int nCmdShow  
                   )  
{  
    static TCHAR DlgName[] = _T("InnerHook");  
    g_hInstance = hInstance ;  
    DialogBoxParam(hInstance,DlgName,NULL,( DLGPROC)ProcWinMain,NULL);  
    return 0;  
}  


INT_PTR CALLBACK ProcWinMain(   HWND hWnd,   
                             UINT Msg,   
                             WPARAM wParam,   
                             LPARAM lParam   
                             )  
{  
    static TCHAR unLockStr[] = _T("Unlocking");  
    static TCHAR LockStr[] = _T("Locking");  
    static BOOL isLock = FALSE;  
    static BOOL isLock2 = FALSE;  
    static HHOOK hHook2 = NULL;  
    switch(Msg)  
    {  
    case WM_INITDIALOG:  
        {  
            SetFocus(GetDlgItem(hWnd,IDC_EDITTEXT));  
        }  
        break;  


    case WM_CLOSE:  
        EndDialog(hWnd,NULL);  
        break;  
    case WM_COMMAND:  
        {  
            switch(LOWORD(wParam))  
            {  
                case ID_BTNHOOKBOARD :  
                {  
                    if(isLock == FALSE)  
                    {  
                        hHook = SetWindowsHookEx(WH_KEYBOARD,BoardProc,NULL,GetCurrentThreadId());  
                        SetDlgItemText(hWnd,ID_BTNHOOKBOARD,LockStr);  
                        isLock = TRUE;  
                    }  
                    else  
                    {  
                        SetDlgItemText(hWnd,ID_BTNHOOKBOARD,unLockStr);  
                        isLock = FALSE;   
                        UnhookWindowsHookEx(hHook);  
                    }  
                }  
                break;  
                case ID_BTNHOOKMOUSE :  
                {  
                    if(isLock2 == FALSE)  
                    {  
                        hHook2 = SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());  
                        SetDlgItemText(hWnd,ID_BTNHOOKMOUSE,LockStr);  
                        isLock2 = TRUE;  
                    }  
                    else  
                    {  
                        SetDlgItemText(hWnd,ID_BTNHOOKMOUSE,unLockStr);  
                        isLock2 = FALSE;      
                        UnhookWindowsHookEx(hHook2);  
                    }  
                }  
                break;  
            }  
        }  
        break;  
    default:  
        return FALSE;   
    }  
    return TRUE;  
}  


LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam,LPARAM lParam)  
{  
    return 1;           //返回非0值,表示處理了該消息  
}  


LRESULT CALLBACK BoardProc(int nCode, WPARAM wParam,LPARAM lParam)  
{  
    if (wParam == VK_RETURN || wParam == '1' || wParam == '0')  
    {  
        return CallNextHookEx(hHook,nCode,wParam,lParam);   //表示不處理該消息,交還給Windows自己處理  
    }  
    else  
        return 1;       //表示已經處理了該消息  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章