日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

Windows 钩子的使用

發(fā)布時(shí)間:2024/4/19 windows 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows 钩子的使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們知道Windows中的窗口程序是基于消息,由事件驅(qū)動(dòng)的,在某些情況下可能需要捕獲或者修改消息,從而完成一些特殊的功能(MFC框架就利用Windows鉤子對(duì)消息進(jìn)行引導(dǎo))。對(duì)于捕獲消息而言,無(wú)法使用IAT或Inline Hook之類的方式去進(jìn)行捕獲,這就要用到接下來(lái)要介紹的Windows提供的專門用于處理消息的鉤子函數(shù)。

1.?掛鉤原理

Windows下的應(yīng)用程序大部分都是基于消息機(jī)制的,它們都會(huì)有一個(gè)消息過(guò)程函數(shù),根據(jù)不同的消息完成不同的功能。Windows操作系統(tǒng)提供的鉤子機(jī)制的作用就是用來(lái)截獲和監(jiān)視這些系統(tǒng)中的消息。Windows鉤子琳瑯滿目,可以用來(lái)應(yīng)對(duì)各種不同的消息。

按照鉤子作用的范圍不同,又可以分為局部鉤子和全局鉤子。局部鉤子是針對(duì)某個(gè)線程的;而全局鉤子則是作用于整個(gè)系統(tǒng)中基于消息的應(yīng)用。全局鉤子需要使用DLL文件,在DLL中實(shí)現(xiàn)相應(yīng)的鉤子函數(shù)。在操作系統(tǒng)中安裝全局鉤子后,只要進(jìn)程接收到可以發(fā)出鉤子的消息,全局鉤子的DLL文件就會(huì)被操作系統(tǒng)自動(dòng)或強(qiáng)行地加載到該進(jìn)程中。因此,設(shè)置消息鉤子,也可以達(dá)到DLL注入的目的。

2.?鉤子函數(shù)

