第三章 注入技术---全局钩子注入
一、全局钩子注入
windows中的大部分应用程序都是基于消息机制的,根据不同的消息完成不同的功能。 钩子机制就是由windows操作系统提供的可以用来截获和监视系统中这些消息的。
钩子又可以分为局部钩子和全局钩子。其中局部钩子是针对某个线程的,全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL中实现相应的钩子函数。
二、API介绍
SetWindowsHookEx函数
https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa?redirectedfrom=MSDN
HHOOK WINAPI SetWindowsHookEX(
_In_ int idHook, //要安装的钩子程序的类型
_In_ HOOKPROC lpfn, //一个指向钩子程序的指针,如果dwThreadId=0或者指定不同进程创建线程,则
//lpfn必须指向DLL中的钩子过程,否则lpfn可以指向与当前进程关联的代码函数
_In_ HINSANCE hMod, //包含由lpfn参数指向的函数的DLL句柄,如果是当前进程则hMod必须为NULL
_In_ DWORD dwThreadId) //与钩子程序关联的线程标识符。如果为0,则与所有系统中的线程相关联
函数成功返回钩子函数的句柄,失败返回NULL
三、实现过程
如果创建的是全局钩子,那么钩子函数必须在一个DLL中。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在DLL中,则在对应事件发生时,系统会把这个DLL加载到发生事件的进程地址空间中,使它能够调用钩子函数进行处理。
因为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;
在上述代码中,SetWindowsHookEx的第一个参数表示钩子的类型,WH_GETMESSAGE表示安装消息队列的消息钩子,它可以监视发送到消息队列的消息。第二个参数表示钩子回调函数。第三个表示钩子回调函数的DLL模块句柄。第四个参数表示与钩子关联的线程ID,0表示全局钩子,它关联所有线程。
//钩子回调函数
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
上述回调函数的参数和返回值的类型是固定的。其中,CallNextHookEx函数表示将当前钩子传递给钩子链中的下一个钩子,第一个参数要指定当前钩子的句柄。如果直接返回0,则表示中断钩子传递,对钩子进行拦截。
//卸载钩子
BOOL UnsetGlobalHook() {
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);
}
return TRUE;
}
卸载成功后,所有加载了全局钩子DLL模块的进程,都会释放该DLL模块。
全局钩子是以DLL形式加载到其他进程空间中的,而且进程都是独立的,所以任意修改其中一个内存里的数据是不会影响另一个进程的。那么,如何将钩子句柄传递给其他进程呢?为了解决这个问题,本节采用的方式是在DLL中,创建共享内存。
共享内存是指突破进程独立性,多个进程共享同一段内存。在DLL中创建共享内存,就是在DLL中创建一个变量,然后将DLL加载到多个进程空间,只要一个进程修改了该变量值,其他进程DLL中的这个值也会改变,就相当于多个进程共享一个内存。
共享内存的实现原理
首先为DLL创建一个数据段,然后再对程序的链接器进行设置,把指定的数据段链接为共享数据段。这里就可以成功地创建共享内存了。
//共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker,"/SECTION:mydata,RWS")
在上面的代码中,使用#pragma data_seg创建了一个名为"mydata"的数据段,然后使用/SECTION:mydata,RWS把mydata数据段设置为可读。可写、可共享的共享数据段。
四、实现代码如下
001项目,生成001.dll
dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "framework.h"
HMODULE g_hDllModule = NULL;
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_hDllModule = hModule;
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
export.cpp
#include "framework.h"
extern HMODULE g_hDllModule;
// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")
extern "C" {
__declspec(dllexport) LRESULT GetMsgProc(int code,WPARAM wParam,LPARAM lParam);
__declspec(dllexport) BOOL SetGlobalHook();
__declspec(dllexport) BOOL UnsetGlobalHook();
}
// 钩子回调函数
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;
}
002项目,实现全局dll注入
002.cpp
#include <Windows.h>
#include<tchar.h>
#include<stdio.h>
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 = LoadLibraryW(TEXT("001.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;
}
五、小结
主要通过调用SetWindowsHookEx函数设置全局钩子,完成DLL注入。通过调用CallNextHookEx函数传递钩子,使得进程继续运行。通过调用UnhookWindowsHookEx函数卸载钩子,实现DLL释放。
在调用SetWindowsHookEx函数设置全局钩子的时候,一定要将钩子回到函数编写在DLL模块中,并指定该DLL模块的句柄。
在DLL中利用#pragma data_seg指令创建共享内存,加载该DLL的进程,共享内存。只要一个进程修改了内存中的数据,则其他数据对应内存的数据也会改变。

浙公网安备 33010602011771号