作者:北大青鸟西安 | 来源:互联网 | 2023-07-13 12:25
全局钩子注入Windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数比如SetWindowsHookEx函数,根据不同的消息完成不同的功能。消息钩子是windo
全局钩子注入
Windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数比如SetWindowsHookEx
函数
,根据不同的消息完成不同的功能。
消息钩子是windows提供的一种消息过滤和预处理机制,可以用来截获和监视系统中的消息。
按照钩子作用范围不同,又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL文件中实现相应的钩子函数。
核心函数介绍
HHOOK WINAPI SetWindowsHookExW(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_opt_ HINSTANCE hmod,
_In_ DWORD dwThreadId);
1.idHook
要安装的钩子(HOOK)的类型,它决定了HOOKPROC
被调用的时机,可选参数如下。
WH_MSGFILTER = -1 |
线程级,截获用户与控件交互的消息 |
---|
WH_JOURNALRECORD = 0 |
系统级,记录所有消息队列送出的输入消息 |
WH_JOURNALPLAYBACK = 1 |
系统级,回放由WH_JOURNALRECORD记录的消息 |
WH_KEYBOARD = 2 |
系统级或线程级,截获键盘消息 |
WH_GETMESSAGE = 3 |
系统级或线程级,截获从消息队列送出的消息 |
WH_CALLWNDPROC = 4 |
系统级或线程级,截获发送到目标窗口的消息 |
WH_CBT = 5 |
系统级或线程级,截获系统基本消息 例如:窗口的创建,激活,关闭,最大/最小化,移动等 |
WH_SYSMSGFILTER = 6 |
系统级,截获系统范围内用户与控件交互的消息 |
WH_MOUSE = 7 |
系统级或线程级,截获鼠标消息 |
WH_HARDWARE = 8 |
系统级或线程级,截获非标准硬件(非鼠标,键盘)的消息 |
WH_DEBUG = 9 |
系统级或线程级,在其它钩子调用前调用,用于调试钩子 |
WH_SHELL = 10 |
系统级或线程级,截获发给外壳应用程序的消息 |
WH_FOREGROUNDIDLE = 11 |
系统级或线程级,在程序前台线程空闲时调用 |
WH_CALLWNDPROCRET = 12 |
系统级或线程级,截获目标窗口处理完的消息 在SendMessage被调用后发生 |
WH_KEYBOARD_LL = 13 |
系统级,截获全局键盘消息 |
WH_MOUSE_LL = 14 |
系统级,截获全局鼠标消息 |
2.lpfn
指向钩子回调函数的指针。如果最后一个参数dwThreadId
为0或者是其它进程创建的线程标识符,则lpfn
参数必须指向DLL中的钩子回调函数,即HOOKPROC
函数必须在DLL中实现。否则,lpfn
可以指向与当前进程相关联的代码中的钩子过程。
3.hmod
包含由lpfn
参数指向的钩子回调函数的DLL
句柄。如果dwThreadId参数指定由当前进程创建线程,并且钩子回调函数位于当前进程关联的代码中,则hmod
参数必须设置为NULL
。
4.dwThreadId
与钩子程序关联的线程标识符(指定要HOOK
的线程 ID)。如果此参数为0,则钩子过程与系统中所有线程相关联,即全局消息钩子
实现原理
通过SetWindowsHookExW
函数安装一个用于过滤特定类型消息的钩子函数(钩子WH_GETMESSAGE
可以立即被触发,而某些类型的钩子需要在处理指定类型的消息时才能被触发),当其它进程中产生了我们过滤的消息,就会调用HOOKPROC
,如果发现目标DLL
(HOOKPROC
实现的DLL
)尚未加载,就会使用KeUserModeCallback
函数回调User32.dll
的__ClientLoadLibrary()
函数,由User32.dll
把这个DLL
加载到目标进程中(低级键盘和鼠标钩子的加载有所不同),从而实现DLL
注入其它进程的目的。
代码及其实现过程
首先新建一个dll项目
在pch.h
中声明这几个我们定义的函数都是裸函数,由我们自己平衡堆栈
extern "C" _declspec(dllexport) int SetHook();
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
extern "C" _declspec(dllexport) BOOL UnsetHook();
在pch.cpp
里面写入三个函数并创建共享内存(进程通信的方法有很多,比如自定义消息、管道、dll共享节、共享内存等等,这里就用共享内存来实现进程通信)
#include "pch.h"
#include
#include
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 SetHook() {
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
if (NULL == g_hHook) {
return FALSE;
}
return TRUE;
}
//这里第二个参数是回调函数,那么我们还需要写一个回调函数的实现,这里就需要用到CallNextHookEx这个api,主要是第一个参数,这里传入钩子的句柄的话,就会把当前钩子传递给下一个钩子,若参数传入0则对钩子进行拦截
// 卸载钩子
BOOL UnsetHook() {
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);//UnhookWindowsHookEx这个api是来卸载钩子的
}
return TRUE;
}
在dllmain.cpp
设置DLL_PROCESS_ATTACH
,然后编译生成Dll1.dll
。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.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;
}
之后再创建一个控制台项目
用LoadLibrabryW
加载dll,生成inject2.cpp
文件
// inject2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
int main()
{
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("D:\\vs2017project\\Dll1\\Debug\\Dll1.dll"));
if (NULL == hDll)
{
printf("LoadLibrary Error[%d]\n", ::GetLastError());
break;
}
SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");
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, "UnsetHook");
if (NULL == UnsetGlobalHook)
{
printf("GetProcAddress Error[%d]\n", ::GetLastError());
break;
}
UnsetGlobalHook();
printf("UnsetGlobalHook OK.\n");
} while (FALSE);
system("pause");
return 0;
}
执行测试
编译inject2.cpp生成inject2.exe并运行
打开进程工具,搜索Dll1.dll是否加载,发现在大多数进程中都得到了加载
尝试卸载钩子
进程中的Dll1.dll已经被卸载