You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于在GDI的CreatePen函数地址处挂载跳转钩子的代码咨询

函数指针Hook的Zig代码修正与风险说明

核心问题分析

你的代码目标是替换内存中指定地址的函数指针,将其指向自定义钩子函数,同时保留原函数调用逻辑。当前Zig代码存在多处语法和逻辑错误,以下是具体问题和修正方案:

1. 指针类型错误

原代码中FunctionAddress被声明为*u32,但x64系统下函数指针是8字节(u64),应改为*volatile u64(volatile防止编译器优化读写操作)。

2. 错误的跳转指令写法

原代码试图用[2]u8存储跳转指令,但实际上我们是直接替换函数指针的值,不需要机器码指令——直接将钩子函数的地址写入目标内存地址即可。

3. DllMain返回类型错误

Windows的DllMain要求返回BOOL(即c_int),而非Zig的错误联合类型!bool。

4. WriteFile参数不完整

WriteFile需要指定写入字节数,且应使用标准常量STD_OUTPUT_HANDLE而非硬编码的-11。

5. 未保存原函数指针的正确方式

原代码中ActualFunction的赋值方式错误,应先读取目标地址的原始函数指针,再替换为钩子地址。


修正后的Zig代码

const std = @import("std");
const windows = std.os.windows;

// 目标地址:存储CreatePen函数指针的内存位置
const FunctionAddress: *volatile u64 = @ptrFromInt(0x142c46108);
// 原函数指针,后续在DllMain中初始化
var ActualFunction: fn(c_int, c_int, windows.DWORD) ?anyopaque = undefined;

pub export fn DllMain(handle: ?anyopaque, reason: windows.DWORD, reserved: ?anyopaque) callconv(windows.WINAPI) windows.BOOL {
    if (reason != windows.DLL_PROCESS_ATTACH) {
        return windows.TRUE;
    }

    // 1. 保存原函数指针
    ActualFunction = @ptrFromInt(FunctionAddress.*);

    // 2. 获取钩子函数地址
    const hook_address = windows.kernel32.GetProcAddress(handle, "CreatePenHook") orelse {
        std.log.err("Failed to get CreatePenHook address", .{});
        return windows.FALSE;
    };
    const hook_address_int: u64 = @intFromPtr(hook_address);

    // 3. 修改内存保护为可读写
    var old_protect: windows.DWORD = undefined;
    const protect_success = windows.kernel32.VirtualProtect(
        FunctionAddress,
        @sizeOf(u64),
        windows.PAGE_EXECUTE_READWRITE,
        &old_protect
    );
    if (protect_success == 0) {
        std.log.err("VirtualProtect failed: {d}", .{windows.kernel32.GetLastError()});
        return windows.FALSE;
    }

    // 4. 写入钩子函数地址到目标指针位置
    FunctionAddress.* = hook_address_int;

    // 5. 恢复原内存保护(可选但推荐)
    _ = windows.kernel32.VirtualProtect(
        FunctionAddress,
        @sizeOf(u64),
        old_protect,
        &old_protect
    );

    return windows.TRUE;
}

pub export fn CreatePenHook(arg1: c_int, arg2: c_int, arg3: windows.DWORD) callconv(windows.WINAPI) ?anyopaque {
    // 输出日志到控制台
    const msg = "Created Pen\n";
    const stdout_handle = windows.kernel32.GetStdHandle(windows.STD_OUTPUT_HANDLE) orelse {
        std.log.err("Failed to get stdout handle", .{});
        return ActualFunction(arg1, arg2, arg3);
    };
    var bytes_written: windows.DWORD = undefined;
    _ = windows.kernel32.WriteFile(
        stdout_handle,
        msg.ptr,
        @intCast(windows.DWORD, msg.len),
        &bytes_written,
        null
    );

    // 调用原函数
    return ActualFunction(arg1, arg2, arg3);
}

关键风险提示

  1. 目标地址有效性:必须确保0x142c46108确实是存储CreatePen函数指针的有效地址,若地址错误会导致程序崩溃或非法内存访问。
  2. 内存保护权限:若目标地址所在内存页无法修改(如系统只读区域),VirtualProtect会失败,需确认该地址的可写性。
  3. 线程安全:若多个线程同时访问该函数指针,替换操作可能导致竞态条件,建议添加同步机制(如临界区)。
  4. 原函数调用一致性:确保钩子函数的参数、调用约定(WINAPI即stdcall)与原函数完全一致,否则会导致栈损坏。
  5. DllMain限制:在DllMain中避免执行复杂操作(如控制台输出),部分场景下可能导致死锁,建议将钩子初始化移至专门的导出函数。

内容的提问来源于stack exchange,提问作者Nazar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:17:33