不知道大家是通過什麼開始對鉤子(Hook)有了解的,我是看過Jeffrey Richter的《WINDOWS
高級編程指南》(新版的中文譯名為《Windows核心編程》)。在這本書裡作者介紹了三種將代碼注入其他進程的方法,其中一種就是使用的全局消息鉤子。我就是從這本書裡對全局鉤子有了最初的認識。
大家應該都知道,全局消息鉤子要依賴於一個DLL才能夠正常工作。於是呢,我也就理所當在地認為全局鉤子都要依賴於一個DLL才能正常工作的,我想大部分人肯定和我一樣也這麼認為的。
但實際上不是這樣的。有某些全局鉤子可以不依賴於任何DLL而正常工作的。這些鉤子包括,WH_JOURNALPLAYBACK,WH_JOURNALRECORD,WH_KEYBOARD_LL,WH_MOUSE_LL。為什麼這些鉤子可以不依賴於DLL而正常工作呢?我們可以從MSDN中得到答案,MSDN中對於這四種鉤子都這樣的描述“This
hook is called in the context of the thread that installed it.”,翻譯成中文意思是鉤子函數的調用是在安裝鉤子的線程上下文中進行的。
說得更明白些,意思就是這些鉤子是在哪個線程當中安裝的,其鉤子函數就在哪個線程中執行。所以使用這四種鉤子是達不到代碼注入的效果的,當然也就可以不依賴於任何DLL了。MSDN中只對個別鉤子指出了必須還是沒有必要使用DLL。
下面是我給出的一個底層鍵盤鉤子的代碼示例,當然是不需要DLL的。
/*
kbhook.cpp
*/
#define _WIN32_WINNT 0400
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include
#include
#include
DWORD g_main_tid = 0;
HHOOK g_kb_hook = 0;
BOOL CALLBACK con_handler (DWORD)
{
PostThreadMessage (g_main_tid, WM_QUIT, 0, 0);
return TRUE;
};
LRESULT CALLBACK kb_proc (int code, WPARAM w, LPARAM l)
{
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;
const char *info = NULL;
if (w == WM_KEYDOWN)
info = "key dn";
else if (w == WM_KEYUP)
info = "key up";
else if (w == WM_SYSKEYDOWN)
info = "sys key dn";
else if (w == WM_SYSKEYUP)
info = "sys key up";
printf ("%s - vkCode [%04x], scanCode [%04x]\n",
info, p->vkCode, p->scanCode);
// always call next hook
return CallNextHookEx (g_kb_hook, code, w, l);
};
int main (void)
{
g_main_tid = GetCurrentThreadId ();
SetConsoleCtrlHandler (&con_handler, TRUE);
g_kb_hook = SetWindowsHookEx (
WH_KEYBOARD_LL,
&kb_proc,
GetModuleHandle (NULL), // 不能為NULL,否則失敗
0);
if (g_kb_hook == NULL)
{
fprintf (stderr,
"SetWindowsHookEx failed with error %d\n",
::GetLastError ());
return 0;
};
// 消息循環是必須的,想知道原因可以查msdn
MSG msg;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
};
UnhookWindowsHookEx (g_kb_hook);
return 0;
};