windows鉤子的編寫技術

來源:互聯網
上載者:User
Windows鉤子是使用DLL編寫的。在高層,鉤子由需要監視其他應用程式或系統活動的應用程式使用;在低層,鉤子是windows訊息傳遞中的點,
函數根據這個路徑被注入或附加,以過濾某些類型的訊息,使之達到目的地。一旦捕獲了這些訊息,就可以對其進行修改、記錄或丟棄。函數本身稱為
過濾器。這些過濾器是按照正在過濾的事件的類型進行分類的。當一個過濾函數被附加到一個鉤子時,稱為“sethook”。肯定有些時候設定
了同一類型的多個鉤子。為了處理這種情況,Windows記錄著過濾函數鏈的情況。最近添加的過濾函數是鏈中的第一個函數,因此,它最先有機會過
濾任何訊息通訊量。然後,這個函數執行它的工作並把訊息發送到鏈中的下個鉤子過程,後者完全放棄該訊息。下面先列出Wondows鉤子的類型:
WH_CALLWNDPROC 在訊息被發送到其目的視窗常式之前監視所有的Windows訊息
WH_CALLWNDPROCRET
監視已經被目的Windows常式處理後的訊息
WH_CBT 監視Computer-Base-Training應用程式所用的訊息
WH_DEBUG
輔助調試其他鉤子函數
WH_GETMESSAGE 監視投遞到訊息佇列中的Windows訊息
WH_JOURNALPLAYBACK
投遞以前由WH_JOURNALRECORD異常分支常式處理的訊息
WH_JOURNALRECORD
記錄投遞到系統訊息佇列的輸入訊息
WH_KEYBOARD 監視鍵盤的活動
WH_KEYBOARD_LL
監視低層鍵盤輸入事件(只適用於Windows 2000/XP)
WH_MOUSE 監視滑鼠訊息
WH_MOUSE_LL
監視低層滑鼠輸入事件(只適用於Windows 2000/XP)
WH_MSGFILTER
監視作為對話方塊、訊息框、菜單或捲軸中輸入事件的結果而產生的訊息
WH_SHELL
接受對外殼應用程式有意義的事件通知
WH_SYSMSGFILTER
類似於WH_MSGFILTER,監視作為對話方塊、訊息框、菜單或捲軸中輸入事件的結果而產生的訊息。然而WH_SYSMSGFILTER
為所有活動的應用程式監視這些訊息
現在要來講解 SetWindowsHookEx函數
為了設定一個鉤子,必須調用SetWindowsHookEx函數,原型如下:
HHOOK
SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId)
第一個參數 IdHook代表一個常量,該常量標識了正要設定的鉤子的類型。第二個參數 lpfn 指向一個函數的HOOKPROC類型的指標。第三個參數hMod
包含過濾函數的模組的HINSTANCE 。 第四個參數為線程ID。傳回值是一個鉤子的控制代碼,如果函數沒有成功則傳回值為NULL,需要調用GetLastError函數,
以確定失敗的原因。
UnhookWindowsHookEx函數
如果應用程式設定了windows鉤子,那麼在該程式終止前也需要由它從鉤子鏈中去掉一個鉤子。這是通過UnhookWindowsHookEx函數來實現。原型如下
BOOL UnhookWindowsHookEx(HHOOK hHook)
CallNextHook函數
當為給定鉤子類型設定了一個過濾函數後,該過濾函數放在鉤子類型的過濾函數的鏈頭。過濾函數完成其任務後,就可以通過CallNextHook函數調用鏈中
下一個過濾函數。如果不想繼續進行處理,例如編寫了一個鍵盤鉤子並且想放棄某次擊鍵,就不要調用該函數。然而,如果簡單地記錄一個事件,並且想讓訊息
繼續沿其訊息路徑前進,應用程式則需要在處理完訊息後調用CallNextHook函數,否則其他訊息將得不到處理。如果這種類型的鉤子沒有其他過濾函數,Windows會把這個訊息傳遞到適當的訊息佇列中。
下面來舉一個簡單的例子來說明鉤子的運用,我們來編寫一個鍵盤鉤子程式,在這個例子中所有按下“F1”鍵將要被捕捉,大家熟悉以後可以試著學習捕獲訊息佇列中的windows訊息,這對於編寫外掛程式非常有用,當然有些訊息是程式中自訂的,然而你仍然可以通過一寫技巧來捕捉到其中的某些訊息。這是後話。
首先為鍵盤鉤子建立將包含過濾函數的DLL,在項目工程中建立一個名為KeybdHook的MFC的常規DLL。開啟KeybdHook.h檔案,在CkeybdHookApp類聲明之前加入一些聲明:
#define EXPORTED_DLL_FUNCTION _declspec(dllexport) _stdcall   //提供一種匯出具有_stdcall調用機制的函數的簡單方式
LRESULT _stdcall KbdHookProc(int nCode, WPARAM wParam , LPARAM lParam); //實際的過濾函數
在KeybdHook.cpp中聲明一些全域變數如下:
#pragma data_seg(".SHARDAT")
static HWND
ghWndMain = 0;
static HHOOK
ghKeyHook = NULL;
#pragma data_seg()
HIINSTANCE
ghInstance = 0;
HOOKPROC glpfnHookProc = 0;
好了,KbdHookProc函數的實現是這樣的:
LRESULT EXPORTED_DLL_FUNCTION KbdHookProc(int nCode, WPARAM wParam , LPARAM lParam)
{
    BOOL bHandledKeyStroke = FALSE;
    if(((DWORD)lParam & 0x40000000) && (HC_ACTION == nCode))
    {
switch(wParam)
{
case VK_F1:
           AfxMessageBox("捕捉到了 F1 按鍵!");
           bHandleKeyStroke = TRUE;
break;
default:
break;
       }
     }
    return (bHandledKeystroke ? TRUE: ::CallNextHookEx(ghKeyHook,nCode,wParam,lParam));
}
nCode可能包含的值之一是HC_ACTION常量。實際上這個參數只能是兩個值之一 :HC_ACTION 和 HC_NOREMOVE。 在這兩種情況下,WPARAM 和 LPARAM變數將包含關於擊鍵的資訊。如果nCode小於0,該過濾函數不應該處理擊鍵,而是該調用CallNextHookEx函數。
接下來匯出DLL函數。首先開啟KeybdHook.h檔案,聲明一個用於安裝或設定鍵盤鉤子的函數:
BOOL _stdcall InstallKeyBoardHook(HWND hWnd);
實現如下:
BOOL EXPORTED_DLL_FUNCTION InstallKeyBoardHook(HWND hWnd)
{
   BOOL bSuccess = FALSE;
   if(!ghKeyHook)
   {
      ghWndMain
= hWnd;
      glpfnHookProc
= (HOOKPROC) KbdHookProc;
      bSuccess = (NULL!=(ghKeyHook = ::SetWindowsHookEx(WH_KEYBOARD,glpfnHookProc,ghInstance,NULL)));
    }
   return bSuccess;
}
還要匯出一個函數來釋放鉤子:
BOOL EXPORTED_DLL_FUNCTION DeInstallKeyboardHook()
{
   if(ghKeyHook)
   {
if (TRUE ==(0 != ::UnhookWindowsHookEx(ghKeyHook)))
{
   ghKeyHook = NULL;
}
   }
return (NULL == ghKeyHook);
}
如果大家看一看由MFC產生的DLL的代碼很容易看出來,DLL類是從CWinApp 類派生來的,並且實現了InitInstance成員函數,應該在這裡提供DLL所需的初試化代碼。
使用CWinApp類的優勢是可以像對其他任何CWinApp衍生類別那樣進行DLL編程。例如,可以重載DLL的CWinApp::InitInstance函數,並提供全域初始化。雖然MFC提供了
預設的DllMain實現,但一般還是建議使用者在DLL的CWinApp衍生類別的InitInstance函數中自己進行初始化,在很多情況下是必須這麼做的。
BOOL CKeybdHookApp::InitInstance()
{
   AFX_MANAGE_STATE(AfxGetStaticModuleState());
   ghInstance = AfxGetInstanceHandle();
   return TRUE;
}
int CKeybdHookApp::ExitInstance()
{
   DeInstallKeyboardHook();
   return CWinApp::ExitInstance();
}
這樣一個對鍵盤按鍵訊息捕捉的鉤子函數就全部建立好了。之所以用DLL是方便客戶(指應用程式或其他DLL)匯出調用。以後為了測試鍵盤鉤子,可以建立一個基於對話方塊
的應用程式KeybdHookClient。假定為單擊OK按扭事件建立一個訊息處理常式,在處理常式中調用DLL的InstallKeyboardHook匯出函數:
void CkeybdHookClientDlg::OnOK()
{
   InstallKeyboardHook(GetSafeHwnd());
}
重載對話方塊OnCancel函數
void CkeybdHookClientDlg::OnCancel()
{
   DeInstallKeyboardHook();
   CDialog::OnCancel();
}
別忘記在KeybdClientDlg.cpp檔案前加上 #include "KeybdHook.h"
要測試這個鉤子程式,可以找一個使用F1的程式即可。當你啟動你的KeybdHookClient應用程式,再在其他的軟體程式中按"F1"時,就會彈出一個訊息框,系統告訴你捕捉了按鍵訊息。
OK!今天的講解結束!
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.