如何向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
相关产品推荐
相关产品推荐

