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

如何向64位应用注入指向32位地址的CodeCave并使用C#实现

案例实现原理

你看到的案例是64位进程下跳转到低32位地址CodeCave的标准实现,核心逻辑是用无偏移限制的64位绝对间接跳转突破普通E9相对跳转的2GB偏移限制,指向预先分配在低4GB地址空间的CodeCave:

  • 原程序位置的E9 FE59CA82是5字节相对近跳转,跳转目标7FF748DA0000和原指令地址的差在2GB以内,符合E9跳转的寻址范围。
  • 7FF748DA0000位置的FF25 00000000是64位下的jmp qword ptr [rip]指令:rip寄存器的值是当前指令的下一条地址,也就是后面8字节的存储位置,CPU会直接读取这8字节作为绝对跳转地址。案例里这8字节的值是0x00000000051A0000,高32位全0,属于低32位地址范围,所以最终跳转到你要的32位地址CodeCave。
  • 64位Windows的虚拟地址空间默认保留了低4GB的地址区,只要把CodeCave分配在这个区域,地址就可以用32位数值表示,满足你的需求。

C#实现步骤

前置依赖

你需要调用Windows原生API完成进程内存操作,先声明必要的API和常量:

using System.Runtime.InteropServices;

public static class Win32Api
{
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out uint lpNumberOfBytesWritten);

    [DllImport("kernel32.dll")]
    public static extern bool CloseHandle(IntPtr hObject);

    // 常量定义
    public const uint PROCESS_ALL_ACCESS = 0x1F0FFF;
    public const uint MEM_COMMIT = 0x1000;
    public const uint MEM_RESERVE = 0x2000;
    public const uint MEM_32BIT = 0x40000; // 强制分配在低32位地址空间
    public const uint PAGE_EXECUTE_READWRITE = 0x40;
}

完整流程

  • 第一步:获取目标64位进程的句柄,传入目标进程PID调用OpenProcess获取足够的内存操作权限。
  • 第二步:分配32位地址的CodeCave内存,调用VirtualAllocEx时指定MEM_32BIT标志,分配的内存地址会自动落在低4GB范围内,权限设置为可执行可读写。
  • 第三步:写入你的自定义Shellcode到刚才分配的32位地址内存中,注意Shellcode必须是64位汇编代码,仅存储地址是32位即可。
  • 第四步:构造跳板跳转指令:0xFF 0x25 0x00 0x00 0x00 0x00 + 8字节的CodeCave绝对地址(低4字节是你的32位地址,高4字节补0),总共14字节,写入目标进程内的跳板地址(需要提前找到目标进程中可写入的空闲内存位置)。
  • 第五步:修改原程序要注入的位置,写入5字节的E9相对跳转指令,计算偏移规则为:偏移 = 跳板地址 - (注入点地址 + 5),如果原位置长度超过5字节,多余的字节用0x90(nop指令)填充。
  • 第六步:如果需要回到原程序执行逻辑,在Shellcode末尾用相同的跳转逻辑跳回原程序注入点的下一条指令即可。

注意事项

  • 如果MEM_32BIT标志分配内存失败,可以手动遍历0x10000到0x7FFF0000的地址范围,找到未分配的地址手动指定lpAddress参数调用VirtualAllocEx分配。
  • 注入前要确认原程序注入点的字节长度足够容纳5字节的E9跳转,避免覆盖后续有效指令导致程序崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 01:48:03