[cpp] view plaincopy print?
  • HHOOK?SetWindowsHookEx(????????
  • ??
  • ????int?idHook,??
  • ????HOOKPROC?lpfn,??
  • ????HINSTANCE?hMod,??
  • ????DWORD?dwThreadId??
  • );??
  • HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId );

    該函數(shù)的返回值是一個(gè)鉤子句柄。參數(shù)介紹如下:

    lpfn:指定Hook函數(shù)的地址。如果dwThreadId參數(shù)被賦值為0,或者被設(shè)置為一個(gè)其他進(jìn)程中的線程ID,那么lpfn屬于DLL中的函數(shù)過(guò)程。如果dwThreadId為當(dāng)前進(jìn)程中的一個(gè)線程ID,那么lpfn可以使指向當(dāng)前進(jìn)程模塊中的函數(shù),當(dāng)然,也可以使DLL模塊中的函數(shù)。

    hMod:該參數(shù)指定鉤子函數(shù)所在模塊的模塊句柄。即lpfn所在的模塊句柄。如果dwThreadId為當(dāng)前進(jìn)程中的線程ID,且lpfn所指函數(shù)在當(dāng)前進(jìn)程中,則該參數(shù)被設(shè)置為NULL。

    dwThreadId:指定需要被掛鉤的線程ID號(hào)。如果設(shè)置為0,表示在所有基于消息的線程中掛鉤;如果設(shè)置了具體的線程ID,表示在指定線程中掛鉤。該參數(shù)影響上面兩個(gè)參數(shù)的取值,同時(shí)也決定了該鉤子是全局鉤子還是局部鉤子。

    idHook:該參數(shù)表示鉤子的類型。常用的幾種如下:

    ※? WH_GETMESSAGE

    按照該鉤子的作用是監(jiān)視被投遞到消息隊(duì)列中的消息。也就是當(dāng)調(diào)用GetMessage或PeekMessage函數(shù)時(shí),函數(shù)從程序的消息隊(duì)列中獲取一個(gè)消息后調(diào)用該鉤子。

    WH_GETMESSAGEG的鉤子函數(shù)如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?GetMsgProc(????????
  • ??
  • ????int?code,???????//hook?code??
  • ????WPARAM?wParam,??????//removal?option??
  • ????LPARAM?lParam????????//message??
  • );??
  • LRESULT CALLBACK GetMsgProc( int code, //hook codeWPARAM wParam, //removal optionLPARAM lParam //message );

    ※? WH_MOUSE

    該鉤子用于監(jiān)視鼠標(biāo)消息。鉤子函數(shù)如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?MouseProc(????????
  • ??
  • ????int?nCode,??????//hook?code??
  • ????WPARAM?wParam,??????//message?identifier??
  • ????LPARAM?lParam???????//mouse?coordinates??
  • );??
  • LRESULT CALLBACK MouseProc( int nCode, //hook codeWPARAM wParam, //message identifierLPARAM lParam //mouse coordinates );

    ※? WH_KEYBOARD

    該鉤子用于監(jiān)視鍵盤消息。鉤子函數(shù)如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?KeyboardProc(????????
  • ??
  • ????int?code,???????//hook?code??
  • ????WPARAM?wParam,??????//virtual-key?code??
  • ????LPARAM?lParam???????//keystroke-message?information??
  • );??
  • LRESULT CALLBACK KeyboardProc( int code, //hook codeWPARAM wParam, //virtual-key codeLPARAM lParam //keystroke-message information );

    ※? WH_DEBUG

    用于調(diào)試其它鉤子。鉤子函數(shù)如下:

    [cpp] view plaincopy print?
  • LRESULT?CALLBACK?DebugProc(????????
  • ??
  • ????int?nCode,??????//hook?code??
  • ????WPARAM?wParam,??????//hook?type??
  • ????LPARAM?lParam???????//debugging?information??
  • );??
  • LRESULT CALLBACK DebugProc( int nCode, //hook codeWPARAM wParam, //hook typeLPARAM lParam //debugging information );

    對(duì)于以上鉤子函數(shù)的詳情還請(qǐng)各位看客老爺們自行挪步到MSDN了。

    移除先前用SetWindowsHookEx安裝的鉤子:

    [cpp] view plaincopy print?
  • BOOL?UnhookWindowsHookEx(????????
  • ??
  • ????HHOOK?hhk??
  • );??
  • BOOL UnhookWindowsHookEx( HHOOK hhk );

    唯一的參數(shù)是待移除的鉤子句柄。

    在實(shí)際應(yīng)用中,可以多次調(diào)用SetWindowsHookEx函數(shù)來(lái)安裝鉤子,而且可以安裝多個(gè)同樣類型的鉤子。這樣,鉤子就會(huì)形成一條鉤子鏈,最后安裝的鉤子會(huì)首先截獲到消息。當(dāng)該鉤子對(duì)消息處理完畢后,可以選擇返回或者把消息繼續(xù)傳遞下去。如果是為了屏蔽某消息,可以在安裝的鉤子函數(shù)中直接返回非零值。如果希望我們的鉤子函數(shù)處理完消息后可以繼續(xù)傳遞給目標(biāo)窗口,則必須選擇將消息繼續(xù)傳遞。繼續(xù)傳遞消息的函數(shù)定義如下:

    [cpp] view plaincopy print?
  • LRESULT?CallNextHookEx(????????
  • ??
  • ????HHOOK?hhk,??????//handle?to?current?hook??
  • ????int?nCode,??????//hook?code?passed?to?hook?procedure??
  • ????WPARAM?wParam,??????//value?passed?to?hook?procedure??
  • ????LPARAM?lParam???????//value?passed?to?hook?procedure??
  • );??
  • LRESULT CallNextHookEx( HHOOK hhk, //handle to current hookint nCode, //hook code passed to hook procedureWPARAM wParam, //value passed to hook procedureLPARAM lParam //value passed to hook procedure );

    第一個(gè)參數(shù)是鉤子句柄,就是調(diào)用SetWindowsHookEx函數(shù)的返回值;后面3個(gè)參數(shù)是鉤子的參數(shù),直接一次copy即可。例如:

    [cpp] view plaincopy print?
  • HHOOK?g_Hook?=?SetWindowsHookEx(…);??
  • LRESULT?CALLBACK?GetMsgProc(????????
  • ??
  • ????int?code,???????//hook?code??
  • ????WPARAM?wParam,??????//removal?option??
  • ????LPARAM?lParam????????//message??
  • )??
  • {??
  • ????return?CallNextHookEx(g_Hook,?code,?wParam,?lParam);??
  • }??
  • HHOOK g_Hook = SetWindowsHookEx(…); LRESULT CALLBACK GetMsgProc( int code, //hook codeWPARAM wParam, //removal optionLPARAM lParam //message ) {return CallNextHookEx(g_Hook, code, wParam, lParam); }


    3.?鉤子實(shí)例

    Windows鉤子的使用場(chǎng)景比較廣泛,我們就幾種比較常見的情況做一個(gè)應(yīng)用示例。

    3.1全局鍵盤鉤子

    先新建一個(gè)DLL程序(這個(gè)不會(huì)可以看我以前的博客,這里就不重復(fù)了),我們?cè)陬^文件中增加兩個(gè)導(dǎo)出函數(shù)和兩個(gè)全局。

    [cpp] view plaincopy print?
  • #define?MY_API?__declspec(dllexport)??
  • extern?"C"?MY_API?VOID?SetHookOn();??
  • extern?"C"?MY_API?VOID?SetHookOff();??
  • HHOOK?g_Hook?=?NULL;????????//鉤子句柄??
  • HINSTANCE?g_Inst?=?NULL;????//DLL模塊句柄??
  • #define MY_API __declspec(dllexport) extern "C" MY_API VOID SetHookOn(); extern "C" MY_API VOID SetHookOff(); HHOOK g_Hook = NULL; //鉤子句柄 HINSTANCE g_Inst = NULL; //DLL模塊句柄

    在DllMain中保存該DLL模塊的句柄,以方便安裝全局鉤子。

    [cpp] view plaincopy print?
  • BOOL?APIENTRY?DllMain(?HANDLE?hModule,???
  • ???????????????????????DWORD??ul_reason_for_call,???
  • ???????????????????????LPVOID?lpReserved??
  • ?????????????????????)??
  • {??
  • ??
  • ????//保存DLL模塊句柄??
  • ????g_Inst?=?(HINSTANCE)hModule;??
  • ??????return?TRUE;??
  • }??
  • BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {//保存DLL模塊句柄g_Inst = (HINSTANCE)hModule;return TRUE; }

    安裝與卸載鉤子的函數(shù)如下:

    [cpp] view plaincopy print?
  • VOID?SetHookOn()??
  • {??
  • ????//安裝鉤子??
  • ????g_Hook?=?SetWindowsHookEx(WH_KEYBOARD,?KeyboardProc,?g_Inst,?0);??
  • }??
  • ??
  • VOID?SetHookOff()??
  • {??
  • ????//卸載鉤子??
  • ????UnhookWindowsHookEx(g_Hook);??
  • }??
  • VOID SetHookOn() {//安裝鉤子g_Hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_Inst, 0); }VOID SetHookOff() {//卸載鉤子UnhookWindowsHookEx(g_Hook); }

    鉤子函數(shù)的實(shí)現(xiàn)如下:

    [cpp] view plaincopy print?
  • //鉤子函數(shù)??
  • LRESULT?CALLBACK?KeyboardProc(int?code,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????if(code?<?0)??
  • ????{??
  • ????????//如果code小于0,必須調(diào)用CallNextHookEx傳遞消息,不處理該消息,并返回CallNextHookEx的返回值。??
  • ????????return?CallNextHookEx(g_Hook,?code,?wParam,?lParam);??
  • ????}??
  • ??
  • ????if(code?==?HC_ACTION?&&?lParam?>?0)??
  • ????{??
  • ????????//code等于HC_ACTION,表示消息中包含按鍵消息??
  • ????????//如果為WM_KEYDOWN,則顯示按鍵對(duì)應(yīng)的文本??
  • ????????char?szBuf[MAXBYTE]?=?{0};??
  • ????????GetKeyNameText(lParam,?szBuf,?MAXBYTE);??
  • ????????MessageBox(NULL,?szBuf,?"提示",?MB_OK);??
  • ????}??
  • ??
  • ????return?CallNextHookEx(g_Hook,?code,?wParam,?lParam);??
  • ??
  • }??
  • //鉤子函數(shù) LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) {if(code < 0){//如果code小于0,必須調(diào)用CallNextHookEx傳遞消息,不處理該消息,并返回CallNextHookEx的返回值。return CallNextHookEx(g_Hook, code, wParam, lParam);}if(code == HC_ACTION && lParam > 0){//code等于HC_ACTION,表示消息中包含按鍵消息//如果為WM_KEYDOWN,則顯示按鍵對(duì)應(yīng)的文本char szBuf[MAXBYTE] = {0};GetKeyNameText(lParam, szBuf, MAXBYTE);MessageBox(NULL, szBuf, "提示", MB_OK);}return CallNextHookEx(g_Hook, code, wParam, lParam);}

    編譯鏈接后產(chǎn)生我們需要的.dll和.lib文件,然后新建一個(gè)項(xiàng)目來(lái)導(dǎo)入動(dòng)態(tài)庫(kù)內(nèi)容調(diào)用相關(guān)函數(shù)。

    新建項(xiàng)目如下:



    首先導(dǎo)入庫(kù):

    [cpp] view plaincopy print?
  • #pragma?comment?(lib,?"全局鉤子.lib")??
  • #pragma comment (lib, "全局鉤子.lib")

    聲明將要調(diào)用的函數(shù)(不聲明鏈接時(shí)將報(bào)錯(cuò)):

    [cpp] view plaincopy print?
  • extern?"C"?VOID?SetHookOn();??
  • extern?"C"?VOID?SetHookOff();??
  • extern "C" VOID SetHookOn(); extern "C" VOID SetHookOff();

    在按鈕事件中調(diào)用導(dǎo)出函數(shù):

    [cpp] view plaincopy print?
  • void?CHookDebugDlg::OnHookon()???
  • {??
  • ????SetHookOn();??
  • }??
  • ??
  • void?CHookDebugDlg::OnHookoff()???
  • {??
  • ????SetHookOff();??
  • }??
  • void CHookDebugDlg::OnHookon() {SetHookOn(); }void CHookDebugDlg::OnHookoff() {SetHookOff(); }

    執(zhí)行結(jié)果如下:


    3.2低級(jí)鍵盤鉤子

    數(shù)據(jù)防泄漏軟件通常會(huì)精致PrintScreen鍵,防止通過(guò)截屏將數(shù)據(jù)保存為圖片而導(dǎo)致數(shù)據(jù)泄密。下面我們也可以模仿一下,簡(jiǎn)單的實(shí)現(xiàn)該功能。這里需要注意的是,普通的鍵盤鉤子(WH_KEYBOARD)是無(wú)法過(guò)濾一些系統(tǒng)按鍵的,得通過(guò)安裝低級(jí)鍵盤鉤子(WH_KEYBOARD_LL)來(lái)達(dá)到目的。

    在低級(jí)鍵盤鉤子的回調(diào)函數(shù)中,判斷是否為PrintScreen鍵,如果是,則直接返回TRUE;如果不是,則傳遞給下一個(gè)鉤子處理。

    具體DLL中的實(shí)現(xiàn)代碼如下:

    [cpp] view plaincopy print?
  • BOOL?SetHookOn()??
  • {??
  • ????if(g_Hook?!=?NULL)??
  • ????{??
  • ????????return?FALSE;??
  • ????}??
  • ????//安裝鉤子??
  • ????g_Hook?=?SetWindowsHookEx(WH_KEYBOARD_LL,?LowLevelKeyboardProc,?g_Inst,?0);??
  • ????if(NULL?==?g_Hook)??
  • ????{??
  • ????????MessageBox(NULL,?"安裝鉤子出錯(cuò)!",?"ERROR",?MB_ICONSTOP);??
  • ????????return?FALSE;??
  • ????}??
  • ??
  • ????return?TRUE;??
  • }??
  • ??
  • BOOL?SetHookOff()??
  • {??
  • ????if(g_Hook?==?NULL)??
  • ????{??
  • ????????return?FALSE;??
  • ????}??
  • ????//卸載鉤子??
  • ????UnhookWindowsHookEx(g_Hook);??
  • ????g_Hook?=?NULL;??
  • ????return?TRUE;??
  • }??
  • ??
  • //鉤子函數(shù)??
  • LRESULT?CALLBACK?LowLevelKeyboardProc(int?code,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????KBDLLHOOKSTRUCT?*Key_Info?=?(KBDLLHOOKSTRUCT?*)lParam;??
  • ??
  • ????if(HC_ACTION?==?code)??
  • ????{??
  • ????????if(WM_KEYDOWN?==?wParam?||?WM_SYSKEYDOWN?==?wParam)??
  • ????????{??
  • ????????????if(Key_Info->vkCode?==?VK_SNAPSHOT)??
  • ????????????{??
  • ????????????????MessageBox(NULL,?"該鍵已禁用!",?"ERROR",?MB_ICONSTOP);??
  • ????????????????return?TRUE;??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??
  • ????return?CallNextHookEx(g_Hook,?code,?wParam,?lParam);??
  • ??
  • }??
  • BOOL SetHookOn() {if(g_Hook != NULL){return FALSE;}//安裝鉤子g_Hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, g_Inst, 0);if(NULL == g_Hook){MessageBox(NULL, "安裝鉤子出錯(cuò)!", "ERROR", MB_ICONSTOP);return FALSE;}return TRUE; }BOOL SetHookOff() {if(g_Hook == NULL){return FALSE;}//卸載鉤子UnhookWindowsHookEx(g_Hook);g_Hook = NULL;return TRUE; }//鉤子函數(shù) LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) {KBDLLHOOKSTRUCT *Key_Info = (KBDLLHOOKSTRUCT *)lParam;if(HC_ACTION == code){if(WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam){if(Key_Info->vkCode == VK_SNAPSHOT){MessageBox(NULL, "該鍵已禁用!", "ERROR", MB_ICONSTOP);return TRUE;}}}return CallNextHookEx(g_Hook, code, wParam, lParam);}

    依然利用前面的小程序,執(zhí)行后按下PrintScreen鍵,效果如下:




    可能在編譯時(shí)會(huì)報(bào)錯(cuò),說(shuō)WH_KEYBOARD_LL和KBDLLHOOKSTRUCT未定義,此時(shí)可以在文件開頭加上如下代碼:

    [cpp] view plaincopy print?
  • #define?WH_KEYBOARD_LL?13??
  • ??
  • typedef?struct?tagKBDLLHOOKSTRUCT?{??
  • ??
  • DWORD?vkCode;??
  • ??
  • DWORD?scanCode;??
  • ??
  • DWORD?flags;??
  • ??
  • DWORD?time;??
  • ??
  • DWORD?dwExtraInfo;??
  • ??
  • }?KBDLLHOOKSTRUCT,?FAR?*LPKBDLLHOOKSTRUCT,?*PKBDLLHOOKSTRUCT;??
  • #define WH_KEYBOARD_LL 13typedef struct tagKBDLLHOOKSTRUCT {DWORD vkCode;DWORD scanCode;DWORD flags;DWORD time;DWORD dwExtraInfo;} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

    其實(shí)在winuser.h中已有定義,但是可能是兼容的緣故用不了。

    3.3鉤子注入DLL

    利用WH_GETMESSAGE鉤子,可以方便地將DLL文件注入到所有基于消息機(jī)制的程序中。因?yàn)橛袝r(shí)候可能需要DLL文件完成一些工作,但是工作時(shí)需要DLL在目標(biāo)進(jìn)程的空間中。這個(gè)時(shí)候,就可以將DLL注入目標(biāo)進(jìn)程來(lái)完成相關(guān)的功能。

    主要的代碼如下:

    [cpp] view plaincopy print?
  • BOOL?APIENTRY?DllMain(?HANDLE?hModule,???
  • ???????????????????????DWORD??ul_reason_for_call,???
  • ???????????????????????LPVOID?lpReserved??
  • ?????????????????????)??
  • {??
  • ????//保存DLL模塊句柄??
  • ????g_Inst?=?(HINSTANCE)hModule;??
  • ??
  • ????switch?(ul_reason_for_call)??
  • ????{??
  • ????????case?DLL_PROCESS_ATTACH:??
  • ????????????{??
  • ????????????????DoSomething();??
  • ????????????????break;??
  • ????????????}??
  • ????????case?DLL_THREAD_ATTACH:??
  • ????????case?DLL_THREAD_DETACH:??
  • ????????case?DLL_PROCESS_DETACH:??
  • ????????????break;??
  • ????}??
  • ????return?TRUE;??
  • }??
  • VOID?SetHookOn()??
  • {??
  • ????g_Hook?=?SetWindowsHookEx(WH_GETMESSAGE,?GetMsgProc,?g_Inst,?0);??
  • }??
  • ??
  • VOID?SetHookOff()??
  • {??
  • ????UnhookWindowsHookEx(g_Hook);??
  • }??
  • ??
  • LRESULT?CALLBACK?GetMsgProc(int?code,?WPARAM?wParam,?LPARAM?lParam)??
  • {??
  • ????return?CallNextHookEx(g_Hook,?code,?wParam,?lParam);??
  • }??
  • ??
  • VOID?DoSomething()??
  • {??
  • ????MessageBox(NULL,?"Hello,我被執(zhí)行了!",?"提示",?MB_OK);??
  • }??
  • BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {//保存DLL模塊句柄g_Inst = (HINSTANCE)hModule;switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{DoSomething();break;}case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE; } VOID SetHookOn() {g_Hook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_Inst, 0); }VOID SetHookOff() {UnhookWindowsHookEx(g_Hook); }LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {return CallNextHookEx(g_Hook, code, wParam, lParam); }VOID DoSomething() {MessageBox(NULL, "Hello,我被執(zhí)行了!", "提示", MB_OK); }

    執(zhí)行效果圖:


    ?需要注意的是,此處執(zhí)行的DoSomething并不是導(dǎo)出函數(shù)哦。




    與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的Windows 钩子的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。