一、什么是HOOK(鉤子)
對于Windows系統(tǒng),它是建立在事件驅(qū)動機(jī)制上的,說白了就是整個系統(tǒng)都是通過消息傳遞實(shí)現(xiàn)的。hook(鉤子)是一種特殊的消息處理機(jī)制,它可以監(jiān)視系統(tǒng)或者進(jìn)程中的各種事件消息,截獲發(fā)往目標(biāo)窗口的消息并進(jìn)行處理。所以說,我們可以在系統(tǒng)中自定義鉤子,用來監(jiān)視系統(tǒng)中特定事件的發(fā)生,完成特定功能,如屏幕取詞,監(jiān)視日志,截獲鍵盤、鼠標(biāo)輸入等等。
鉤子的種類很多,每種鉤子可以截獲相應(yīng)的消息,如鍵盤鉤子可以截獲鍵盤消息,外殼鉤子可以截取、啟動和關(guān)閉應(yīng)用程序的消息等。鉤子可以分為線程鉤子和系統(tǒng)鉤子,線程鉤子可以監(jiān)視指定線程的事件消息,系統(tǒng)鉤子監(jiān)視系統(tǒng)中的所有線程的事件消息。因?yàn)橄到y(tǒng)鉤子會影響系統(tǒng)中所有的應(yīng)用程序,所以鉤子函數(shù)必須放在獨(dú)立的動態(tài)鏈接庫(DLL) 中。
所以說,hook(鉤子)就是一個Windows消息的攔截機(jī)制,可以攔截單個進(jìn)程的消息(線程鉤子),也可以攔截所有進(jìn)程的消息(系統(tǒng)鉤子),也可以對攔截的消息進(jìn)行自定義的處理。Windows消息帶了一些程序有用的信息,比如Mouse類信息,就帶有鼠標(biāo)所在窗體句柄、鼠標(biāo)位置等信息,攔截了這些消息,就可以做出例如金山詞霸一類的屏幕取詞功能。
二、Hook 分類
(1) 線程鉤子監(jiān)視指定線程的事件消息。
(2) 系統(tǒng)鉤子監(jiān)視系統(tǒng)中的所有線程的事件消息。因?yàn)橄到y(tǒng)鉤子會影響系統(tǒng)中所有的應(yīng)用程序,所以鉤子函數(shù)必須放在獨(dú)立的動態(tài)鏈接庫(DLL)中。這是系統(tǒng)鉤子和線程鉤子很大的不同之處。
三、HOOK(鉤子)的工作原理
在正確使用鉤子函數(shù)前,我們先講解鉤子函數(shù)的工作原理。當(dāng)您創(chuàng)建一個鉤子時(shí),WINDOWS會先在內(nèi)存中創(chuàng)建一個數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)包含了鉤子的相關(guān)信息,然后把該結(jié)構(gòu)體加到已經(jīng)存在的鉤子鏈表中去。新的鉤子將加到老的前面。當(dāng)一個事件發(fā)生時(shí),如果您安裝的是一個線程鉤子,您進(jìn)程中的鉤子函數(shù)將被調(diào)用。如果是一個系統(tǒng)鉤子,系統(tǒng)就必須把鉤子函數(shù)插入到其它進(jìn)程的地址空間,要做到這一點(diǎn)要求鉤子函數(shù)必須在一個動態(tài)鏈接庫中,所以如果您想要使用系統(tǒng)鉤子,就必須把該鉤子函數(shù)放到動態(tài)鏈接庫中去。
當(dāng)然有兩個例外:工作日志鉤子和工作日志回放鉤子。這兩個鉤子的鉤子函數(shù)必須在安裝鉤子的線程中。原因是:這兩個鉤子是用來監(jiān)控比較底層的硬件事件的,既然是記錄和回放,所有的事件就當(dāng)然都是有先后次序的。所以如果把回調(diào)函數(shù)放在DLL中,輸入的事件被放在幾個線程中記錄,所以我們無法保證得到正確的次序。故解決的辦法是:把鉤子函數(shù)放到單個的線程中,譬如安裝鉤子的線程。
幾點(diǎn)需要說明的地方:
(1) 如果對于同一事件(如鼠標(biāo)消息)既安裝了線程鉤子又安裝了系統(tǒng)鉤子,那么系統(tǒng)會自動先調(diào)用線程鉤子,然后調(diào)用系統(tǒng)鉤子。
(2) 對同一事件消息可安裝多個鉤子處理過程,這些鉤子處理過程形成了鉤子鏈。當(dāng)前鉤子處理結(jié)束后應(yīng)把鉤子信息傳遞給下一個鉤子函數(shù)。而且最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最后,也就是后加入的先獲得控制權(quán)。
?。?) 鉤子特別是系統(tǒng)鉤子會消耗消息處理時(shí)間,降低系統(tǒng)性能。只有在必要的時(shí)候才安裝鉤子,在使用完畢后要及時(shí)卸載。