鉤子(hook)編程

 

鉤子(hook)編程  

 

一、鉤子介紹

     1.1鉤子的實現機制

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

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

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

1.2鉤子分類與實現

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

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

 

二、創建鉤子

2.1鉤子的創建

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

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

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

lpfn:相應的鉤子過程,也就是一個處理消息的回調函數名而已,如果參數dwThreadId0,或者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文檔查看詳細說明,如KeyboardProcMessageProc等。

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);

                                         //鼠標鉤子,lParamMOUSEHOOKSTRUCT結構指針

                                         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_ACTIONHC_NOREMOVE兩個值。

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

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

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

HHOOK g_HookKeyBoard = NULL;

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

{

             //wParam表示虛擬鍵,lparam29位表示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 

 

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