不依赖于DLL就能工作的全局钩子
不知道大家是通过什么开始对钩子(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;
};
大家应该都知道,全局消息钩子要依赖于一个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;
};