Hook是WINDOWS提供的一種消息處理機制,它使得程序員可以使用子過程來監視系統消息,並在消息到達目標過程前得到處理。
HOOK鏈
WINDOWS提供了幾種不同類型的HOOKS;不同的HOOK可以處理不同的消息。例如,WH_MOUSE HOOK用來監視鼠標消息。
WINDOWS為這幾種HOOKS維護著各自的HOOK鏈。HOOK鏈是一個由應用程序定義的回調函數隊列,當某種類型的消息發生時,WINDOWS向此種類型的HOOK鏈的第一個函數發送該消息,在第一函數處理完該消息後由該函數向鏈表中的下一個函數傳遞消息,依次向下。如果鏈中某個函數沒有向下傳送該消息,那麼鏈表中後面的函數將得不到此消息。(對於某些類型的HOOK,不管HOOK鏈中的函數是否向下傳遞消息,與此類型HOOK聯系的所有HOOK函數都會收到系統發送的消息)
HOOK過程
為了攔截特定的消息,你可以使用SetWindowsHookEx函數在該類型的HOOK鏈中安裝你自己的HOOK函數。
該函數MSDN中的描述
[html]
The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.
Syntax
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
Parameters
idHook
[in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
WH_CALLWNDPROC
Installs a hook procedure that monitors messages before the system sends them to the destination window procedure. For more information, see the CallWndProc hook procedure.
WH_CALLWNDPROCRET
Installs a hook procedure that monitors messages after they have been processed by the destination window procedure. For more information, see the CallWndRetProc hook procedure.
WH_CBT
Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. For more information, see the CBTProc hook procedure.
WH_DEBUG
Installs a hook procedure useful for debugging other hook procedures. For more information, see the DebugProc hook procedure.
WH_FOREGROUNDIDLE
Installs a hook procedure that will be called when the application's foreground thread is about to become idle. This hook is useful for performing low priority tasks during idle time. For more information, see the ForegroundIdleProc hook procedure.
WH_GETMESSAGE
Installs a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook procedure.
WH_JOURNALPLAYBACK
Installs a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook procedure. For more information, see the JournalPlaybackProc hook procedure.
WH_JOURNALRECORD
Installs a hook procedure that records input messages posted to the system message queue. This hook is useful for recording macros. For more information, see the JournalRecordProc hook procedure.
WH_KEYBOARD
Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure.
WH_KEYBOARD_LL
Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard input events. For more information, see the LowLevelKeyboardProc hook procedure.
WH_MOUSE
Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
WH_MOUSE_LL
Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events. For more information, see the LowLevelMouseProc hook procedure.
WH_MSGFILTER
Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. For more information, see the MessageProc hook procedure.
WH_SHELL
Installs a hook procedure that receives notifications useful to shell applications. For more information, see the ShellProc hook procedure.
WH_SYSMSGFILTER
Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. The hook procedure monitors these messages for all applications in the same desktop as the calling thread. For more information, see the SysMsgProc hook procedure.
lpfn
[in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
hMod
[in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
dwThreadId
[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.
Return Value
If the function succeeds, the return value is the handle to the hook procedure.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.
Syntax
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
Parameters
idHook
[in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
WH_CALLWNDPROC
Installs a hook procedure that monitors messages before the system sends them to the destination window procedure. For more information, see the CallWndProc hook procedure.
WH_CALLWNDPROCRET
Installs a hook procedure that monitors messages after they have been processed by the destination window procedure. For more information, see the CallWndRetProc hook procedure.
WH_CBT
Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. For more information, see the CBTProc hook procedure.
WH_DEBUG
Installs a hook procedure useful for debugging other hook procedures. For more information, see the DebugProc hook procedure.
WH_FOREGROUNDIDLE
Installs a hook procedure that will be called when the application's foreground thread is about to become idle. This hook is useful for performing low priority tasks during idle time. For more information, see the ForegroundIdleProc hook procedure.
WH_GETMESSAGE
Installs a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook procedure.
WH_JOURNALPLAYBACK
Installs a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook procedure. For more information, see the JournalPlaybackProc hook procedure.
WH_JOURNALRECORD
Installs a hook procedure that records input messages posted to the system message queue. This hook is useful for recording macros. For more information, see the JournalRecordProc hook procedure.
WH_KEYBOARD
Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure.
WH_KEYBOARD_LL
Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard input events. For more information, see the LowLevelKeyboardProc hook procedure.
WH_MOUSE
Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
WH_MOUSE_LL
Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events. For more information, see the LowLevelMouseProc hook procedure.
WH_MSGFILTER
Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. For more information, see the MessageProc hook procedure.
WH_SHELL
Installs a hook procedure that receives notifications useful to shell applications. For more information, see the ShellProc hook procedure.
WH_SYSMSGFILTER
Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu, or scroll bar. The hook procedure monitors these messages for all applications in the same desktop as the calling thread. For more information, see the SysMsgProc hook procedure.
lpfn
[in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
hMod
[in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
dwThreadId
[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.
Return Value
If the function succeeds, the return value is the handle to the hook procedure.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
這裡我來簡單的解釋一下:
函數的功能描述是:SetWindowsHookEx函數安裝一個程序自定義的鉤子過程到鉤子鏈。你可以安裝一個鉤子過程來監視系統的特定消息。可以監視某一個特定的線程的消息,或者是與這個消息相關的當前運行的所有線程。
函數一共有四個參數
參數1:idHook
定義了鉤子過程的類型,也就是這個鉤子將要監視哪種消息
參數2:lpfn
它是一個鉤子過程的指針。如果dwThreadId也就是第四個參數的值是0或者是由其它進程創建的線程的ID,那麼這個參數必須是指向一個位於DLL中的鉤子過程的指針,否則這個指針可以是當前進程代碼空間中定義的一個鉤子過程的地址。
參數3:hMod
是一個包含了lpfn所指向的鉤子過程的DLL的句柄。如果第四個參數線程ID的值是由當前進程創建的線程的ID值並且鉤子過程是在當前進程中定義的,那麼這個參數必須設置成NULL。
參數4:dwThreadId
指明鉤子過程要監視的線程ID。如果是0那麼鉤子過程將監視所有桌面運行的線程。
返回值:
成功返回鉤子過程的句柄。
失敗返回NULL,獲得額外信息,調用GetLastError
lpfn指向鉤子回調函數的函數地址,這個函數用來對鉤子檢測到的消息進行處理。MSDN中函數的原型如下,這裡以鍵盤檢測函數的原型為例:
The KeyboardProc hook procedure is an application-defined or library-defined callback function used with theSetWindowsHookEx function. The system calls this function whenever an application calls the GetMessage orPeekMessage function and there is a keyboard message (WM_KEYUP or WM_KEYDOWN) to be processed.
The HOOKPROC type defines a pointer to this callback function. KeyboardProc is a placeholder for the application-defined or library-defined function name.
Syntax
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);Parameters
code
[in] Specifies a code the hook procedure uses to determine how to process the message. If code is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx. This parameter can be one of the following values.
HC_ACTION
The wParam and lParam parameters contain information about a keystroke message.
HC_NOREMOVE
The wParam and lParam parameters contain information about a keystroke message, and the keystroke message has not been removed from the message queue. (An application called thePeekMessage function, specifying the PM_NOREMOVE flag.)
wParam
[in] Specifies the virtual-key code of the key that generated the keystroke message.
lParam
[in] Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. For more information about the lParam parameter, see Keystroke Message Flags. This parameter can be one or more of the following values.
0-15
Specifies the repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
16-23
Specifies the scan code. The value depends on the OEM.
24
Specifies whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
25-28
Reserved.
29
Specifies the context code. The value is 1 if the ALT key is down; otherwise, it is 0.
30
Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
31
Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
Return Value
If code is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If code is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.
Remarks
An application installs the hook procedure by specifying the WH_KEYBOARD hook type and a pointer to the hook procedure in a call to the SetWindowsHookEx function.
keyboardProc鉤子過程是定義在應用程序中或是庫文件中的回調函數,它有SetWindowsHookEx來使用。當應用程序中調用了GetMessage或是PeekMessage方法時並且處理的是鍵盤消息(WM_KEYUP或WM_KEYDOWN)HOOKPROC類型定義了一個指向回調函數的指針。從原型定義中我們可以看出:typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);
它其實就是在WinUser.h中定義的一個函數指針
一個簡單的回調函數的例子如下(鍵盤鉤子為例)
[cpp]
// 鉤子回調函數(鉤子過程)
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0 || nCode == HC_NOREMOVE)
//向下傳遞消息使得hook鏈上的其他函數可以處理該消息
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
if (lParam & 0x40000000) // Check the previous key state
{
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//將自定義消息發送到主窗口
//wParam 定義了虛擬鍵代碼
//lParam 定義了按鍵數據
::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
// 鉤子回調函數(鉤子過程)
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0 || nCode == HC_NOREMOVE)
//向下傳遞消息使得hook鏈上的其他函數可以處理該消息
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
if (lParam & 0x40000000) // Check the previous key state
{
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//將自定義消息發送到主窗口
//wParam 定義了虛擬鍵代碼
//lParam 定義了按鍵數據
::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}上面PostMessage函數中的g_hWnd是主窗口的句柄,就是我們用來處理檢測到的消息的那個窗口。
WM_KEYSTROKE是我們自己定義的一個消息宏,當鉤子檢測到消息後,就將這個消息發送到主窗口來進行處理。
#define WM_KEYSTROKE (WM_USER + 101)
我們定義了下面一個函數來安裝或卸載一個消息鉤子。
[cpp]
//安裝或者卸載鉤子的函數
BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
BOOL bOk;
g_hWnd = hWndCaller;
if (bInstall)
{
g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), dwThreadId);//安裝消息鉤子
bOk = (g_hHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
return bOk;
}
//安裝或者卸載鉤子的函數
BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
BOOL bOk;
g_hWnd = hWndCaller;
if (bInstall)
{
g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), dwThreadId);//安裝消息鉤子
bOk = (g_hHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
return bOk;
}ModuleFromAddress函數幫助我們獲得模塊地址:
[cpp]
HMODULE WINAPI ModuleFromAddress(PVOID pv)
{
MEMORY_BASIC_INFORMATION mbi;
if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
{
return (HMODULE)mbi.AllocationBase;
}
else
{
return NULL;
}
}
HMODULE WINAPI ModuleFromAddress(PVOID pv)
{
MEMORY_BASIC_INFORMATION mbi;
if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
{
return (HMODULE)mbi.AllocationBase;
}
else
{
return NULL;
}
}由於上面我們定義的消息鉤子的回調函數是和安裝鉤子的程序代碼定義在一起的,而不是定義在外部DLL中,所以這個鉤子只能監視本線程的消息,而不是一個全局消息鉤子。要想實現能對所有程序的鍵盤消息進行檢測,必須將消息鉤子的回調函數定義在DLL模塊中。
DLL中的定義如下:
.h
[cpp]
#ifdef HOOKDLL_EXPORTS
#define HOOKDLL_API __declspec(dllexport)
#else
#define HOOKDLL_API __declspec(dllimport)
#endif
//自定義消息,當鍵盤按鍵按下和釋放時發送消息
#define WM_KEYSTROKE (WM_USER + 101)
#define WM_KEYINPUT (WM_USER + 102)
//導出函數
BOOL HOOKDLL_API WINAPI SetKeyboardHook(BOOL bInstall,
DWORD dwThreadId = 0,
HWND hWndCaller = NULL);
#ifdef HOOKDLL_EXPORTS
#define HOOKDLL_API __declspec(dllexport)
#else
#define HOOKDLL_API __declspec(dllimport)
#endif
//自定義消息,當鍵盤按鍵按下和釋放時發送消息
#define WM_KEYSTROKE (WM_USER + 101)
#define WM_KEYINPUT (WM_USER + 102)
//導出函數
BOOL HOOKDLL_API WINAPI SetKeyboardHook(BOOL bInstall,
DWORD dwThreadId = 0,
HWND hWndCaller = NULL);
.cpp
[cpp]
#include <windows.h>
#define HOOKDLL_EXPORTS
#include "HookDll.h"
// Shared data among all instances.
#pragma data_seg(".HOOKDATA")
HWND g_hWnd = NULL; // Window handle
HHOOK g_hHook = NULL; // Hook handle
// Get module from address
HMODULE WINAPI ModuleFromAddress(PVOID pv)
{
MEMORY_BASIC_INFORMATION mbi;
if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
{
return (HMODULE)mbi.AllocationBase;
}
else
{
return NULL;
}
}
// 鉤子回調函數(鉤子過程)
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0 || nCode == HC_NOREMOVE)
//向下傳遞消息使得hook鏈上的其他函數可以處理該消息
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
if (lParam & 0x40000000) // Check the previous key state
{
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//將自定義消息發送到主窗口
//wParam 定義了虛擬鍵代碼
//lParam 定義了按鍵數據
::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//安裝或者卸載鉤子的函數
BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
BOOL bOk;
g_hWnd = hWndCaller;
if (bInstall)
{
g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), dwThreadId);//安裝消息鉤子
bOk = (g_hHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
return bOk;
}
#include <windows.h>
#define HOOKDLL_EXPORTS
#include "HookDll.h"
// Shared data among all instances.
#pragma data_seg(".HOOKDATA")
HWND g_hWnd = NULL; // Window handle
HHOOK g_hHook = NULL; // Hook handle
// Get module from address
HMODULE WINAPI ModuleFromAddress(PVOID pv)
{
MEMORY_BASIC_INFORMATION mbi;
if (::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
{
return (HMODULE)mbi.AllocationBase;
}
else
{
return NULL;
}
}
// 鉤子回調函數(鉤子過程)
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0 || nCode == HC_NOREMOVE)
//向下傳遞消息使得hook鏈上的其他函數可以處理該消息
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
if (lParam & 0x40000000) // Check the previous key state
{
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//將自定義消息發送到主窗口
//wParam 定義了虛擬鍵代碼
//lParam 定義了按鍵數據
::PostMessage(g_hWnd, WM_KEYSTROKE, wParam, lParam);
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//安裝或者卸載鉤子的函數
BOOL WINAPI SetKeyboardHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
BOOL bOk;
g_hWnd = hWndCaller;
if (bInstall)
{
g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), dwThreadId);//安裝消息鉤子
bOk = (g_hHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
return bOk;
}.def
[cpp]
LIBRARY CppHooKDll
EXPORTS
SetKeyboardHook
SECTIONS
.HOOKDATA Read Write Shared
LIBRARY CppHooKDll
EXPORTS
SetKeyboardHook
SECTIONS
.HOOKDATA Read Write Shared
在需要安裝全局鉤子的程序中調用:
[cpp]
::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), 0)
::SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,
ModuleFromAddress(KeyboardHookProc), 0)