Featured image of post Hook

Hook

消息Hook

消息钩子是 Windows 操作系统中一种强大的消息拦截和处理机制,它允许应用程序在消息到达目标窗口过程之前监视、修改或拦截系统消息

消息处理流程键盘/鼠标事件 → 系统消息队列 → [HOOK处理] → 应用程序消息队列 → 窗口过程

详细流程:

  1. 硬件事件产生:用户操作(按键、鼠标点击等)
  2. 系统消息队列:事件被包装成消息加入系统队列
  3. 钩子处理:已安装的钩子函数处理消息
  4. 应用程序队列:处理后的消息发送到目标程序队列
  5. 窗口过程:程序通过消息循环处理消息

常用钩子类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 钩子类型常量
#define WH_KEYBOARD        2    // 键盘消息钩子
#define WH_MOUSE           7    // 鼠标消息钩子  
#define WH_GETMESSAGE      3    // 获取消息钩子
#define WH_CALLWNDPROC     4    // 窗口过程调用钩子
#define WH_KEYBOARD_LL    13    // 低级键盘钩子
#define WH_MOUSE_LL       14    // 低级鼠标钩子
#define WH_CBT             5    // 计算机基础训练钩子
#define WH_DEBUG           9    // 调试钩子
#define WH_JOURNALRECORD   0    // 日志记录钩子
#define WH_JOURNALPLAYBACK 1    // 日志回放钩子
#define WH_SHELL          10    // Shell钩子
#define WH_SYSMSGFILTER    6    // 系统消息过滤钩子

相关API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 设置Windows钩子
HHOOK SetWindowsHookEx(
    int       idHook,        // 钩子类型
    HOOKPROC  lpfn,          // 钩子过程函数指针
    HINSTANCE hMod,          // 包含钩子过程的DLL句柄
    DWORD     dwThreadId     // 线程ID (0表示系统全局钩子)
);

// 卸载Windows钩子
BOOL UnhookWindowsHookEx(
    HHOOK hhk    // 要卸载的钩子句柄
);

// 调用下一个钩子
LRESULT CallNextHookEx(
    HHOOK  hhk,    // 当前钩子句柄
    int    nCode,   // 钩子代码
    WPARAM wParam,  // 消息参数
    LPARAM lParam   // 消息参数
);

键盘记录器

hookDll.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include "pch.h"
#include "hookDll.h"
#include <stdio.h>
#include <string>

HINSTANCE g_hInstance = NULL;
HHOOK hhook = NULL;
#pragma data_seg(".shared")
HWND g_hMainWindow = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")

// 使用低级键盘钩子(WH_KEYBOARD_LL)
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        KBDLLHOOKSTRUCT* pKey = (KBDLLHOOKSTRUCT*)lParam;

        // 使用 OutputDebugString 替代 printf
        char debugMsg[256];
        sprintf_s(debugMsg, sizeof(debugMsg), "Key pressed: %d (0x%X)\n", pKey->vkCode, pKey->vkCode);
        OutputDebugStringA(debugMsg);

        // 发送消息到主窗口(如果存在)
        if (g_hMainWindow && IsWindow(g_hMainWindow)) {
            COPYDATASTRUCT cds;
            char data[64];
            sprintf_s(data, sizeof(data), "Key: %d", pKey->vkCode);
            cds.dwData = 1;
            cds.cbData = (DWORD)strlen(data) + 1;
            cds.lpData = data;
            SendMessage(g_hMainWindow, WM_COPYDATA, 0, (LPARAM)&cds);
        }

        // 屏蔽ESC键
        if (pKey->vkCode == VK_ESCAPE) {
            OutputDebugStringA("ESC key blocked!\n");
            return 1; // 阻止消息传递
        }
    }

    // 传递给下一个钩子
    return CallNextHookEx(hhook, nCode, wParam, lParam);
}

// 安装钩子
BOOL InstallHook() {
    // 使用 WH_KEYBOARD_LL 而不是 WH_KEYBOARD
    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, g_hInstance, 0);
    if (hhook) {
        OutputDebugStringA("Keyboard hook installed successfully!\n");
        return TRUE;
    }
    else {
        DWORD error = GetLastError();
        char errorMsg[256];
        sprintf_s(errorMsg, sizeof(errorMsg), "SetWindowsHookEx failed with error: %d\n", error);
        OutputDebugStringA(errorMsg);
        return FALSE;
    }
}

