前言:
windows中大部分的應用程序都是基於消息機制的,它們都有一個消息過程函數,根據不同的消息完成不同的功能。而消息鈎子是windows提供的一種消息過濾和預處理機制,可以用來截獲和監視系統中的消息。按照鈎子作用范圍不同,又可以分為局部鈎子和全局鈎子。局部鈎子是針對某個線程的,而全局鈎子是作用於整個系統的基於消息的應用。全局鈎子需要使用DLL文件,在DLL文件中實現相應的鈎子函數。
函數介紹:
HHOOK WINAPI SetWindowsHookExW( _In_ int idHook, _In_ HOOKPROC lpfn, _In_opt_ HINSTANCE hmod, _In_ DWORD dwThreadId);
(1).idHook 要安裝的鈎子(HOOK)的類型,它決定了 HOOKPROC 被調用的時機,可選參數如下。
值 | 含義 |
WH_MSGFILTER = -1 | 線程級,截獲用戶與控件交互的消息 |
WH_JOURNALRECORD = 0 | 系統級,記錄所有消息隊列送出的輸入消息 |
WH_JOURNALPLAYBACK = 1 | 系統級,回放由WH_JOURNALRECORD記錄的消息 |
WH_KEYBOARD = 2 | 系統級或線程級,截獲鍵盤消息 |
WH_GETMESSAGE = 3 | 系統級或線程級,截獲從消息隊列送出的消息 |
WH_CALLWNDPROC = 4 | 系統級或線程級,截獲發送到目標窗口的消息 |
WH_CBT = 5 | 系統級或線程級,截獲系統基本消息 例如:窗口的創建,激活,關閉,最大/最小化,移動等 |
WH_SYSMSGFILTER = 6 | 系統級,截獲系統范圍內用戶與控件交互的消息 |
WH_MOUSE = 7 | 系統級或線程級,截獲鼠標消息 |
WH_HARDWARE = 8 | 系統級或線程級,截獲非標准硬件(非鼠標,鍵盤)的消息 |
WH_DEBUG = 9 | 系統級或線程級,在其它鈎子調用前調用,用於調試鈎子 |
WH_SHELL = 10 | 系統級或線程級,截獲發給外殼應用程序的消息 |
WH_FOREGROUNDIDLE = 11 | 系統級或線程級,在程序前台線程空閑時調用 |
WH_CALLWNDPROCRET = 12 | 系統級或線程級,截獲目標窗口處理完的消息 在SendMessage被調用后發生 |
WH_KEYBOARD_LL = 13 | 系統級,截獲全局鍵盤消息 |
WH_MOUSE_LL = 14 | 系統級,截獲全局鼠標消息 |
(2).lpfn 指向鈎子回調函數的指針。如果最后一個參數 dwThreadId 為0或者是其它進程創建的線程標識符,則 lpfn 參數必須指向DLL中的鈎子回調函數,即 HOOKPROC 函數必須在DLL中實現。否則,lpfn 可以指向與當前進程相關聯的代碼中的鈎子過程。
(3).hmod 包含由 lpfn 參數指向的鈎子回調函數的DLL句柄。如果 dwThreadId 參數指定由當前進程創建線程,並且鈎子回調函數位於當前進程關聯的代碼中,則 hmod 參數必須設置為 NULL。
(4).dwThreadId 與鈎子程序關聯的線程標識符(指定要 HOOK 的線程 ID)。如果此參數為0,則鈎子過程與系統中所有線程相關聯,即全局消息鈎子
實現原理:
通過 SetWindowsHookExW 函數安裝一個用於過濾特定類型消息的鈎子函數(經實驗,鈎子WH_GETMESSAGE 可以立即被觸發,而某些類型的鈎子需要在處理指定類型的消息時才能被觸發),當其它進程中產生了我們過濾的消息,就會調用 HOOKPROC ,如果發現目標DLL(HOOKPROC實現的DLL)尚未加載,就會使用KeUserModeCallback 函數回調 User32.dll 的 __ClientLoadLibrary() 函數,由 User32.dll把這個DLL加載到目標進程中(低級鍵盤和鼠標鈎子的加載有所不同),從而實現DLL注入其它進程的目的。
注意:此法可以對所有有消息循環的程序進行注入,但對那些沒有消息循環的純后台程序無能為力
主要代碼如下:
//共享內存 #pragma data_seg("mydata") HHOOK g_hHook = NULL; //必須賦初值,否則微軟編譯器會把沒有初始化的數據放到普通的未初始化數據段中 //而不是放在shared中,從而導致多個進程之間的共享行為失敗 #pragma data_seg() #pragma comment(linker,"/SECTION:mydata,RWS") HMODULE g_hModule; //鈎子句柄 extern "C" _declspec(dllexport) //卸載鈎子 BOOL UnsetGlobalHook() { if (g_hHook) { UnhookWindowsHookEx(g_hHook); } return TRUE; } //鈎子的回調函數 LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) { MessageBox(NULL, L"你被監視了", L"提示", MB_ICONWARNING | MB_OKCANCEL); //CallNextHookEx函數表示將當前鈎子傳遞給鈎子鏈中的下一個鈎子 //第一個參數當前鈎子的句柄。如果直接返回0,則表示中斷鈎子傳遞 //對鈎子進行攔截 return CallNextHookEx(g_hHook, code, wParam, lParam); } extern "C" _declspec(dllexport) //設置全局鈎子 BOOL SetGlobalHook() { //SetWindowsHookEx,第一個參數表示鈎子的類型,WH_GETMESSAGE表示安裝消息隊列的消息鈎子 //可以監視發送到消息隊列的消息。第二個參數表示鈎子回調函數,第三個參數表示包含鈎子回調 //函數的DLL模塊句柄,如果要設置全局鈎子,則該參數必須指定DLL模塊句柄,第四個參數表示與 //鈎子關聯的線程ID,0表示為全局鈎子,關聯所有線程。 g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0); if (g_hHook == NULL) { return FALSE; } return TRUE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { g_hModule = hModule; break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
//定義函數指針和函數指針變量 typedef BOOL(*ty_SetGlobalHook)(); ty_SetGlobalHook fpSetGlobalHook = NULL; //獲取要注入的DLL的加載基址 m_hDll= LoadLibrary(m_Edit_DllName); if (!m_hDll) { MessageBox(L"Dll加載失敗"); } //獲取設置全局鈎子函數的函數地址。賦值給函數指針變量 fpSetGlobalHook = (ty_SetGlobalHook)GetProcAddress(m_hDll, "SetGlobalHook"); if (!fpSetGlobalHook) { MessageBox(L"加載設置全局鈎子函數地址失敗"); } //設置全局鈎子 BOOL bRet = fpSetGlobalHook(); if (bRet) { MessageBox(L"全局鈎子設置成功"); } else { MessageBox(L"全局鈎子設置失敗"); }