"鉤子"敲門磚

對鉤子的理解

說起鉤子,很多人都想起它的形狀,根據形狀去想象windows的鉤子,會陷入誤區。Windows的鉤子,是沒有形狀的,之所以叫鉤子,並不是說它的形狀象鉤子,而是它的作用和鉤子一樣:就是用來“掛靠”的。比如有個包工頭想去接個項目,但是他沒有公司,拿不出相關的證件,怎麼辦呢?那就得找個公司掛靠,成立個子公司(subclass技術)之類,讓那個公司幫他出資源,出相關的證件之類......windows的鉤子和這很相似。比如我們常常想訪問另一個進程的一些資源(常見的獲取編輯框的密碼),怎麼辦呢?用鉤子來做掛靠,成爲另一個進程的一個分子,那樣你就可以在一定規則下(掛靠的當然都會有限制了)訪問、控制該進程的資源了。鉤子不但可以掛靠別的進程,本進程也可以用鉤子去“掛靠”和控制。

 

有關鉤子的API

(有關API的更詳細和權威的介紹,請參考MSDN

1、安裝鉤子的APISetWindowsHookEx(),要使用鉤子,首先得安裝鉤子。任何一個鉤子都由系統來維護一個指針列表(鉤子鏈表),其指針指向鉤子的各個處理函數。最近安裝的鉤子放在鏈的開始,最早安裝的鉤子則放在最後,當鉤子監視的消息出現時,操作系統調用鏈表開始處的第一個鉤子處理函數進行處理,也就是說最後加入的鉤子優先獲得控制權。呵呵,最後這句話就是提示我們怎麼反鉤子的。

 

HHOOK SetWindowsHookEx(int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId)

 

其中:參數idHook 指定了鉤子的類型,總共有如下13種:

WH_CALLWNDPROC 系統將消息發送到指定窗口之前的"鉤子"

WH_CALLWNDPROCRET 消息已經在窗口中處理的"鉤子"

WH_CBT 基於計算機培訓的"鉤子"

WH_DEBUG 差錯"鉤子"

WH_FOREGROUNDIDLE 前臺空閒窗口"鉤子"

WH_GETMESSAGE 接收消息投遞的"鉤子"

WH_JOURNALPLAYBACK 回放以前通過WH_JOURNALRECORD"鉤子"記錄的輸入消息

WH_JOURNALRECORD 輸入消息記錄"鉤子"

WH_KEYBOARD 鍵盤消息"鉤子"

WH_MOUSE 鼠標消息"鉤子"

WH_MSGFILTER 對話框、消息框、菜單或滾動條輸入消息"鉤子"

WH_SHELL 外殼"鉤子"

WH_SYSMSGFILTER 系統消息"鉤子"

       這樣,你就可以用各種各樣的鉤子去做掛靠訪問了,比如你可以用魚勾去釣魚,用衣服鉤子去鉤衣服,但不要用錯……

參數lpfn爲指向鉤子處理函數的指針,即回調函數的首地址;參數hMod則標識了鉤子處理函數所處模塊的句柄;第四個參數dwThreadId 指定被監視的線程,如果明確指定了某個線程的ID就只監視該線程,此時的鉤子即爲線程鉤子;如果該參數被設置爲0,則表示此鉤子爲監視系統所有線程的全局鉤子。此函數在執行完後將返回一個鉤子句柄。

對於全局鉤子,要使用動態連接庫來實現,因爲你的鉤子需要被其他進程和系統調用,如果放在你的進程中,估計別的進程也得產生個鉤子來訪問你的鉤子了J,對於一些線程鉤子,雖然不是全局鉤子,建議也使用動態連接庫。

2、傳遞鉤子的APICallNextHookEx( )。在安裝鉤子以後,鉤子會對相關的資源進行訪問控制,如果希望自己處理完相應的事物後,將系統交還給系統和原來的進程,就需要調用CallNextHookEx( )。一些反鉤子的代碼常常不調用該函數,這樣外掛程序安裝的鉤子就不會被運行。

 

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

 

其中,參數hhk爲由SetWindowsHookEx()函數返回的當前鉤子句柄;參數nCode爲傳給鉤子過程的事件代碼;參數wParamlParam 則爲傳給鉤子處理函數的參數值,其具體含義同設置的鉤子類型有關。

3、卸載鉤子的APISetWindowsHookEx()。由於鉤子對系統的性能有影響,在安裝使用完鉤子以後,就需要把鉤子卸載。

 

BOOL UnhookWindowsHookEx(HHOOK hhk);

 

其中hhk是使用SetWindowsHookEx()安裝鉤子時產生的返回值。

 

注意:windows的鉤子爲了提高系統的性能,採用了“copy-on-write”的技術,在SetWindowsHookEx()調用後並不馬上安裝鉤子。比如在設置WH_CALLWNDPROC鉤子時,鉤子並不馬上生效,而要在系統將消息發送到指定窗口的時候纔會正式安裝鉤子,纔會把鉤子掛靠在進程內(實際上不只是掛靠鉤子過程,還會把整個dll影射到進程內),才能訪問進程的資源,否則是個假鉤子,徒有虛名而已。

 

鉤子的簡單應用

這個例子不是我寫的,在網上找的,是個鍵盤鉤子。通過這個例子可以對鉤子和鉤子的有關API有比較深刻的認識。原來的連接在http://www.pconline.com.cn/pcedu/empolder/gj/vc/0403/340480.html可以找到,但是代碼編譯有問題。我作了些修改。

附代碼如下:

 

// test.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

 

DWORD   g_main_tid  = 0;

HHOOK   g_kb_hook   = 0;

#define _WIN32_WINNT    0x0400

 

BOOL CALLBACK con_handler (DWORD)

{

 

     PostThreadMessage (g_main_tid, WM_QUIT, 0, 0);

     return TRUE;

 

};

//--add by pdg

#define WH_KEYBOARD_LL     13

typedef struct tagKBDLLHOOKSTRUCT {

     DWORD   vkCode;

     DWORD   scanCode;

     DWORD   flags;

     DWORD   time;

     ULONG_PTR dwExtraInfo;

} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

//--add by pdg,由於編譯時認不到這兩個定義,因此此次重新做了定義

LRESULT CALLBACK kb_proc (int code, WPARAM w, LPARAM l)

{

     PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;

     const char *info = NULL;

     if (w == WM_KEYDOWN)

         info = "key dn";

     else if (w == WM_KEYUP)

         info = "key up";

     else if (w == WM_SYSKEYDOWN)

         info = "sys key dn";

     else if (w == WM_SYSKEYUP)

         info = "sys key up";

 

     printf ("%s - vkCode [%04x], scanCode [%04x]/n",

         info, p->vkCode, p->scanCode);

 

     //  always call next hook

     return CallNextHookEx (g_kb_hook, code, w, l);

};

 

 

int _tmain(int argc, _TCHAR* argv[])

{

     g_main_tid = GetCurrentThreadId ();

     SetConsoleCtrlHandler (&con_handler, TRUE);

     g_kb_hook = SetWindowsHookEx (

         WH_KEYBOARD_LL,

         &kb_proc,

         GetModuleHandle (NULL), // 不能爲NULL,否則失敗

         0);

 

     if (g_kb_hook == NULL)

     {

         fprintf (stderr,

              "SetWindowsHookEx failed with error %d/n",

              ::GetLastError ());

         return 0;

     };

     //  消息循環是必須的,想知道原因可以查msdn

     MSG msg;

 

     while (GetMessage (&msg, NULL, 0, 0))

     {

         TranslateMessage (&msg);

         DispatchMessage (&msg);

     };

     UnhookWindowsHookEx (g_kb_hook);

     return 0;

}

 

 

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