鈎子(hook)


鈎子(hook)編程

鈎子(hook)編程  

 

 

一、鈎子介紹

     1.1鈎子的實現機制

鈎子英文名叫Hook,是一種截獲windows系統中某應用程序或者所有進程的消息的一種技術。下圖是windows應用程序傳遞消息的過程:

如在鍵盤中按下一鍵,操作系統將收到鍵按下消息,把消息放入消息隊列,然后消息隊列對消息進行派發,發給相應的應用程序,經過應用程序處理后發給操作系統,操作系統再調用相應的應用程序的創建的窗口過程。

        我們可能通過鈎子截獲這些消息,讓消息不再往下傳遞,或者說截獲到感興趣的消息后做點什么。

1.2鈎子分類與實現

        鈎子分進程內鈎子與全局鈎子,進程內鈎子是截取某一指定的進程的消息,直接在進程內創建與消除鈎子即可。全局鈎子是截取所有進程的消息,得以動態庫的方式實現。

        實現一個鈎子一般有三個步驟,首先創建鈎子,有專門的API:SetWindowsHookEx,創建成功后,消息將會傳給SetWindowsHookEx的形參指定的處理函數。然后在消息處理函數中分析收到的消息,做相應的處理。最后,鈎子用完后,用API(UnhookWindowsHookEx)消毀鈎子。

 

二、創建鈎子

2.1鈎子的創建

            SetWindowsHookEx安裝一個應用程序定義的鈎子過程,並把創建的鈎子過程放在鈎子鏈中,可以安裝多個鈎子,多個鈎子就形成了鈎子鏈,最后安裝的鈎子總是在最前面。創建鈎子的函數如下:

創建鈎子,返回鈎子句柄,否則返回NULL。形參定義如下:

idHook:鈎子過程類型,如:鼠標消息鈎子、鍵盤消息鈎子、消息隊列監控鈎子等等。具體取值如下:

lpfn:相應的鈎子過程,也就是一個處理消息的回調函數名而已,如果參數dwThreadId為0,或者dwThreadId指向的是其他進程創建的線程標志符,那么lpfn必須指向一個位於某一動態庫中的鈎子過程。其他情況下,lpfn可以指向本進程內的某一鈎子過程。

hMod:指向鈎子過程所在的應用程序實例句柄,如:鈎子過程所在的DLL的句柄。如果dwThreadId指定的線程是由當前進程創建,並且鈎子過程在當時進程中,那么hMod必須設置為NULL。

dwThreadId:指定與鈎子相關的線程標志。如果為0,那么鈎子將與桌面上運行的所有線程相關。

        鈎子過程可以與特定線程相關也可以與所有線程相關,取決於dwThreadId的取值。

2.2鈎子過程分析

        鈎子過程,是處理鈎子截取的消息的一個回調函數,即SetWindowsHookEx函數中的第二個形參。格式如下:

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);形參含義並不是都一樣的,不同鈎子過程形參表示的意義不一樣,我們以鼠標鈎子為例說明,參數含義如下:

nCode:指示鈎子過程如何處理當前的消息,如果鈎子是鼠標消息鈎子的話,有兩種含義:

 

wParam:指示鼠標消息標志。

lParam:指向MOUSEHOOKSTRUCT結構體指針。以下是MOUSEHOOKSTRUCT結構體,包含着鼠標消息。

typedef struct {

   POINT pt;//光標包含x,y,是屏幕坐標。

   HWND hwnd;//目標窗口句柄

   UINT wHitTestCode;

   ULONG_PTR dwExtraInfo;

} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;

通過這三個參數,可對消息進行分析處理。

        其他鈎子過程參數的含義可以通過MSDN文檔查看詳細說明,如KeyboardProc、MessageProc等。

2.3消毀鈎子

        BOOL UnhookWindowsHookEx(HHOOK hhk);此API的功能是把SetWindowsHookEx創建的鈎子從鈎子鏈中移除。形參是SetWindowsHookEx返回的鈎子句柄。

 

三、進程內鈎子

        進程內鈎子一般是為了截獲當前應用程序的消息,一般會可以在應用程序初始化的時候創建,當然也可以在自己想創建的時候創建,只是創建之前的消息無法截獲。

3.1鼠標鈎子過程函數

以下為鼠標鈎子過程代碼:

HWND            g_DestWndH = NULL;

HHOOK          g_hHookDm = NULL;

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)

{            //此區間內的消息都是鼠標消息

             if (WM_MOUSEMOVE <= wParam && wParam <= WM_MOUSEWHEEL) {

                           if(g_DestWndH != NULL) {

                                         ::SendMessage(g_DestWndH, wParam, wParam, lParam);

                                         //鼠標鈎子,lParam是MOUSEHOOKSTRUCT結構指針

                                         PMOUSEHOOKSTRUCT lpMsg = (PMOUSEHOOKSTRUCT)lParam;

                                         lpMsg->hwnd = NULL;    //把目的窗口置NULL

                                         lpMsg->dwExtraInfo = 0L;

                                         lpMsg->pt = CPoint(0, 0);

                                         lpMsg->wHitTestCode = 0L;

                           }

                           return 1;//如果想讓此消息不再往下傳,返回非0

             }

             else//不感興趣的消息往下傳

                           return ::CallNextHookEx(g_hHookDm, nCode, wParam, lParam);

}

