在 Windows 中大部分的应用程序都是基于消息机制的,它们都有一个过程函数,根据不同的消息完成不同的功能。
Windows 操作系统提供的钩子机制就是用来截获和监视系统中这些消息的。
按照钩子作用的范围不同,它们又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子则是作用于整个系统的基于消息的应用。
全局钩子需要使用 DLL 文件,在 DLL 中实现相应的钩子函数。
如果创建的是全局钩子,那么钩子函数必须在一个 DLL 中。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。
如果钩子函数是实现代码在 DLL 中,则在对应事件发生时,系统会把这个 DLL 加载到发生事件的进程地址空间中,使它能够调用钩子函数进行处理。
为了能够让 DLL 注入到所有的进程中,程序设置 WH_GETMESSAGE 消息的全局钩子。因为 WH_GETMESSAGE 类型的钩子会监视消息队列,并且 Windows 系统是基于消息驱动的,所以所有进程都会有自己的一个消息队列,都会加载 WH_GETMESSAGE 类型的全局钩子 DLL。
// 设置全局钩子
BOOL SetGlobalHook()
{
g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
if (NULL == g_hHook)
{
return FALSE;
}
return TRUE;
}
// 钩子回调函数
LRESULT GetMsgProc(
int code,
WPARAM wParam,
LPARAM lParam)
{
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
// 卸载钩子
BOOL UnsetGlobalHook()
{
if (g_hHook)
{
::UnhookWindowsHookEx(g_hHook);
}
return TRUE;
}
上面介绍了全局钩子的设置、钩子回调函数的实现以及全局钩子的卸载,这些操作都需要用到全局钩子的句柄作为参数。而全局钩子是以 DLL 形式加载到其他进程空间中的,而且进程都是独立的,所以任意修改其中一个内存里的数据是不会影响另一个进程的。
那么,如何将钩子句柄传递给其他进程呢?方法是在 DLL 中创建共享内存。
共享内存是指突破进程独立性,多个进程共享同一段内存。在 DLL 中创建共享内存,就是在 DLL 中创建一个变量,然后将 DLL 加载到多个进程空间,只要一个进程修改了该变量值,其他进程 DLL 中的这个值也会改变,就相当于多个进程共享一个内存。
共享内存的实现有的是比较简单,首先为 DLL 创建一个数据段,然后再对程序的链接器进行设置,把指定的数据段链接为共享数据段。这样,就可以成功地创建共享内存了。
// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")
// GlobalHook_Test.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
extern HMODULE g_hDllModule;
// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")
// 钩子回调函数
LRESULT GetMsgProc(
int code,
WPARAM wParam,
LPARAM lParam)
{
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
// 设置全局钩子
BOOL SetGlobalHook()
{
g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
if (NULL == g_hHook)
{
return FALSE;
}
return TRUE;
}
// 卸载钩子
BOOL UnsetGlobalHook()
{
if (g_hHook)
{
::UnhookWindowsHookEx(g_hHook);
}
return TRUE;
}
#include "stdafx.h"
#include
int _tmain(int argc, _TCHAR* argv[])
{
typedef BOOL(*typedef_SetGlobalHook)();
typedef BOOL(*typedef_UnsetGlobalHook)();
HMODULE hDll = NULL;
typedef_SetGlobalHook SetGlobalHook = NULL;
typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
BOOL bRet = FALSE;
do
{
hDll = ::LoadLibrary("GlobalHook_Test.dll");
if (NULL == hDll)
{
printf("LoadLibrary Error[%d]\n", ::GetLastError());
break;
}
SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetGlobalHook");
if (NULL == SetGlobalHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
break;
}
bRet = SetGlobalHook();
if (bRet)
{
printf("SetGlobalHook OK.\n");
}
else
{
printf("SetGlobalHook ERROR.\n");
}
system("pause");
UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetGlobalHook");
if (NULL == UnsetGlobalHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
break;
}
UnsetGlobalHook();
printf("UnsetGlobalHook OK.\n");
}while(FALSE);
system("pause");
return 0;
}