描述
- 通过全局钩子监听windows消息,消息发生时,系统会将包含钩子回调函数的dll注入到所有进程中
准备知识
利用全局钩子进行dll注入
- windows的钩子机制用于监听系统消息并做出反应,分为局部钩子和全局钩子,局部钩子是针对某个线程的,全局钩子作用于整个系统基于消息的应用
- 全局钩子必须写在一个dll中,钩子安装成功后,一旦发生事件,会将包含钩子回调代码的dll加载到进程的内存空间中
dll的两种导出方式
- 利用def文件导出
- 利用declspec关键字导出
- 注意,如果dll不导出数据的话,生成时不会产生lib文件
全局/静态/外部变量和函数
- 全局变量和函数作用域是整个程序,在不同的cpp文件里定义同一个全局变量会报错冲突,写在函数外的变量默认是全局的
- 静态变量和函数作用域仅限当前cpp文件
- 外部变量和函数声明,表示当前变量和函数在别的cpp文件里被定义了,函数默认是外部的
调用其他文件中的函数和变量
- 有两种方式:头文件和extern
- 使用头文件调用,函数和变量必须在头文件中声明和定义
- 使用extern关键字调用,函数和变量在cpp或者c文件中声明和定义
预编译头文件
- 预编译头是程序设计时把头文件编译为中间格式(如目标文件),以节约在开发过程中编译器反复编译该头文件的开销
- 比如stdafx.h文件中,引用常用的windws.h等大量头文件,只用编译一次,后续编译使用已生成的目标文件
- 为了避免同一个头文件被包含(include)多次,C/C++中有两种宏实现方式:一种是#ifndef方式;另一种是#pragma once方式
- 可以在vs属性设置中选择使用/不使用编译头,dll工程默认是使用的
- 更多关于预编译头
typedef声明
- 可以为复杂声明定义一个简单的别名
- 记住两个模式
- typedef()() 函数指针
- typedef()[] 数组指针
- 例子
原声明:int *(*a[5])(int, char*); 变量名为a,直接用一个新别名pFun替换a就可以了: typedef int *(*pFun)(int, char*); 原声明的最简化版:pFun a[5];
代码
- 分为钩子程序和钩子安装程序
钩子程序
- 建立dll工程,取消pch预编译头,在dllMain.cpp中获取声明module全局变量并获取地址
- 再新建一个globalhook.cpp文件,导出钩子函数,并定义钩子回调函数
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;
}
- 关于如何导出dll函数,本文采用的是def导出,特别要注意def文件不是自己取名的,是要右键项目添加新项,选择代码中的模板定义文件def,然后在里面写入下面的代码
LIBRARY
EXPORTS
SetGlobalHook
UnsetGlobalHook
如果是自己创建文件然后取名def,则无法导出函数,下图中用dumpbin工具查看导出函数,上面那部分导出失败,下面部分导出成功
- 回调函数中用到钩子句柄g_hHook,因此需要将其从一个进程传递到其它进程,通过设置共享内存来完成值传递
#pragma data_seg("hookdata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:hookdata,RWS")
钩子安装程序
- loadlibrary加载钩子dll,获取导出函数的api,返回函数指针,然后解引用调用
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
typedef BOOL(*typedef_SetHook)();
typedef BOOL(*typedef_UnsetHook)();
HMODULE hDll = NULL;
typedef_SetHook SetGlobalHook = NULL;
typedef_UnsetHook UnsetGlobalHook = NULL;
BOOL bRet = FALSE;
do
{
hDll = ::LoadLibrary("GlobalHook.dll");
if (NULL == hDll)
{
printf("LoadLibrary Error[%d]\n", ::GetLastError());
break;
}
SetGlobalHook = (typedef_SetHook)::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_UnsetHook)::GetProcAddress(hDll, "UnsetGlobalHook");
if (NULL == UnsetGlobalHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
}
UnsetGlobalHook();
printf("UnsetGlobalHook OK.\n");
} while (FALSE);
system("pause");
return 0;
}
结果
- 找了台win10虚拟机做测试,安装钩子后,打开processexplorer查看进程加载dll情况,随便找一个procexp64.exe进程,可以看到加载了GlobalHook.dll,注入成功!
- 卸载钩子后,可以看到GlobalHook.dll成功释放