1. 程式人生 > >hook函式教程(一)什麼是鉤子

hook函式教程(一)什麼是鉤子

一、什麼是鉤子  

我們可以首先從字面上瞭解鉤子,鉤子是幹什麼的呢?日常生活中,我們的鉤子是用來鉤住某種東西的,比如,說,魚鉤是用來釣魚的,一旦魚咬了鉤,鉤子就一直鉤住魚了,任憑魚在水裡怎麼遊,也逃不出魚鉤的控制。同樣的,Windows的鉤子Hook也是用來鉤東西的,比較抽象的是他是用來鉤Windows事件或者訊息的。最常見的就是滑鼠和鍵盤鉤子,用Hook鉤子鉤住滑鼠、鍵盤,當你的滑鼠、鍵盤有任何操作時,通過Hook就能知道他們都做了什麼了,多麼形象啊,把老鼠Mouse鉤住了,不管你幹什麼,都逃不過我鉤子Hook的手掌心。 技術上講,鉤子(Hook)是Windows訊息處理機制的一個很重要的內容,誰叫Windows是基於訊息的呢。應用程式可以通過鉤子機制截獲處理Window訊息或是其他一些特定事件。
我們可以在同一個鉤子上掛很多東西。 想起參加工作前要求被體檢的時候,當你被登記之後,按照你的登記表上的順序,就等著到各個科室一個一個的去檢查吧。每一個科室都有決定你是否繼續的可能,只有通過了這個,你才可以到下一個去,如果沒有通過,那麼,你是看不到最後的大夫了,可以直接over回家了。 如果把體檢比喻為事件的話,當事件發生時,應用程式(體檢過程)可以在相應的鉤子Hook上設定多個鉤子子程式(Hook Procedures)(多個科室的檢查),由其組成一個與鉤子相關聯的指向鉤子函式的指標列表(鉤子連結串列)(體檢表,確定了你要走的順序)。當鉤子所監視的訊息出現時(你拿著表格來體檢了),Windows(導診員)首先將其送到呼叫連結串列中所指向的第一個鉤子函式中(體檢表上第一個科室,一般是身高體重吧,呵呵),鉤子函式將根據其各自的功能(每個科室檢查的專案不一樣啊)對訊息進行監視(有的大夫就隨便看看了事),、修改(碰到好心的大夫還可以幫你往好裡添點呢,呵呵)和控制(有的大夫好嚴格啊),並在處理完成後(當然有的大夫就直接把你刷下了,回家吧,沒有下一個了)把訊息傳遞給下一鉤子函式(下一個專案的科室,當然,也可以強制訊息的傳遞,直接打發你回家)直至到達鉤子連結串列的末尾(檢查完了!)。在鉤子函式交出控制權後,被攔截的訊息最終仍將交還給視窗處理函式(好了,拿著表去上班吧)。
雖然鉤子函式對訊息的過濾將會略加影響系統的執行效率,但在很多場合下通過鉤子對訊息的過濾處理可以完成一些其他方法所不能完成的特殊功能。

二、比較專業的對鉤子的技術性理解 

     鉤子(Hook),是Windows訊息處理機制的一個平臺,應用程式可以在上面設定子程以監視指定視窗的某種訊息,而且所監視的視窗可以是其他程序所建立的。當訊息到達後,在目標視窗處理函式之前處理它。鉤子機制允許應用程式截獲處理window訊息或特定事件。 

      Windows系統是建立在事件驅動的機制上的,說穿了就是整個系統都是通過訊息的傳遞來實現的。而鉤子是Windows系統中非常重要的系統介面,用它可以截獲並處理送給其他應用程式的訊息,來完成普通應用程式難以實現的功能。鉤子可以監視系統或程序中的各種事件訊息,截獲發往目標視窗的訊息並進行處理。這樣,我們就可以在系統中安裝自定義的鉤子,監視系統中特定事件的發生,完成特定的功能,比如截獲鍵盤、滑鼠的輸入,螢幕取詞,日誌監視等等。可見,利用鉤子可以實現許多特殊而有用的功能。

      鉤子實際上是一個處理訊息的程式段,通過系統呼叫,把它掛入系統。每當特定的訊息發出,在沒有到達目的視窗前,鉤子程式就先捕獲該訊息,亦即鉤子函式先得到控制權。這時鉤子函式即可以加工處理(改變)該訊息,也可以不作處理而繼續傳遞該訊息,還可以強制結束訊息的傳遞。

      一個Hook都有一個與之相關聯的指標列表,稱之為鉤子連結串列,由系統來維護。這個列表的指標指向指定的,應用程式定義的,被Hook子程呼叫的回撥函式,也就是該鉤子的各個處理子程。當與指定的Hook型別關聯的訊息發生時,系統就把這個訊息傳遞到Hook子程。一些Hook子程可以只監視訊息,或者修改訊息,或者停止訊息的前進,避免這些訊息傳遞到下一個Hook子程或者目的視窗。最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最後,也就是後加入的先獲得控制權。 zdwork.cn

      Windows 並不要求鉤子子程的解除安裝順序一定得和安裝順序相反。每當有一個鉤子被解除安裝,Windows 便釋放其佔用的記憶體,並更新整個Hook連結串列。如果程式安裝了鉤子,但是在尚未解除安裝鉤子之前就結束了,那麼系統會自動為它做解除安裝鉤子的操作。

      大多數人或者網上文章認為全域性鉤子都要依賴於一個DLL才能正常工作的,常常會看到很多人在論壇上長期爭論一個話題:“全域性鉤子一定要在DLL裡面嗎?”。實際上這裡有一個概念的問題,究竟上面提到的全域性鉤子是指什麼。通過對上面各種鉤子的作用域的理解就會發現這個問題的答案。 本文來自智動軟體zdwork.cn

