鈎子(hook)編程
鈎子(hook)編程
一、鈎子介紹
1.1鈎子的實現機制
鈎子英文名叫Hook,是一種截獲windows系統中某應用程序或者所有進程的消息的一種技術。下圖是windows應用程序傳遞消息的過程:
如在鍵盤中按下一鍵,操作系統將收到鍵按下消息,把消息放入消息隊列,然后消息隊列對消息進行派發,發給相應的應用程序,經過應用程序處理后發給操作系統,操作系統再調用相應的應用程序的創建的窗口過程。
我們可能通過鈎子截獲這些消息,讓消息不再往下傳遞,或者說截獲到感興趣的消息后做點什么。
1.2鈎子分類與實現
鈎子分進程內鈎子與全局鈎子,進程內鈎子是截取某一指定的進程的消息,直接在進程內創建與消除鈎子即可。全局鈎子是截取所有進程的消息,得以動態庫的方式實現。
實現一個鈎子一般有三個步驟,首先創建鈎子,有專門的API:SetWindowsHookEx,創建成功后,消息將會傳給SetWindowsHookEx的形參指定的處理函數。然后在消息處理函數中分析收到的消息,做相應的處理。最后,鈎子用完后,用API(UnhookWindowsHookEx)消毀鈎子。
二、創建鈎子
2.1鈎子的創建
SetWindowsHookEx安裝一個應用程序定義的鈎子過程,並把創建的鈎子過程放在鈎子鏈中,可以安裝多個鈎子,多個鈎子就形成了鈎子鏈,最后安裝的鈎子總是在最前面。創建鈎子的函數如下:
創建鈎子,返回鈎子句柄,否則返回NULL。形參定義如下:
idHook:鈎子過程類型,如:鼠標消息鈎子、鍵盤消息鈎子、消息隊列監控鈎子等等。具體取值如下:
lpfn:相應的鈎子過程,也就是一個處理消息的回調函數名而已,如果參數dwThreadId為0,或者dwThreadId指向的是其他進程創建的線程標志符,那么lpfn必須指向一個位於某一動態庫中的鈎子過程。其他情況下,lpfn可以指向本進程內的某一鈎子過程。
hMod:指向鈎子過程所在的應用程序實例句柄,如:鈎子過程所在的DLL的句柄。如果dwThreadId指定的線程是由當前進程創建,並且鈎子過程在當時進程中,那么hMod必須設置為NULL。
dwThreadId:指定與鈎子相關的線程標志。如果為0,那么鈎子將與桌面上運行的所有線程相關。
鈎子過程可以與特定線程相關也可以與所有線程相關,取決於dwThreadId的取值。
2.2鈎子過程分析
鈎子過程,是處理鈎子截取的消息的一個回調函數,即SetWindowsHookEx函數中的第二個形參。格式如下:
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);形參含義並不是都一樣的,不同鈎子過程形參表示的意義不一樣,我們以鼠標鈎子為例說明,參數含義如下:
nCode:指示鈎子過程如何處理當前的消息,如果鈎子是鼠標消息鈎子的話,有兩種含義:
wParam:指示鼠標消息標志。
lParam:指向MOUSEHOOKSTRUCT結構體指針。以下是MOUSEHOOKSTRUCT結構體,包含着鼠標消息。
typedef struct {
POINT pt;//光標包含x,y,是屏幕坐標。
HWND hwnd;//目標窗口句柄
UINT wHitTestCode;
ULONG_PTR dwExtraInfo;
} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;
通過這三個參數,可對消息進行分析處理。
其他鈎子過程參數的含義可以通過MSDN文檔查看詳細說明,如KeyboardProc、MessageProc等。
2.3消毀鈎子
BOOL UnhookWindowsHookEx(HHOOK hhk);此API的功能是把SetWindowsHookEx創建的鈎子從鈎子鏈中移除。形參是SetWindowsHookEx返回的鈎子句柄。
三、進程內鈎子
進程內鈎子一般是為了截獲當前應用程序的消息,一般會可以在應用程序初始化的時候創建,當然也可以在自己想創建的時候創建,只是創建之前的消息無法截獲。
3.1鼠標鈎子過程函數
以下為鼠標鈎子過程代碼:
HWND g_DestWndH = NULL;
HHOOK g_hHookDm = NULL;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{ //此區間內的消息都是鼠標消息
if (WM_MOUSEMOVE <= wParam && wParam <= WM_MOUSEWHEEL) {
if(g_DestWndH != NULL) {
::SendMessage(g_DestWndH, wParam, wParam, lParam);
//鼠標鈎子,lParam是MOUSEHOOKSTRUCT結構指針
PMOUSEHOOKSTRUCT lpMsg = (PMOUSEHOOKSTRUCT)lParam;
lpMsg->hwnd = NULL; //把目的窗口置NULL
lpMsg->dwExtraInfo = 0L;
lpMsg->pt = CPoint(0, 0);
lpMsg->wHitTestCode = 0L;
}
return 1;//如果想讓此消息不再往下傳,返回非0
}
else//不感興趣的消息往下傳
return ::CallNextHookEx(g_hHookDm, nCode, wParam, lParam);
}
3.2創建鈎子過程
以下為創建鈎子過程的代碼:
if(g_hHookDm == NULL) {
//如果dwThreadId指定的線程是由當前進程創建,並且鈎子過程在當時進程中,那么hMod必須設置為NULL。
g_hHookDm = ::SetWindowsHookEx(WH_MOUSE,MouseProc, NULL, GetCurrentThreadId());
if (NULL != g_hHookDm)//創建成功
g_DestWndH = m_hWnd;//保存目的窗口句柄
}
3.3消毀進程內鈎子
把鈎子過程從鈎子鏈中拿下來,調用一個API即可:
if (NULL != g_hHookDm) {
UnhookWindowsHookEx(g_hHookDm);
g_DestWndH = NULL;
}
到此進程內鈎子的創建與執行到消毀整個過程都完成了。
四、全局鈎子
4.1鍵盤鈎子過程函數
以上進程內鈎子只是截獲本進程的消息,如果要截獲桌面全部線程的消息則要通過全局鈎子。全局鈎子必須在DLL上實現,鈎子過程不能在本進程代碼中實現,所以先得寫一個DLL,代碼見”HookDll”,以鍵盤鈎子為例:
nCode:與鼠標鈎子是一樣的含義,有HC_ACTION與HC_NOREMOVE兩個值。
Param:代表具體的虛擬鍵,如:VK_TAB、VK_RETURN分別代表Tab鍵、Enter鍵按下,其他虛擬鍵消息可查看MSDN的”virtual-key code”.
lParam:存放一些擴展信息,如:按鍵重復數據、29位表示ALT鍵的按下情況。
參數的具體含義可查MSDN。關於組合鍵的問題,參看以下鍵盤鈎子函數的示例代碼:
HHOOK g_HookKeyBoard = NULL;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//wParam表示虛擬鍵,lparam第29位表示ALT鍵按下狀態(按下為1,否則為0),
//GetKeyState可以獲得對應鍵的狀態,所以收下表示Control + ALT + Z組合鍵按下
if ('Z' == wParam && GetKeyState(VK_CONTROL) < 0 && (lParam >> 29 & 1)) {
::SendMessage(g_DestWnd, WM_KEYDOWN, wParam, lParam);
return 1;
}
else
return CallNextHookEx(g_HookKeyBoard, nCode, wParam, lParam);
}
注:全局鈎子在某版本的系統中,調試狀態下,如果調試程序窗口沒有獲得焦點,在設置斷點的方不會停留,如果獲得焦點才會停留,也就是說,在調試狀態下,不屬於本進程的消息斷點處不會停留。
4.2創建全局鈎子接口
創建全局鈎子的方法在DLL中,所以得引出一個接口,代碼如下:
BOOL SetHook(HWND hWnd){
if (NULL == hWnd)
return FALSE;
g_DestWnd = hWnd;
//第三個參數可通過些方法獲得:GetModuleHandle("MouseHook.dll")
return (NULL != (g_HookKeyBoard = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInst, 0)));
}
其形參是一窗口句柄,指示截獲的興趣消息需傳向的窗口的句柄,其中MetWindowsHookE的第三個參數是DLL的應用程序實例句柄,可通過DllMain的傳入參數得到,亦可通過GetModuleHandle方法得到,第四個參數必須為0,才能獲得全部桌面線程的消息。成功將返回TRUE否則FALSE。
4.3消毀全局鈎子接口
在DLL中也得引出一接口消毀全局鈎子,代碼如下:
void DestroyHook(){
if (NULL != g_HookKeyBoard) {
UnhookWindowsHookEx(g_HookKeyBoard);
g_DestWnd = NULL;
}
}
4.4使用DLL創建全局鈎子
以下為創建與消毀全局鈎子的實現,代碼如下:
HINSTANCE hIst = NULL;
void CHookTestDlg::OnCreateDllHook()
{
hIst = ::LoadLibrary("..\\HookDll\\Debug\\HookDll.dll");
if (NULL != hIst) {
typedef BOOL (*pFunSetHook)(HWND);
pFunSetHook pSetHook = (pFunSetHook)GetProcAddress(hIst, "SetHook");
if (NULL != pSetHook) {
if(pSetHook(m_hWnd))
AfxMessageBox("創建全局鈎子成功...");
}
}
}
void CHookTestDlg::OnDestroyDllHook()
{
if (NULL != hIst) {
typedef void (*pFunDestroyHook)();
pFunDestroyHook pDestryHook = (pFunDestroyHook)GetProcAddress(hIst, "DestroyHook");
if (NULL != pDestryHook) {
pDestryHook();
::FreeLibrary(hIst);
hIst = NULL;
}
}
}
配套源碼鏈接:http://download.csdn.net/detail/mingojiang/4526390
轉載請注明出處:http://blog.csdn.net/mingojiang