Hook技术简介
鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達后,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。這和前面我博客的窗口子類化都異曲同工,但是Hook可強大多了,我是這兩天才開始看的,所以略知皮毛。
鉤子子程必須按照以下的語法:
LRESULT CALLBACK HookProc
(
? int nCode, //指定是否需要處理該消息?
WPARAM wParam,?
LPARAM lParam //包含該消息的附加消息 ,
);
這個回調函數的名字可以隨你取,但形式可一定要滿足以上要求,其實鉤子的回調函數和Windows的差不多一個德行。看看鉤子函數的返回值,若是返回非0值,表示我們已經自己處理了該消息,則消息就不被傳遞到目標窗口過程。
看看LRESULT CallNextHookEx
( HHOOK hhk;
int nCode;
WPARAM wParam;
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId );返回值是一個hook的句柄。
idHook是我們感興趣的消息類型,比如我們對鼠標消息感興趣就是WH_MOUSE,再者比如鍵盤消息WH_KEYBOARD,我們可以通過查找Win32 API使用手冊來找到自己感興趣的消息。
第二個參數是鉤子函數的地址,這里就有兩種情況:其實鉤子有兩種,一種是局部鉤子,這種鉤子只能關注自己所在的進程的事件,另一種鉤子叫做遠程鉤子,這里又有兩種:1.基于線程的它將捕獲其它進程中某一特定線程的事件。簡言之,就是可以用來觀察其它進程中的某一特定線程將發生的事件。2.系統范圍的 將捕捉系統中所有進程將發生的事件消息。 看上去局部鉤子的功能沒有遠程鉤子的給力,但是凡事都是要付出代價的,遠程鉤子會影響系統的性能,特別是監視系統范圍的鉤子,因為要監視系統范圍的消息,明顯就會影響系統的速度。
第三個參數和第四個參數相關,所以一起解釋。
如果第四個參數是NULL,則說明是全局鉤子,那么就是鉤子子程與所有的線程關聯,此時第三個參數是程序實例句柄;
如果第三個參數是NULL,則說明鉤子是局部鉤子,說明子程代碼位于當前進程,這時候第四個參數就是當前進程的ID,可以用GetCurrentThreadID()填充,或者可以保存實例來填充,再做介紹···
再看看鉤子函數的卸載,用UnHookWindowsHookEx(HHOOK hhk);參數就是SetWindowsHookEx返回的句柄。
下面是是一個小程序,大概的功能就是實現在所在進程內的鼠標消息和鍵盤消息的截獲,一旦點擊了“LockMouse”那么就在目標窗口截獲了所以鼠標的消息,這里實現的是屏蔽鼠標消息,只能通過按回車鍵恢復鼠標功能。若是點擊了“LockKeyBoard”按鈕,那么只能在編輯框輸入0或者1,但是在沒有點擊的情況下是正常的編輯框。這點可是比窗口子類化更加簡單。
#include?"Windows.h"?? #include?"tchar.h"?? #include?"resource.h"?? ?? ?? HINSTANCE?g_hInstance?;?? static?HHOOK?hHook?=?NULL;?? ?? ?? INT_PTR?CALLBACK?ProcWinMain(HWND?hWnd,?UINT?Msg,?WPARAM?wParam?,LPARAM?lParam);?? LRESULT?CALLBACK?MouseProc(int?nCode,?WPARAM?wParam,LPARAM?lParam);??????? LRESULT?CALLBACK?BoardProc(int?nCode,?WPARAM?wParam,LPARAM?lParam);??????? ?? ?? int?WINAPI?WinMain(?????HINSTANCE?hInstance,?? ???????????????????HINSTANCE?hPrevInstance,?? ???????????????????LPSTR?lpCmdLine,?? ???????????????????int?nCmdShow?? ???????????????????)?? {?? ????static?TCHAR?DlgName[]?=?_T("InnerHook");?? ????g_hInstance?=?hInstance?;?? ????DialogBoxParam(hInstance,DlgName,NULL,(?DLGPROC)ProcWinMain,NULL);?? ????return?0;?? }?? ?? ?? INT_PTR?CALLBACK?ProcWinMain(???HWND?hWnd,??? ?????????????????????????????UINT?Msg,??? ?????????????????????????????WPARAM?wParam,??? ?????????????????????????????LPARAM?lParam??? ?????????????????????????????)?? {?? ????static?TCHAR?unLockStr[]?=?_T("Unlocking");?? ????static?TCHAR?LockStr[]?=?_T("Locking");?? ????static?BOOL?isLock?=?FALSE;?? ????static?BOOL?isLock2?=?FALSE;?? ????static?HHOOK?hHook2?=?NULL;?? ????switch(Msg)?? ????{?? ????case?WM_INITDIALOG:?? ????????{?? ????????????SetFocus(GetDlgItem(hWnd,IDC_EDITTEXT));?? ????????}?? ????????break;?? ?? ?? ????case?WM_CLOSE:?? ????????EndDialog(hWnd,NULL);?? ????????break;?? ????case?WM_COMMAND:?? ????????{?? ????????????switch(LOWORD(wParam))?? ????????????{?? ????????????????case?ID_BTNHOOKBOARD?:?? ????????????????{?? ????????????????????if(isLock?==?FALSE)?? ????????????????????{?? ????????????????????????hHook?=?SetWindowsHookEx(WH_KEYBOARD,BoardProc,NULL,GetCurrentThreadId());?? ????????????????????????SetDlgItemText(hWnd,ID_BTNHOOKBOARD,LockStr);?? ????????????????????????isLock?=?TRUE;?? ????????????????????}?? ????????????????????else?? ????????????????????{?? ????????????????????????SetDlgItemText(hWnd,ID_BTNHOOKBOARD,unLockStr);?? ????????????????????????isLock?=?FALSE;??? ????????????????????????UnhookWindowsHookEx(hHook);?? ????????????????????}?? ????????????????}?? ????????????????break;?? ????????????????case?ID_BTNHOOKMOUSE?:?? ????????????????{?? ????????????????????if(isLock2?==?FALSE)?? ????????????????????{?? ????????????????????????hHook2?=?SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());?? ????????????????????????SetDlgItemText(hWnd,ID_BTNHOOKMOUSE,LockStr);?? ????????????????????????isLock2?=?TRUE;?? ????????????????????}?? ????????????????????else?? ????????????????????{?? ????????????????????????SetDlgItemText(hWnd,ID_BTNHOOKMOUSE,unLockStr);?? ????????????????????????isLock2?=?FALSE;?????? ????????????????????????UnhookWindowsHookEx(hHook2);?? ????????????????????}?? ????????????????}?? ????????????????break;?? ????????????}?? ????????}?? ????????break;?? ????default:?? ????????return?FALSE;??? ????}?? ????return?TRUE;?? }?? ?? ?? LRESULT?CALLBACK?MouseProc(int?nCode,?WPARAM?wParam,LPARAM?lParam)?? {?? ????return?1;???????????//返回非0值,表示處理了該消息?? }?? ?? ?? LRESULT?CALLBACK?BoardProc(int?nCode,?WPARAM?wParam,LPARAM?lParam)?? {?? ????if?(wParam?==?VK_RETURN?||?wParam?==?'1'?||?wParam?==?'0')?? ????{?? ????????return?CallNextHookEx(hHook,nCode,wParam,lParam);???//表示不處理該消息,交還給Windows自己處理?? ????}?? ????else?? ????????return?1;???????//表示已經處理了該消息?? }??
鉤子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。這和前面我博客的窗口子類化都異曲同工,但是Hook可強大多了,我是這兩天才開始看的,所以略知皮毛。
/* 鉤子的原理 */
我聽完孫鑫老師C++教程里的解釋,覺得還是把Hook講得比較形象,加上我自己的一些理解就覺得Hook原理不是很難。windows一直都是有自己處理各種消息的函數,Hook其實就能夠做到程序員自己處理自己感興趣的事情。這樣說,假設Windows的消息就是馬路上的車輛,一般情況下是Windows自己派人在檢查,然后呢,Hook是擁有這個能力能在Windows自己安排的檢查站之前也進行抽查,Hook根據程序員的需求可以變化,比如我就感興趣100萬以上的車(可能是走私的(*^__^*)),Hook就能在檢查的時候專門找100萬以上的車,至于其他不上檔次的車Hook就放行,交還給Windows自己的檢查站。同樣Hook可以“為所欲為”,可以擅自設立一個檢查站,也可以兩個,三個···換成程序來說,鉤子函數的工作原理是:當我們創建一個鉤子時,WINDOWS會先在內存中創建一個數據結構,該數據結構包含了鉤子的相關信息,然后把該結構體加到已經存在的鉤子鏈表中去。新的鉤子將加到老的前面。當一個事件發生時,如果我們安裝的是一個局部鉤子(下面有解釋,暫時理解為你程序本身中的),我們進程中的鉤子函數將被調用。/* 鉤子鏈表和函數 */
每一個Hook都有一個與之相關聯的指針列表,稱之為鉤子鏈表,由系統來維護。被Hook子程調用的回調函數,也就是該鉤子的各個處理子程。當與指定的Hook類型關聯的消息發生時,系統就把這個消息傳遞到Hook子程。一些Hook子程可以只監視消息,或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個Hook子程或者目的窗口。最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最后,也就是后加入的先獲得控制權。鉤子子程是一個應用程序定義的回調函數(CALLBACKFunction),不能定義成某個類的成員函數,只能定義為普通的C函數。用以監視系統或某一特定類型的事件,這些事件可以是與某一特定線程關聯的,也可以是系統中所有線程的事件。鉤子子程必須按照以下的語法:
LRESULT CALLBACK HookProc
(
? int nCode, //指定是否需要處理該消息?
WPARAM wParam,?
LPARAM lParam //包含該消息的附加消息 ,
);
這個回調函數的名字可以隨你取,但形式可一定要滿足以上要求,其實鉤子的回調函數和Windows的差不多一個德行。看看鉤子函數的返回值,若是返回非0值,表示我們已經自己處理了該消息,則消息就不被傳遞到目標窗口過程。
看看LRESULT CallNextHookEx
( HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;)這個函數把鉤子信息傳遞給下一個鉤子函數,也就是可以理解成把車輛放行到下一個檢查站,這個可以根據自己的需要進行調用。若是我們只設定了一個鉤子函數,那么我們假設把鉤子消息用CallNextHookEx傳給下個鉤子函數,因為不存在所以就傳遞回了目標窗口函數。
/* 鉤子的安裝和釋放 */
調用SetWindowHookEx函數,該函數的原型如下:HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId );返回值是一個hook的句柄。
idHook是我們感興趣的消息類型,比如我們對鼠標消息感興趣就是WH_MOUSE,再者比如鍵盤消息WH_KEYBOARD,我們可以通過查找Win32 API使用手冊來找到自己感興趣的消息。
第二個參數是鉤子函數的地址,這里就有兩種情況:其實鉤子有兩種,一種是局部鉤子,這種鉤子只能關注自己所在的進程的事件,另一種鉤子叫做遠程鉤子,這里又有兩種:1.基于線程的它將捕獲其它進程中某一特定線程的事件。簡言之,就是可以用來觀察其它進程中的某一特定線程將發生的事件。2.系統范圍的 將捕捉系統中所有進程將發生的事件消息。 看上去局部鉤子的功能沒有遠程鉤子的給力,但是凡事都是要付出代價的,遠程鉤子會影響系統的性能,特別是監視系統范圍的鉤子,因為要監視系統范圍的消息,明顯就會影響系統的速度。
第三個參數和第四個參數相關,所以一起解釋。
如果第四個參數是NULL,則說明是全局鉤子,那么就是鉤子子程與所有的線程關聯,此時第三個參數是程序實例句柄;
如果第三個參數是NULL,則說明鉤子是局部鉤子,說明子程代碼位于當前進程,這時候第四個參數就是當前進程的ID,可以用GetCurrentThreadID()填充,或者可以保存實例來填充,再做介紹···
再看看鉤子函數的卸載,用UnHookWindowsHookEx(HHOOK hhk);參數就是SetWindowsHookEx返回的句柄。
下面是是一個小程序,大概的功能就是實現在所在進程內的鼠標消息和鍵盤消息的截獲,一旦點擊了“LockMouse”那么就在目標窗口截獲了所以鼠標的消息,這里實現的是屏蔽鼠標消息,只能通過按回車鍵恢復鼠標功能。若是點擊了“LockKeyBoard”按鈕,那么只能在編輯框輸入0或者1,但是在沒有點擊的情況下是正常的編輯框。這點可是比窗口子類化更加簡單。
總結
- 上一篇: MySQL如何查询两个日期之间的记录
- 下一篇: 170728、单例模式的三种水平代码