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