3.2創建鈎子過程

        以下為創建鈎子過程的代碼:

if(g_hHookDm == NULL) {

             //如果dwThreadId指定的線程是由當前進程創建,並且鈎子過程在當時進程中,那么hMod必須設置為NULL。

             g_hHookDm = ::SetWindowsHookEx(WH_MOUSE,MouseProc, NULL, GetCurrentThreadId());

             if (NULL != g_hHookDm)//創建成功

                           g_DestWndH = m_hWnd;//保存目的窗口句柄

}

3.3消毀進程內鈎子

        把鈎子過程從鈎子鏈中拿下來,調用一個API即可:

if (NULL != g_hHookDm) {

             UnhookWindowsHookEx(g_hHookDm);

             g_DestWndH = NULL;

}

到此進程內鈎子的創建與執行到消毀整個過程都完成了。

四、全局鈎子

4.1鍵盤鈎子過程函數

以上進程內鈎子只是截獲本進程的消息,如果要截獲桌面全部線程的消息則要通過全局鈎子。全局鈎子必須在DLL上實現,鈎子過程不能在本進程代碼中實現,所以先得寫一個DLL,代碼見”HookDll”,以鍵盤鈎子為例:

nCode:與鼠標鈎子是一樣的含義,有HC_ACTION與HC_NOREMOVE兩個值。

Param:代表具體的虛擬鍵,如:VK_TAB、VK_RETURN分別代表Tab鍵、Enter鍵按下,其他虛擬鍵消息可查看MSDN的”virtual-key code”.

lParam:存放一些擴展信息,如:按鍵重復數據、29位表示ALT鍵的按下情況。

參數的具體含義可查MSDN。關於組合鍵的問題,參看以下鍵盤鈎子函數的示例代碼:

HHOOK g_HookKeyBoard = NULL;

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)

{

             //wParam表示虛擬鍵,lparam第29位表示ALT鍵按下狀態(按下為1,否則為0),

             //GetKeyState可以獲得對應鍵的狀態,所以收下表示Control + ALT + Z組合鍵按下

             if ('Z' == wParam && GetKeyState(VK_CONTROL) < 0 && (lParam >> 29 & 1)) {

                           ::SendMessage(g_DestWnd, WM_KEYDOWN, wParam, lParam);

                           return 1;

             }

             else

                           return CallNextHookEx(g_HookKeyBoard, nCode, wParam, lParam);

}

注:全局鈎子在某版本的系統中,調試狀態下,如果調試程序窗口沒有獲得焦點,在設置斷點的方不會停留,如果獲得焦點才會停留,也就是說,在調試狀態下,不屬於本進程的消息斷點處不會停留。

4.2創建全局鈎子接口

        創建全局鈎子的方法在DLL中,所以得引出一個接口,代碼如下:

BOOL SetHook(HWND hWnd){

             if (NULL == hWnd)

                           return FALSE;

             g_DestWnd = hWnd;

             //第三個參數可通過些方法獲得:GetModuleHandle("MouseHook.dll")

             return (NULL != (g_HookKeyBoard = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInst, 0)));

}

其形參是一窗口句柄,指示截獲的興趣消息需傳向的窗口的句柄,其中MetWindowsHookE的第三個參數是DLL的應用程序實例句柄,可通過DllMain的傳入參數得到,亦可通過GetModuleHandle方法得到,第四個參數必須為0,才能獲得全部桌面線程的消息。成功將返回TRUE否則FALSE。

4.3消毀全局鈎子接口

        在DLL中也得引出一接口消毀全局鈎子,代碼如下:

void DestroyHook(){

             if (NULL != g_HookKeyBoard) {

                           UnhookWindowsHookEx(g_HookKeyBoard);

                           g_DestWnd = NULL;

             }

}

4.4使用DLL創建全局鈎子

        以下為創建與消毀全局鈎子的實現,代碼如下:

HINSTANCE hIst = NULL;

void CHookTestDlg::OnCreateDllHook()

{

             hIst = ::LoadLibrary("..\\HookDll\\Debug\\HookDll.dll");

             if (NULL != hIst) {

                           typedef BOOL (*pFunSetHook)(HWND);

                           pFunSetHook pSetHook = (pFunSetHook)GetProcAddress(hIst, "SetHook");

                           if (NULL != pSetHook) {

                                         if(pSetHook(m_hWnd))

                                                       AfxMessageBox("創建全局鈎子成功...");

                           }

             }

}

void CHookTestDlg::OnDestroyDllHook()

{

             if (NULL != hIst) {

                           typedef void (*pFunDestroyHook)();

                           pFunDestroyHook pDestryHook = (pFunDestroyHook)GetProcAddress(hIst, "DestroyHook");

                           if (NULL != pDestryHook) {

                                         pDestryHook();

                                         ::FreeLibrary(hIst);

                                         hIst = NULL;

                           }

             }

}

 

配套源碼鏈接:http://download.csdn.net/detail/mingojiang/4526390

 

轉載請注明出處:http://blog.csdn.net/mingojiang 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM