鉤子(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