利用鉤子函數(shù)來捕捉鍵盤響應(yīng)的windows應(yīng)用程序

字號:

一:引言:  
    你也許一直對金山詞霸的屏幕抓詞的實(shí)現(xiàn)原理感到困惑,你也許希望將你的鍵盤,鼠標(biāo)的活動適時的記錄下來,甚至你想知道木馬在windows操作系統(tǒng)是怎樣進(jìn)行木馬dll的加載的…..其實(shí)這些都是用到了windows的鉤子函數(shù)。因此本文將對鉤子函數(shù)的相關(guān)知識進(jìn)行闡述。當(dāng)然,本文的目的并不是想通過此程序讓讀者去竊取別人的密碼,只是由于鉤子函數(shù)在windows系統(tǒng)中是一個非常重要的系統(tǒng)接口函數(shù),所以想和大家共同的探討,當(dāng)然本文也對怎樣建立動態(tài)連結(jié)庫(DLL)作了一些簡單的描述。(本文的程序為vc6.0的開發(fā)環(huán)境,語言是:C和win32 api)。
    二:鉤子概述:
    微軟的windowsX操作系統(tǒng)是建立在事件驅(qū)動的機(jī)制上的,也就是通過消息傳遞來實(shí)現(xiàn)。而鉤子在windows操作系統(tǒng)中,是一種能在事件(比如:消息、鼠標(biāo)激活、鍵盤響應(yīng))到達(dá)應(yīng)用程序前中途接獲事件的機(jī)制。而且,鉤子函數(shù)還可以通過修改、丟棄等手段來對事件起作用。
    Windows 有兩種鉤子,一種是特定線程鉤子(Thread specific hooks),一種是全局系統(tǒng)鉤子(Systemwide hooks)。特定線程鉤子只是監(jiān)視指定的線程,而全局系統(tǒng)鉤子則可以監(jiān)視系統(tǒng)中所有的線程。無論是特定線程鉤子,還是全局系統(tǒng)鉤子,都是通過SetWindowsHookEx ()來設(shè)置鉤子的。對于特定線程鉤子,鉤子的函數(shù)既可以是包含在一個.exe也可以是一個.dll。但是對于一個全局系統(tǒng)鉤子,鉤子函數(shù)必須包含在獨(dú)立的dll中,因此,當(dāng)我們要捕捉鍵盤響應(yīng)時,我們必須創(chuàng)建一個動態(tài)鏈接庫。但是當(dāng)鉤子函數(shù)在得到了控制權(quán),并對相關(guān)的事件處理完后,如果想要該消息得以繼續(xù)的傳遞,那么則必須調(diào)用另一個函數(shù):CallNextHookEx。由于系統(tǒng)必須對每個消息處理,鉤子程序因此增加了處理的負(fù)擔(dān),因此也降低了系統(tǒng)的性能。鑒于這一點(diǎn),在windows ce中對鉤子程序并不支持。所以當(dāng)程序完成并退出時,應(yīng)當(dāng)釋放鉤子,調(diào)用函數(shù):UnhookWindowsHookEx。
    下面我們將舉一個例子(捕捉鍵盤)來詳細(xì)的講解鉤子函數(shù)的程序設(shè)計。
    三:程序的設(shè)計:
    I:設(shè)置鉤子
    設(shè)置鉤子是通過SetWindowsHookEx ()的API函數(shù).
    原形: HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId)
    idhook:裝入鉤子的類型.
    lpfn: 鉤子進(jìn)程的入口地址
    hMod: 應(yīng)用程序的事件句柄
    dwThreadId: 裝入鉤子的線程標(biāo)示
    參數(shù):
    idHook:
    這個參數(shù)可以是以下值:
    WH_CALLWNDPROC、WH_CALLWNDPROCRET、WH_CBT、WH_DEBUG、WH_FOREGROUNDIDLE、WH_GETMESSAGE、WH_JOURNALPLAYBACK、WH_JOURNALRECORD、WH_KEYBOARD、WH_KEYBOARD_LL、WH_MOUSE、WH_MOUSE_LL、WH_MSGFILTER、WH_SHELL、WH_SYSMSGFILTER。
    對于這些參數(shù),我不想一一加以解釋,因為MSDN中有關(guān)于他們的詳細(xì)注解。我只挑選其中的幾個加以中文說明。
    WH_KEYBOARD:一旦有鍵盤敲打消息(鍵盤的按下、鍵盤的彈起),在這個消息被放在應(yīng)用程序的消息隊列前,WINDOWS將會調(diào)用你的鉤子函數(shù)。鉤子函數(shù)可以改變和丟棄鍵盤敲打消息。
    WH_MOUSE:每個鼠標(biāo)消息在被放在應(yīng)用程序的消息隊列前,WINDOWS將會調(diào)用你的鉤子函數(shù)。鉤子函數(shù)可以改變和丟棄鼠標(biāo)消息。
    WH_GETMESSAGE:每次當(dāng)你的應(yīng)用程序調(diào)用一個GetMessage()或者一個PeekMessage()為了去從應(yīng)用程序的消息隊列中要求一個消息時,WINDOWS都會調(diào)用你的鉤子函數(shù)。而鉤子函數(shù)可以改變和丟棄這個消息。
    II:釋放鉤子
    鉤子的釋放使用的是UnhookWindowsHookEx()函數(shù)
    原形:BOOL UnhookWindowsHookEx( HHOOK hhk )
    UnhookWindowsHookEx()函數(shù)將釋放的是鉤子鏈中函數(shù)SetWindowsHookEx所裝入的鉤子進(jìn)程。
    hhk: 將要釋放的鉤子進(jìn)程的句柄。
    III:鉤子進(jìn)程
    鉤子進(jìn)程使用函數(shù)HookProc;其實(shí)HookProc僅僅只是應(yīng)用程序定義的符號。比如你可以寫成KeyBoardHook.但是參數(shù)是不變的。Win32 API提供了諸如:CallWndProc、GetMsgProc、DebugProc、CBTProc、MouseProc、KeyboardProc、MessageProc等函數(shù),對于他們的詳細(xì)講解,可以看MSDN我在此只講解一下KeyBoardHook的含義。
    原形:LRESULT CALLBACK KeyBoardHook (int nCode, WPARAM wParam, LPARAM lParam)
    說明:鉤子進(jìn)程是一些依附在一個鉤子上的一些函數(shù),因此鉤子進(jìn)程只被WINDOWS調(diào)用而不被應(yīng)用程序調(diào)用,他們有時就需要作為一個回調(diào)函數(shù)(CALLBACK)。
    參數(shù)說明:
    nCode:鉤子代碼,鉤子進(jìn)程使用鉤子代碼去決定是否執(zhí)行。而鉤子代碼的值是依靠鉤子的種類來定的。每種鉤子種類都有他們自己一系列特性的代碼。比如對于WH_KEYBOARD,鉤子代碼的參數(shù)有:HC_ACTION,HC_NOREMOVE。HC_ACTION的意義:參數(shù)wParam 和lParam 包含了鍵盤敲打消息的信息,HC_NOREMOVE的意義:參數(shù)wParam 和lParam包含了鍵盤敲打消息的信息,并且,鍵盤敲打消息一直沒有從消息隊列中刪除。(應(yīng)用程序調(diào)用PeekMessage函數(shù),并且設(shè)置PM_NOREMOVE標(biāo)志)。也就是說當(dāng)nCode等于HC_ACTION時,鉤子進(jìn)程必須處理消息。而為HC_NOREMOVE時,鉤子進(jìn)程必須傳遞消息給CallNextHookEx函數(shù),而不能做進(jìn)一步的處理,而且必須有CallNextHookEx函數(shù)的返回值。
    wParam:鍵盤敲打所產(chǎn)生的鍵盤消息,鍵盤按鍵的虛擬代碼。
    lParam:包含了消息細(xì)節(jié)。
    注意:如果鉤子進(jìn)程中nCode小于零,鉤子進(jìn)程必須返回(return) CallNextHookEx(nCode,wParam,lParam);而鉤子進(jìn)程中的nCode大于零,但是鉤子進(jìn)程并不處理消息,作者推薦你調(diào)用CallNextHookEx并且返回該函數(shù)的返回值。否則,如果另一個應(yīng)用程序也裝入WH_KEYBOARD 鉤子,那么該鉤子將不接受鉤子通知并且返回一個不正確的值。如果鉤子進(jìn)程處理了消息,它可能返回一個非零值去阻止系統(tǒng)傳遞該信息到其它剩下的鉤子或者windows進(jìn)程。所以在鉤子進(jìn)程的最后都返回CallNextHookEx的返回值。
    IV:調(diào)用下一個鉤子函數(shù)
    調(diào)用下一個鉤子函數(shù)時使用CallNexHookEx函數(shù)。
    原形:LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam )
    CallNexHookEx()函數(shù)用于對當(dāng)前鉤子鏈中的下一個鉤子進(jìn)程傳遞鉤子信息,一個鉤子進(jìn)程既可以在鉤子信息處理前,也可以在鉤子信息處理后調(diào)用該函數(shù)。為什么使用該函數(shù)已在iii鉤子進(jìn)程中的“注意”中,加以了詳細(xì)的說明。
    hhk: 當(dāng)前鉤子的句柄
    nCode: 傳送到鉤子進(jìn)程的鉤子代碼。
    wParam:傳送到鉤子進(jìn)程的值。
    lParam:傳送到鉤子進(jìn)程的值。
    參數(shù):
    hhk: 當(dāng)前鉤子的句柄. 應(yīng)用程序接受這個句柄,作為先前調(diào)用SetWindowsHookE函數(shù)的結(jié)果
    nCode: 傳送到鉤子進(jìn)程的鉤子代碼,下一個鉤子進(jìn)程使用這個代碼以此決定如何處理鉤子信息
    wParam:傳送給鉤子進(jìn)程的wParam 參數(shù)值 ,參數(shù)值的具體含義與當(dāng)前鉤子鏈的掛接的鉤子類型有關(guān)
    lParam : 傳送給鉤子進(jìn)程的wParam 參數(shù)值 ,參數(shù)值的具體含義與當(dāng)前鉤子鏈的掛接的鉤子類型有關(guān)
    返回值:返回值是鏈中下一個鉤子進(jìn)程返回的值,當(dāng)前鉤子進(jìn)程必須返回這個值,返回值的具體含義與掛接的鉤子類型有關(guān),詳細(xì)信息請參看具體的鉤子進(jìn)程描述。
    V 建立一個動態(tài)連接庫(DLL)
    當(dāng)我們熟悉了以上的各個函數(shù)后,現(xiàn)在我們開始編寫一個動態(tài)連接庫(DLL)。在這兒我采用的是WIN32 DLL,而不是MFC DLL。而且以下所有的程序也都是采用C語言去編寫。這主要是因為使用WIN32 API能夠更詳細(xì)、更全面的控制程序的如何執(zhí)行,而使用MFC,一些低級的控制是不可能實(shí)現(xiàn)的(當(dāng)然,僅對該程序來說,也是可以使用MFC的)。