Hook技术简介
鉤子(Hook),是Windows消息處理機(jī)制的一個(gè)平臺(tái),應(yīng)用程序可以在上面設(shè)置子程以監(jiān)視指定窗口的某種消息,而且所監(jiān)視的窗口可以是其他進(jìn)程所創(chuàng)建的。當(dāng)消息到達(dá)后,在目標(biāo)窗口處理函數(shù)之前處理它。鉤子機(jī)制允許應(yīng)用程序截獲處理window消息或特定事件。
鉤子實(shí)際上是一個(gè)處理消息的程序段,通過(guò)系統(tǒng)調(diào)用,把它掛入系統(tǒng)。每當(dāng)特定的消息發(fā)出,在沒(méi)有到達(dá)目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數(shù)先得到控制權(quán)。這時(shí)鉤子函數(shù)即可以加工處理(改變)該消息,也可以不作處理而繼續(xù)傳遞該消息,還可以強(qiáng)制結(jié)束消息的傳遞。這和前面我博客的窗口子類(lèi)化都異曲同工,但是Hook可強(qiáng)大多了,我是這兩天才開(kāi)始看的,所以略知皮毛。
鉤子子程必須按照以下的語(yǔ)法:
LRESULT CALLBACK HookProc
(
? int nCode, //指定是否需要處理該消息?
WPARAM wParam,?
LPARAM lParam //包含該消息的附加消息 ,
);
這個(gè)回調(diào)函數(shù)的名字可以隨你取,但形式可一定要滿(mǎn)足以上要求,其實(shí)鉤子的回調(diào)函數(shù)和Windows的差不多一個(gè)德行。看看鉤子函數(shù)的返回值,若是返回非0值,表示我們已經(jīng)自己處理了該消息,則消息就不被傳遞到目標(biāo)窗口過(guò)程。
看看LRESULT CallNextHookEx
( HHOOK hhk;
int nCode;
WPARAM wParam;
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId );返回值是一個(gè)hook的句柄。
idHook是我們感興趣的消息類(lèi)型,比如我們對(duì)鼠標(biāo)消息感興趣就是WH_MOUSE,再者比如鍵盤(pán)消息WH_KEYBOARD,我們可以通過(guò)查找Win32 API使用手冊(cè)來(lái)找到自己感興趣的消息。
第二個(gè)參數(shù)是鉤子函數(shù)的地址,這里就有兩種情況:其實(shí)鉤子有兩種,一種是局部鉤子,這種鉤子只能關(guān)注自己所在的進(jìn)程的事件,另一種鉤子叫做遠(yuǎn)程鉤子,這里又有兩種:1.基于線程的它將捕獲其它進(jìn)程中某一特定線程的事件。簡(jiǎn)言之,就是可以用來(lái)觀察其它進(jìn)程中的某一特定線程將發(fā)生的事件。2.系統(tǒng)范圍的 將捕捉系統(tǒng)中所有進(jìn)程將發(fā)生的事件消息。 看上去局部鉤子的功能沒(méi)有遠(yuǎn)程鉤子的給力,但是凡事都是要付出代價(jià)的,遠(yuǎn)程鉤子會(huì)影響系統(tǒng)的性能,特別是監(jiān)視系統(tǒng)范圍的鉤子,因?yàn)橐O(jiān)視系統(tǒng)范圍的消息,明顯就會(huì)影響系統(tǒng)的速度。
第三個(gè)參數(shù)和第四個(gè)參數(shù)相關(guān),所以一起解釋。
如果第四個(gè)參數(shù)是NULL,則說(shuō)明是全局鉤子,那么就是鉤子子程與所有的線程關(guān)聯(lián),此時(shí)第三個(gè)參數(shù)是程序?qū)嵗浔?#xff1b;
如果第三個(gè)參數(shù)是NULL,則說(shuō)明鉤子是局部鉤子,說(shuō)明子程代碼位于當(dāng)前進(jìn)程,這時(shí)候第四個(gè)參數(shù)就是當(dāng)前進(jìn)程的ID,可以用GetCurrentThreadID()填充,或者可以保存實(shí)例來(lái)填充,再做介紹···
再看看鉤子函數(shù)的卸載,用UnHookWindowsHookEx(HHOOK hhk);參數(shù)就是SetWindowsHookEx返回的句柄。
下面是是一個(gè)小程序,大概的功能就是實(shí)現(xiàn)在所在進(jìn)程內(nèi)的鼠標(biāo)消息和鍵盤(pán)消息的截獲,一旦點(diǎn)擊了“LockMouse”那么就在目標(biāo)窗口截獲了所以鼠標(biāo)的消息,這里實(shí)現(xiàn)的是屏蔽鼠標(biāo)消息,只能通過(guò)按回車(chē)鍵恢復(fù)鼠標(biāo)功能。若是點(diǎn)擊了“LockKeyBoard”按鈕,那么只能在編輯框輸入0或者1,但是在沒(méi)有點(diǎn)擊的情況下是正常的編輯框。這點(diǎn)可是比窗口子類(lèi)化更加簡(jiǎn)單。
#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;???????//表示已經(jīng)處理了該消息?? }??
鉤子實(shí)際上是一個(gè)處理消息的程序段,通過(guò)系統(tǒng)調(diào)用,把它掛入系統(tǒng)。每當(dāng)特定的消息發(fā)出,在沒(méi)有到達(dá)目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數(shù)先得到控制權(quán)。這時(shí)鉤子函數(shù)即可以加工處理(改變)該消息,也可以不作處理而繼續(xù)傳遞該消息,還可以強(qiáng)制結(jié)束消息的傳遞。這和前面我博客的窗口子類(lèi)化都異曲同工,但是Hook可強(qiáng)大多了,我是這兩天才開(kāi)始看的,所以略知皮毛。
/* 鉤子的原理 */
我聽(tīng)完孫鑫老師C++教程里的解釋,覺(jué)得還是把Hook講得比較形象,加上我自己的一些理解就覺(jué)得Hook原理不是很難。windows一直都是有自己處理各種消息的函數(shù),Hook其實(shí)就能夠做到程序員自己處理自己感興趣的事情。這樣說(shuō),假設(shè)Windows的消息就是馬路上的車(chē)輛,一般情況下是Windows自己派人在檢查,然后呢,Hook是擁有這個(gè)能力能在Windows自己安排的檢查站之前也進(jìn)行抽查,Hook根據(jù)程序員的需求可以變化,比如我就感興趣100萬(wàn)以上的車(chē)(可能是走私的(*^__^*)),Hook就能在檢查的時(shí)候?qū)iT(mén)找100萬(wàn)以上的車(chē),至于其他不上檔次的車(chē)Hook就放行,交還給Windows自己的檢查站。同樣Hook可以“為所欲為”,可以擅自設(shè)立一個(gè)檢查站,也可以?xún)蓚€(gè),三個(gè)···換成程序來(lái)說(shuō),鉤子函數(shù)的工作原理是:當(dāng)我們創(chuàng)建一個(gè)鉤子時(shí),WINDOWS會(huì)先在內(nèi)存中創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)包含了鉤子的相關(guān)信息,然后把該結(jié)構(gòu)體加到已經(jīng)存在的鉤子鏈表中去。新的鉤子將加到老的前面。當(dāng)一個(gè)事件發(fā)生時(shí),如果我們安裝的是一個(gè)局部鉤子(下面有解釋,暫時(shí)理解為你程序本身中的),我們進(jìn)程中的鉤子函數(shù)將被調(diào)用。/* 鉤子鏈表和函數(shù) */
每一個(gè)Hook都有一個(gè)與之相關(guān)聯(lián)的指針列表,稱(chēng)之為鉤子鏈表,由系統(tǒng)來(lái)維護(hù)。被Hook子程調(diào)用的回調(diào)函數(shù),也就是該鉤子的各個(gè)處理子程。當(dāng)與指定的Hook類(lèi)型關(guān)聯(lián)的消息發(fā)生時(shí),系統(tǒng)就把這個(gè)消息傳遞到Hook子程。一些Hook子程可以只監(jiān)視消息,或者修改消息,或者停止消息的前進(jìn),避免這些消息傳遞到下一個(gè)Hook子程或者目的窗口。最近安裝的鉤子放在鏈的開(kāi)始,而最早安裝的鉤子放在最后,也就是后加入的先獲得控制權(quán)。鉤子子程是一個(gè)應(yīng)用程序定義的回調(diào)函數(shù)(CALLBACKFunction),不能定義成某個(gè)類(lèi)的成員函數(shù),只能定義為普通的C函數(shù)。用以監(jiān)視系統(tǒng)或某一特定類(lèi)型的事件,這些事件可以是與某一特定線程關(guān)聯(lián)的,也可以是系統(tǒng)中所有線程的事件。鉤子子程必須按照以下的語(yǔ)法:
LRESULT CALLBACK HookProc
(
? int nCode, //指定是否需要處理該消息?
WPARAM wParam,?
LPARAM lParam //包含該消息的附加消息 ,
);
這個(gè)回調(diào)函數(shù)的名字可以隨你取,但形式可一定要滿(mǎn)足以上要求,其實(shí)鉤子的回調(diào)函數(shù)和Windows的差不多一個(gè)德行。看看鉤子函數(shù)的返回值,若是返回非0值,表示我們已經(jīng)自己處理了該消息,則消息就不被傳遞到目標(biāo)窗口過(guò)程。
看看LRESULT CallNextHookEx
( HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;)這個(gè)函數(shù)把鉤子信息傳遞給下一個(gè)鉤子函數(shù),也就是可以理解成把車(chē)輛放行到下一個(gè)檢查站,這個(gè)可以根據(jù)自己的需要進(jìn)行調(diào)用。若是我們只設(shè)定了一個(gè)鉤子函數(shù),那么我們假設(shè)把鉤子消息用CallNextHookEx傳給下個(gè)鉤子函數(shù),因?yàn)椴淮嬖谒跃蛡鬟f回了目標(biāo)窗口函數(shù)。
/* 鉤子的安裝和釋放 */
調(diào)用SetWindowHookEx函數(shù),該函數(shù)的原型如下:HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId );返回值是一個(gè)hook的句柄。
idHook是我們感興趣的消息類(lèi)型,比如我們對(duì)鼠標(biāo)消息感興趣就是WH_MOUSE,再者比如鍵盤(pán)消息WH_KEYBOARD,我們可以通過(guò)查找Win32 API使用手冊(cè)來(lái)找到自己感興趣的消息。
第二個(gè)參數(shù)是鉤子函數(shù)的地址,這里就有兩種情況:其實(shí)鉤子有兩種,一種是局部鉤子,這種鉤子只能關(guān)注自己所在的進(jìn)程的事件,另一種鉤子叫做遠(yuǎn)程鉤子,這里又有兩種:1.基于線程的它將捕獲其它進(jìn)程中某一特定線程的事件。簡(jiǎn)言之,就是可以用來(lái)觀察其它進(jìn)程中的某一特定線程將發(fā)生的事件。2.系統(tǒng)范圍的 將捕捉系統(tǒng)中所有進(jìn)程將發(fā)生的事件消息。 看上去局部鉤子的功能沒(méi)有遠(yuǎn)程鉤子的給力,但是凡事都是要付出代價(jià)的,遠(yuǎn)程鉤子會(huì)影響系統(tǒng)的性能,特別是監(jiān)視系統(tǒng)范圍的鉤子,因?yàn)橐O(jiān)視系統(tǒng)范圍的消息,明顯就會(huì)影響系統(tǒng)的速度。
第三個(gè)參數(shù)和第四個(gè)參數(shù)相關(guān),所以一起解釋。
如果第四個(gè)參數(shù)是NULL,則說(shuō)明是全局鉤子,那么就是鉤子子程與所有的線程關(guān)聯(lián),此時(shí)第三個(gè)參數(shù)是程序?qū)嵗浔?#xff1b;
如果第三個(gè)參數(shù)是NULL,則說(shuō)明鉤子是局部鉤子,說(shuō)明子程代碼位于當(dāng)前進(jìn)程,這時(shí)候第四個(gè)參數(shù)就是當(dāng)前進(jìn)程的ID,可以用GetCurrentThreadID()填充,或者可以保存實(shí)例來(lái)填充,再做介紹···
再看看鉤子函數(shù)的卸載,用UnHookWindowsHookEx(HHOOK hhk);參數(shù)就是SetWindowsHookEx返回的句柄。
下面是是一個(gè)小程序,大概的功能就是實(shí)現(xiàn)在所在進(jìn)程內(nèi)的鼠標(biāo)消息和鍵盤(pán)消息的截獲,一旦點(diǎn)擊了“LockMouse”那么就在目標(biāo)窗口截獲了所以鼠標(biāo)的消息,這里實(shí)現(xiàn)的是屏蔽鼠標(biāo)消息,只能通過(guò)按回車(chē)鍵恢復(fù)鼠標(biāo)功能。若是點(diǎn)擊了“LockKeyBoard”按鈕,那么只能在編輯框輸入0或者1,但是在沒(méi)有點(diǎn)擊的情況下是正常的編輯框。這點(diǎn)可是比窗口子類(lèi)化更加簡(jiǎn)單。
總結(jié)
- 上一篇: MySQL如何查询两个日期之间的记录
- 下一篇: 170728、单例模式的三种水平代码