// 设置主窗口句柄用于通信
void SetMainWindow(HWND hWnd) {
    g_hMainWindow = hWnd;
}

// 卸载键盘钩子
void UninstallKeyboardHook() {
    if (hhook) {
        UnhookWindowsHookEx(hhook);
        hhook = NULL;
        OutputDebugStringA("Keyboard hook uninstalled!\n");
    }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (DLL_PROCESS_ATTACH == ul_reason_for_call) {
        g_hInstance = hModule;
        DisableThreadLibraryCalls(hModule); // 优化:禁用不必要的线程通知
    }
    return TRUE;
}

hookDll.h

1
2
3
4
5
6
#pragma once
#include <Windows.h>

extern "C" __declspec(dllexport) BOOL InstallHook();
extern "C" __declspec(dllexport) void UninstallKeyboardHook();
extern "C" __declspec(dllexport) void SetMainWindow(HWND hWnd);

Hook.cpp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <Windows.h>
#include <iostream>
#include <conio.h>

typedef BOOL(*InstallHook)();
typedef void(*UninstallKeyboardHook)();
typedef void(*SetMainWindow)(HWND);

// 用于接收钩子消息的隐藏窗口
HWND g_hWnd = NULL;
HHOOK g_hHook = NULL;

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_COPYDATA:
    {
        COPYDATASTRUCT* pCDS = (COPYDATASTRUCT*)lParam;
        if (pCDS->dwData == 1) {
            std::cout << "Hook DLL: " << (char*)pCDS->lpData << std::endl;
        }
    }
    break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// 创建隐藏窗口
BOOL CreateHiddenWindow() {
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = L"HookWindowClass";

    if (!RegisterClassEx(&wc)) {
        return FALSE;
    }

    g_hWnd = CreateWindowEx(
        0,
        L"HookWindowClass",
        L"Hook Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        100, 100,
        NULL, NULL,
        GetModuleHandle(NULL),
        NULL
    );

    return (g_hWnd != NULL);
}

int main() {
    // 创建隐藏窗口用于消息通信
    if (!CreateHiddenWindow()) {
        std::cout << "Failed to create message window!" << std::endl;
        return -1;
    }

    HMODULE hmodule = LoadLibraryA("hookDll.dll");
    if (!hmodule) {
        std::cout << "Failed to load hookDll.dll! Error: " << GetLastError() << std::endl;
        return -1;
    }

    InstallHook myInstallHook = (InstallHook)GetProcAddress(hmodule, "InstallHook");
    UninstallKeyboardHook myUninstallKeyboardHook = (UninstallKeyboardHook)GetProcAddress(hmodule, "UninstallKeyboardHook");
    SetMainWindow mySetMainWindow = (SetMainWindow)GetProcAddress(hmodule, "SetMainWindow");

    if (!myInstallHook || !myUninstallKeyboardHook || !mySetMainWindow) {
        std::cout << "Failed to get function addresses!" << std::endl;
        FreeLibrary(hmodule);
        return -1;
    }

    // 设置主窗口句柄用于通信
    mySetMainWindow(g_hWnd);

    if (myInstallHook()) {
        std::cout << "HOOK 成功!现在开始监控键盘输入..." << std::endl;
        std::cout << "按 ESC 键会被屏蔽" << std::endl;
        std::cout << "按 'Q' 键退出程序" << std::endl;

        // 消息循环
        MSG msg;
        while (true) {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                if (msg.message == WM_QUIT) {
                    break;
                }
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            // 检查键盘输入(用于退出)
            if (_kbhit()) {
                int ch = _getch();
                if (ch == 'q' || ch == 'Q') {
                    break;
                }
            }

            Sleep(10); // 避免占用太多CPU
        }
    }
    else {
        std::cout << "HOOK 失败!" << std::endl;
    }

    myUninstallKeyboardHook();
    FreeLibrary(hmodule);

    if (g_hWnd) {
        DestroyWindow(g_hWnd);
    }

    return 0;
}

IAT Hook

IAT(Import Address Table)Hook 是一种通过修改程序的导入地址表来拦截 API 调用的技术。这是 Windows 平台下最常见的 Hook 技术之一。

IAT Hook基本原理PE文件 → 导入表 → 导入描述符 → 原始FirstThunk → IAT → 实际函数地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "pch.h"
#include "iatHookDll.h"