上面一共提到了15種鉤子,他們的作用域請看下錶: 

Hook 

Scope

WH_CALLWNDPROC

Thread or global

WH_CALLWNDPROCRET

Thread or global

WH_CBT

Thread or global

WH_DEBUG

Thread or global

WH_FOREGROUNDIDLE

Thread or global

WH_GETMESSAGE

Thread or global

WH_JOURNALPLAYBACK

Global only

WH_JOURNALRECORD

Global only 

WH_KEYBOARD

Thread or global

WH_KEYBOARD_LL 

Global only

WH_MOUSE

Thread or global

WH_MOUSE_LL

Global only

WH_MSGFILTER

Thread or global

WH_SHELL

Thread or global

WH_SYSMSGFILTER

Global only

表一:鉤子作用域

      WH_JOURNALPLAYBACK,WH_JOURNALRECORD,WH_KEYBOARD_LL,WH_MOUSE_LL、WH_SYSMSGFILTER這5種鉤子本身的作用域就是全域性的,不管鉤子是直接寫在應用程式的程式碼裡還是放在DLL中,他們都能夠鉤住系統的訊息。剩下的10種鉤子,他們的作用域既可以是執行緒的又可以是全域性的,當將相應的鉤子直接寫在應用程式的程式碼中時,他們只能捕獲當前執行緒上下文的訊息。那麼他們如何實現捕獲全域性訊息的功能呢?當把鉤子寫入到一個單獨的DLL中再引用後,系統自動將該DLL對映到受鉤子函式影響的所有程序的地址空間中,即將這個DLL注入了那些程序,從而達到捕獲全域性訊息的目的。相對來說,前面5種鉤子本身就是全域性的,是不需要注入的。 智動軟體

      因此,對於前面問題的答案就是:要實現捕獲全域性訊息功能的鉤子,是否要寫在單獨的DLL裡面,取決於鉤子的型別以及相應的作用域。

      系統必須要處理每個訊息,而鉤子的使用增加了系統對每個訊息要執行的處理的數量,因此,鉤子會減慢系統。應該僅僅在必須的時候才安裝鉤子,並且儘可能早的將其解除安裝掉。 

三、Hook Chain(鉤子連結串列) 

      系統支援很多不同種類的鉤子,每種型別提供對訊息處理機制裡的某一不同方面的訪問。例如,應用程式可以使用WH_MOUSE Hook監視滑鼠訊息的傳遞。

      系統為每類鉤子維護著一個獨立的鉤子連結串列。鉤子連結串列是一個指標的列表,其中的指標指向特定的、應用程式定義的回撥函式,該函式被叫做鉤子子程(hook procedure)。當與某種特定型別的鉤子相關聯(鉤住)的訊息發生時,系統將訊息一個接一個地傳遞給鉤子鏈中的每一個鉤子子程(hook procedure),鉤子子程能夠採取的動作取決於涉及的鉤子的型別。某些型別的鉤子子程僅僅能監視訊息;另外一些就能夠修改訊息或者終止訊息在鉤子連結串列中的前進,這樣就阻止了訊息到達下一個鉤子子程或者目標窗體。

      這裡有幾個概念上的翻譯,主要有:

              Hook Chain : 鉤子連結串列

              hook procedures : 鉤子子程(即得到訊息後進行處理的程式段) 

     如果對於同一事件既安裝了執行緒勾子又安裝了全域性勾子,那麼系統會自動先呼叫執行緒勾子,然後呼叫全域性勾子。

最近學習hook,看了幾個教程: