摘自網上供自己備查: http://www.csharpwin.com/csharpspace/2423.shtml
?
1、 WINDOWS的消息機制
2、 HOOK介紹
3、 HOOK鏈
4、 HOOK鉤子的作用范圍
5、 HOOK類型
6、 回調函數
7、 HOOK鉤子的安裝與卸載
8、 HOOK實例演示
?
+++++++++++++++++++
WINDOWS的消息機制
+++++++++++++++++++
Windows系統是以消息處理為其控制機制,系統通過消息為窗口過程(windows
procedure)傳遞輸入。系統和應用兩者都可以產生消息。對于每個輸入事件,例如用
戶按下了鍵盤上的某個鍵、移動了鼠標、單擊了一個控件上的滾動條,等等,系統都
將產生一系列消息。此外,對于應用帶給系統的變化,如字體資源的改變、應用本身
窗口的改變,系統都將通過消息以響應這種變化。應用通過產生消息指示應用的窗口
完成特定的任務,或與其他應用的窗口進行通信。
每個窗口都有一個處理Windows系統發送消息的處理程序,稱為窗口程序。它是
隱含在窗口背后的一段程序腳本,其中包含對事件進行處理的代碼。
Windows系統為每條消息指定了一個消息編號,例如當一個窗口變為活動窗口時,它事
實上是收到一條來自Windows系統的WM_ACTIVATE消息,該消息的編號為6,它對應于窗
口的Activate事件。對于窗口來說,諸如Load,MouseDown等事件,實際上對應的是窗口
內部的消息處理程序,這些程序對于用戶來講是不可見的。
類似地,命令按鈕也有消息處理程序,它的處理程序響應諸如WM_LBUTTONDOWN
和WM_RBUTTONDOWN之類的消息,即激活命令按鈕的MouseDown事件。
WINDOWS的消息處理機制為了能在應用程序中監控系統的各種事件消息,提供
了掛接各種回調函數(HOOK)的功能。這種掛鉤函數(HOOK)類似擴充中斷驅動程序,
掛鉤上 可以掛接多個反調函數構成一個掛接函數鏈。系統產生的各種消息首先被送
到各種掛接函數,掛接函數根據各自的功能對消息進行監視、修改和控制等,然后交
還控 制權或將消息傳遞給下一個掛接函數以致最終達到窗口函數。WINDOW系統的
這種反調函數掛接方法雖然會略加影響到系統的運行效率,但在很多場合下是非常有
用的,通過合理有效地利用鍵盤事件的掛鉤函數監控機制可以達到預想不到的良好效
果。
+++++++++++
hook介紹
+++++++++++
Hook(鉤子)是WINDOWS提供的一種消息處理機制平臺,是指在程序正常運
行中接受信息之前預先啟動的函數,用來檢查和修改傳給該程序的信息,(鉤子)實
際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,
在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這
時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還
可以強制結束消息的傳遞。
注意:安裝鉤子函數將會影響系統的性能。監測“系統范圍事件”的系統鉤子特
別明顯。因為系統在處理所有的相關事件時都將調用您的鉤子函數,這樣您的系統將
會明顯的減慢。所以應謹慎使用,用完后立即卸載。還有,由于您可以預先截獲其它
進程的消息,所以一旦您的鉤子函數出了問題的話必將影響其它的進程。記住:功能
強大也意味著使用時要負責任。
+++++++++++++
HOOK鏈
+++++++++++++
WINDOWS提供了14種不同類型的HOOKS;不同的HOOK可以處理不同的消
息。例如,WH_MOUSE HOOK用來監視鼠標消息。
WINDOWS為這幾種HOOKS維護著各自的HOOK鏈表。HOOK鏈表是一串由
應用程序定義的回調函數(CALLBACK Function)隊列,當某種類型的消息發生時,
WINDOWS向此種類型的HOOK鏈的第一個函數(HOOK鏈的頂部)發送該消息,
在第一函數處理完該消息后由該函數向鏈表中的下一個函數傳遞消息,依次向下。如
果鏈中某個函數沒有向下傳送該消息,那么鏈表中后面的函數將得不到此消息。(對
于某些類型的HOOK,不管HOOK鏈中的函數是否向下傳遞消息,與此類型HOOK
聯系的所有HOOK函數都會收到系統發送的消息)一些Hook子過程可以只監視消息,
或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個Hook子過程或者
目的窗口。最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最后,也就是后加
入的先獲得控制權。
?
+++++++++++++++++
鉤子的作用范圍
++++++++++++++++
一共有兩種范圍(類型)的鉤子:局部的和遠程的。
一、局部鉤子僅鉤掛您自己進程的事件。
二、遠程的鉤子還可以將鉤掛其它進程發生的事件。
遠程的鉤子又有兩種:
1、基于線程的 它將捕獲其它進程中某一特定線程的事件。簡言之,就是可
以用來觀察其它進程中的某一特定線程將發生的事件。
2、系統范圍的 將捕捉系統中所有進程將發生的事件消息。
+++++++++++++
HOOK類型
+++++++++++++
????? Windows共有14種HOOKS,每一種類型的Hook可以使應用程序能夠監視不同
類型的系統消息處理機制。下面描述所有可以利用的Hook類型的發生時機。(這些常
數值均可以API瀏覽器里查到)
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks
????? WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以監視發送到
窗口過程的消息。系統在消息發送到接收窗口過程之前調用WH_CALLWNDPROC
Hook子過程,并且在窗口過程處理完消息之后調用WH_CALLWNDPROCRET Hook
子過程。
????? WH_CALLWNDPROCRET Hook傳遞指針到CWPRETSTRUCT結構,再傳遞到
Hook子過程。CWPRETSTRUCT結構包含了來自處理消息的窗口過程的返回值,同
樣也包括了與這個消息關聯的消息參數。
2、WH_CBT Hook
????? 在以下事件之前,系統都會調用WH_CBT Hook子過程,這些事件包括:
????? 1. 激活,建立,銷毀,最小化,最大化,移動,改變尺寸等窗口事件;
????? 2. 完成系統指令;
????? 3. 來自系統消息隊列中的移動鼠標,鍵盤事件;
????? 4. 設置輸入焦點事件;
????? 5. 同步系統消息隊列事件。
????
????? Hook子過程的返回值確定系統是否允許或者防止這些操作中的一個。
3、WH_DEBUG Hook
????? 在系統調用系統中與其它Hook關聯的Hook子過程之前,系統會調用
WH_DEBUG Hook子過程。你可以使用這個Hook來決定是否允許系統調用與其它
Hook關聯的Hook子過程。
4、WH_FOREGROUNDIDLE Hook
????? 當應用程序的前臺線程處于空閑狀態時,可以使用WH_FOREGROUNDIDLE
Hook執行低優先級的任務。當應用程序的前臺線程大概要變成空閑狀態時,系統就
會調用WH_FOREGROUNDIDLE Hook子過程。
5、WH_GETMESSAGE Hook
????? 應用程序使用WH_GETMESSAGE Hook來監視從GetMessage or PeekMessage函
數返回的消息。你可以使用WH_GETMESSAGE Hook去監視鼠標和鍵盤輸入,以及
其它發送到消息隊列中的消息。
6、WH_JOURNALPLAYBACK Hook
????? WH_JOURNALPLAYBACK Hook使應用程序可以插入消息到系統消息隊列。可
以使用這個Hook回放通過使用WH_JOURNALRECORD Hook記錄下來的連續的鼠
標和鍵盤事件。只要WH_JOURNALPLAYBACK Hook已經安裝,正常的鼠標和鍵盤
事件就是無效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象線程特定
Hook一樣使用。WH_JOURNALPLAYBACK Hook返回超時值,這個值告訴系統在處
理來自回放Hook當前消息之前需要等待多長時間(毫秒)。這就使Hook可以控制實
時事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被
注射到任何行程地址空間。
7、WH_JOURNALRECORD Hook
???? WH_JOURNALRECORD Hook用來監視和記錄輸入事件。典型的,可以使用這
個Hook記錄連續的鼠標和鍵盤事件,然后通過使用WH_JOURNALPLAYBACK Hook
來回放。WH_JOURNALRECORD Hook是全局Hook,它不能象線程特定Hook一樣
使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行
程地址空間。
8、WH_KEYBOARD Hook
????? 在應用程序中,WH_KEYBOARD Hook用來監視WM_KEYDOWN and
WM_KEYUP消息,這些消息通過GetMessage or PeekMessage function返回。可以使
用這個Hook來監視輸入到消息隊列中的鍵盤消息。
9、WH_KEYBOARD_LL Hook
????? WH_KEYBOARD_LL Hook監視輸入到線程消息隊列中的鍵盤消息。
10、WH_MOUSE Hook
????? WH_MOUSE Hook監視從GetMessage 或者 PeekMessage 函數返回的鼠標消息。
使用這個Hook監視輸入到消息隊列中的鼠標消息。
11、WH_MOUSE_LL Hook
????? WH_MOUSE_LL Hook監視輸入到線程消息隊列中的鼠標消息。
12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
????? WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以監視菜單,滾動
條,消息框,對話框消息并且發現用戶使用ALT+TAB or ALT+ESC 組合鍵切換窗口。
WH_MSGFILTER Hook只能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到通
過安裝了Hook子過程的應用程序建立的對話框的消息。WH_SYSMSGFILTER Hook
監視所有應用程序消息。
????
????? WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以在模式循環期間
過濾消息,這等價于在主消息循環中過濾消息。
????
通過調用CallMsgFilter function可以直接的調用WH_MSGFILTER Hook。通過使用這
個函數,應用程序能夠在模式循環期間使用相同的代碼去過濾消息,如同在主消息循
環里一樣。
13、WH_SHELL Hook
????? 外殼應用程序可以使用WH_SHELL Hook去接收重要的通知。當外殼應用程序是
激活的并且當頂層窗口建立或者銷毀時,系統調用WH_SHELL Hook子過程。
??? WH_SHELL 共有5鐘情況:
1. 只要有個top-level、unowned 窗口被產生、起作用、或是被摧毀;
2. 當Taskbar需要重畫某個按鈕;
3. 當系統需要顯示關于Taskbar的一個程序的最小化形式;
4. 當目前的鍵盤布局狀態改變;
5. 當使用者按Ctrl+Esc去執行Task Manager(或相同級別的程序)。
????? 按照慣例,外殼應用程序都不接收WH_SHELL消息。所以,在應用程序能夠接
收WH_SHELL消息之前,應用程序必須調用SystemParametersInfo function注冊它自
己。
++++++++++++++++++++++++++
回調函數(HOOK處理子過程)
++++++++++++++++++++++++++
為了攔截和處理特定的消息,你可以使用SetWindowsHookEx函數(下面將具體
說明這些函數的聲明及各種參數)在該類型的HOOK鏈中安裝你自己的處理HOOK
的子過程(回調函數)。只要您安裝的鉤子的消息事件類型發生,WINDOWS就將調
用鉤子函數。譬如您安裝的鉤子是WH_MOUSE類型,那么只要有一個鼠標事件發生
時,該鉤子函數就會被調用。不管您安裝的是那一類型鉤子,鉤子函數的原型都時是
一樣的,語法如下:
public int MyHook(int nCode, Int32 wParam, IntPtr lParam)
{
//處理代碼
}
其中MyHook可以隨便命名,其它不能變。該函數必須放在模塊段。
參數說明:
nCode指定HOOK傳入的信息類型。Hook子過程使用這個參數來確定任務。這個
參數的值依賴于Hook類型,每一種Hook都有自己的Hook代碼特征字符集。
wParam:短整型參數。
lParam:長整型參數。
wParam,iParam的取值隨nCode不同而不同,它代表了某種類型的HOOK的某個特
定的動作。它們的典型值是包含了關于發送或者接收消息的信息。
至于以上的幾個參數及返回值的具體含義,各種類型的鉤子都不相同,所以您必須查詢
WIN32 API 指南來得到不同類型鉤子參數的詳細定義以及它們返回值的意
義。
譬如:
WH_CALLWNDPROC
nCode 只能是HC_ACTION,它代表有一個消息發送給了一個窗口
wParam 如果非0,代表正被發送的消息
lParam 指向CWPSTRUCT型結構體變量的指針
return value: 未使用,返回0
WH_MOUSE
nCode 為HC_ACTION 或 HC_NOREMOVE
wParam 包含鼠標的事件消息
lParam 指向MOUSEHOOKSTRUCT型結構體變量的指針
return value: 如果不處理返回0,否則返回非0值
++++++++++++++++++++++++++
鉤子的安裝/卸載
++++++++++++++++++++++++++
現在我們知道了一些基本的理論,接下來開始講解如何安裝和卸載一個鉤子。
◆安裝鉤子
使用SetWindowsHookEx函數(API函數),指定一個HOOK類型、自己的HOOK
過程是全局還是局部HOOK,同時給出HOOK過程的進入點,就可以輕松的安裝你
自己的HOOK過程。
SetWindowsHookEx總是將你的HOOK函數放置在HOOK鏈的頂端。你可以使用
CallNextHookEx函數將系統消息傳遞給HOOK鏈中的下一個函數。
[注意]對于某些類型的HOOK,系統將向該類的所有HOOK函數發送消息,這時,
HOOK函數中的CallNextHookEx語句將被忽略。
全局(遠程鉤子)HOOK函數可以攔截系統中所有線程的某個特定的消息,為了
安裝一個全局HOOK過程,必須在應用程序外建立一個DLL,并將該HOOK函數封
裝到其中,應用程序在安裝全局HOOK過程時必須先得到該DLL模塊的句柄。將DLL
名傳遞給LoadLibrary 函數,就會得到該DLL模塊的句柄;得到該句柄 后,使用
GetProcAddress函數可以得到HOOK過程的地址。最后,使用SetWindowsHookEx將
HOOK過程的首址嵌入相應的HOOK鏈中,SetWindowsHookEx傳遞一個模塊句柄,
它為HOOK過程的進入點,線程標識符置為0,指出:該HOOK過程同系統中的所
有線程關聯。如果是安裝局部HOOK此時該HOOK函數可以放置在DLL中,也可以
放置在應用程序的模塊段。
建議只在調試時使用全局HOOK函數。全局HOOK函數將降低系統效率,并且
會同其它使用該類HOOK的應用程序產生沖突。
SetWindowsHookEx函數的C#聲明及參數解釋:
public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int SetWindowsHookEx(int idHook,HookProc lpfn,IntPtr hMod,int dwThreadId);
SetWindowHookEx函數參數說明:
idHook:代表是何種Hook(也就是上面講的14種Hook)
HookProc:代表處理Hook的過程的委托,這是一個CallBack Fucnction(也就是上
面講的回調函數),當掛上某個Hook時,我們便得定義一個Function來當作某個信
息產生時,來處理它的Functionhmod:代表.DLL的hInstance,如果是Local Hook,
該值可以是Null,而如果是Remote Hook,則可以使用System.Reflection.GetModule
(".dll名稱")來傳入。
dwThreadId:代表執行這個Hook的ThreadId(線程ID),如果是全局鉤子,
則傳0ThreadID是您安裝該鉤子函數后想監控的線程的ID號。該參數可以決定
該鉤子是局部的還 是系統范圍的。如果該值為NULL,那么該鉤子將被解釋成
系統范圍內的,那它就 可以監控所有的進程及它們的線程。如果您指定了您自己
進程中的某個線程ID 號, 那該鉤子是一個局部的鉤子。如果該線程ID是
另一個進程中某個線程的ID,那該 鉤子是一個全局的遠程鉤子。這里有
兩個特殊情況:WH_JOURNALRECORD 和 WH_JOURNALPLAYBACK總是代表局部
的系統范圍的鉤子,之所以說是局部,是 因為它們沒有必要放到一個DLL中。
WH_SYSMSGFILTER 總是一個系統范圍內 的遠程鉤子。其實它和WH_MSGFILTER鉤子
類似,如果把參數ThreadID設成0 的話,它們就完全一樣了。
SetWindowHookEx函數回值: 如果SetWindowsHookEx()成功,它會傳回一個值,
代表目前的Hook的Handle,否則返回NULL。您必須保存該句柄,因為后面我們
還要它來卸載鉤子。
CallNextHookEx函數的C#聲明及參數解釋:
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook,int nCode,int wParam,IntPtr lParam);
hHook值是SetWindowsHookEx()的傳回值,nCode, wParam, lParam則是回調函數
中的三個參數。
在鉤子子程中調用得到控制權的鉤子函數在完成對消息的處理后,如果想要該消
息繼續傳遞,那么它必須調用另外一個API函數CallNextHookEx來傳遞它,以執行
鉤子鏈表所指的下一個鉤子子過程。這個函數成功時返回鉤子鏈中下一個鉤子過程的
返回值,返回值的類型依賴于鉤子的類型。
?
◆ 卸載鉤子
要卸載一個鉤子非常簡單,只需要使用UnhookWindowsHookEx函數來卸載創建
的鉤子。
函數聲明:
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int UnhookWindowsHookEx(int idHook);
參數說明:
hHook:是SetWindowsHookEx()的傳回值。