DWORD* g_iatAddr = NULL;
DWORD* g_unHookAddr = NULL;
int WINAPI hookMessageBoxW(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType) {
    int result = MessageBoxA(0, "hook MessageBoxW", "提示", MB_OK);
    return result;
}
BOOL InstallHook() {
    DWORD dwOldProtect = 0;
    VirtualProtect(g_iatAddr, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    *g_iatAddr = (DWORD)hookMessageBoxW;
    VirtualProtect(g_iatAddr, 4, dwOldProtect, &dwOldProtect);
    return TRUE;
}
BOOL UninstallHook() {
    DWORD dwOldProtect = 0;
    VirtualProtect(g_iatAddr, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    *g_iatAddr = (DWORD)g_unHookAddr;
    VirtualProtect(g_iatAddr, 4, dwOldProtect, &dwOldProtect);
    return TRUE;
}
DWORD* GetIatAddr(const char* dllName, const char* dllFunName) {
    //获取当前进程exe文件模块句柄
    HMODULE hModule = GetModuleHandleA(0);
    DWORD dwhModule = (DWORD)hModule;
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwhModule;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + dwhModule);
    PIMAGE_OPTIONAL_HEADER pOptionHeader = &pNtHeader->OptionalHeader;
    IMAGE_DATA_DIRECTORY dataDirectory = pOptionHeader->DataDirectory[1];
    //获取导入表
    PIMAGE_IMPORT_DESCRIPTOR pImageIT = (PIMAGE_IMPORT_DESCRIPTOR)(dataDirectory.VirtualAddress + dwhModule);
    //遍历导入表获取符合条件的函数
    while (pImageIT->Name) {
        char* iatDllName = (char*)(pImageIT->Name + dwhModule);
        if (!_stricmp(iatDllName, dllName)) {   //不区分大小写
            PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)(pImageIT->OriginalFirstThunk + dwhModule);
            PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(pImageIT->FirstThunk + dwhModule);
            while (pINT->u1.Function) {
                if ((pINT->u1.Ordinal & 0x80000000) == 0) {
                    PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.Function + dwhModule);
                    if (!strcmp(pImportName->Name, dllFunName)) {
                        return (DWORD*)pIAT;
                    }
                }
                pINT++;
                pIAT++;
            }
        }
        pImageIT++;
    }
}
BOOL WINAPI DllMain(HMODULE hInstance, DWORD callReason, LPVOID lpReserved) {
    if (callReason == DLL_PROCESS_ATTACH) {
        MessageBoxA(0, "dll加载中", "提示", MB_OK);
        //获取iat表
        g_iatAddr = GetIatAddr("user32.dll", "MessageBoxW");
        //保存要Hook的函数地址
        g_unHookAddr = (DWORD*)*g_iatAddr;
        InstallHook();
    }
    else if (callReason == DLL_PROCESS_DETACH) {
        UninstallHook();
    }
    return TRUE;
}

不在iat表中的函数不能hook

Inline Hook

jmp -> E9

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
BOOL installHook(void* target_func, void* hook_func) {
    DWORD flOldProtect;
    BYTE jmp_code[14]; // 64位系统需要14字节跳转指令
    // 构造64位跳转指令: FF 25 00 00 00 00 JMP [RIP+0] + 8字节地址
    // 9727 = 0x25FF,对应 JMP [RIP+0] 指令
    *(DWORD*)&jmp_code[0] = 0x000025FF; // FF 25 00 00 (小端序)
    *(DWORD*)&jmp_code[4] = 0x00000000; // 00 00 00 00
    *(ULONG_PTR*)&jmp_code[6] = (ULONG_PTR)hook_func; // 8字节目标地址
    if (!VirtualProtect(target_func, 14, PAGE_EXECUTE_READWRITE, &flOldProtect)) {
        return FALSE;
    }
    memcpy(target_func, jmp_code, 14);
    VirtualProtect(target_func, 14, flOldProtect, &flOldProtect);
    return TRUE;
}
BOOL WINAPI inlHook() {
    HMODULE hModule;
    FARPROC ProcAddress;
    CHAR ModuleName[16];
    CHAR ProcName[12];
    // 获取模块句柄
    hModule = GetModuleHandleA("user32.dll");
    ProcAddress = GetProcAddress(hModule, "MessageBoxW");
    if (!installHook(ProcAddress, hookMessageBoxW)) {	//hookMessageBoxW函数的参数要和MessageBoxW一致
        return FALSE;
    }
    return TRUE;
}
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
人生若只如初见
使用 Hugo 构建
主题 StackJimmy 设计