DMALibrary Shellcode注入教程:如何寻找Code Cave并用JMP跳转调用任意函数

DMALibrary Shellcode注入教程:如何寻找Code Cave并用JMP跳转调用任意函数

【免费下载链接】DMALibrary Simple but extensive library for DMA users, made for gamehacking 【免费下载链接】DMALibrary 项目地址: https://gitcode.com/gh_mirrors/dm/DMALibrary

DMALibrary 是一款面向游戏破解场景的开源 DMA 内存库,内置开箱即用的 Code Cave 查找器与 Shellcode 注入能力。本教程带你看懂它的 c_shellcode 类:如何三步定位目标进程中带读、写、执行权限的空闲内存(Code Cave),再用一条 14 字节的 JMP 跳转指令挂钩任意函数入口,实现 Shellcode 执行与任意函数调用 🎯

📌 Code Cave 是什么:一句话看懂 Shellcode 注入的核心

Shellcode 要在目标进程里跑起来,首先得有一块「能读、能写、能执行(R/W/X),并且恰好是空的」内存。这块内存就叫 Code Cave。

DMALibrary 的注入链路可以概括为三步:

  1. 找位置:在目标模块的节区末尾页对齐处,定位满足权限与大小的空闲空间;
  2. 写代码:把你的 Shellcode 写入这块空间;
  3. 搭跳板:用 JMP 跳转把某个函数的入口"劫持"到你的 Shellcode,触发执行。

下面按源码逐层拆解。

🛠️ 快速上手:让 DMALibrary 跑起来的 3 个步骤

步骤 1:获取项目

git clone https://gitcode.com/gh_mirrors/dm/DMALibrary

步骤 2:准备依赖文件

根据 README.md,运行目录需要放置 DMA 供应商提供的三个 DLL:FTD3XX.dllleechcore.dllvmm.dll;编译期还需要 libs/ 目录下的 leechcore.libvmm.lib(出于安全考虑未随仓库提供,需自行编译获取)。

步骤 3:初始化 DMA 句柄

一切内存操作的前提是初始化,可参考示例工程 Example.cpp

mem.Init("explorer.exe", true, true);

初始化完成后,GetShellcode() 即可拿到 Shellcode 模块实例,见 Memory.h

🧩 c_shellcode 类:Shellcode 注入的 3 个核心 API

c_shellcode 类声明位于 Shellcode.h,只有三个公开方法,各司其职:

API作用源码位置
find_codecave(大小, 进程名, 模块名)在指定模块中找一块足够大的 Code Cave,成功返回地址,失败返回 0Shellcode.h#L20
find_all_codecave(大小, 进程名)遍历进程内所有模块(黑名单除外),返回全部可用 Cave 地址Shellcode.h#L28
call_function(跳板, 目标函数, 进程名)用 14 字节 JMP 钩住目标函数入口,跳转执行后自动还原Shellcode.h#L36

🔍 Code Cave 查找原理:三段式定位法

核心实现见 find_codecave,它通过"节区特征过滤 → 页内余量计算 → 全零校验"三步保证找到的空间真的可用。

① 节区特征过滤

通过 VMM 接口读取目标模块的所有 PE 节区,只保留同时具备 IMAGE_SCN_MEM_READ | WRITE | EXECUTE 三种标志的节区,见 Shellcode.cpp#L38——没有执行权限的空间写进去也跑不起来。

② 页内余量计算

Cave 的起点取在节区虚拟大小结束后的偏移 0x10 处:

codecave = 模块基址 + 节区.VirtualAddress + 节区.Misc.VirtualSize + 0x10;

筛选条件要求节区未占满最后一页的余量大于 Shellcode 大小((VirtualSize & 0xfff) < (0x1000 - function_size)),见 Shellcode.cpp#L38-L40

③ 全零校验

地址算出来还不够,库会把这段内存读回来逐字节检查,只要发现任何一个非 0x00 字节,就判定"空间不够用"并返回失败,见 Shellcode.cpp#L59-L66。这一步避免了覆盖有效数据导致的崩溃。

另外,find_all_codecave 会遍历进程的全部模块,但会跳过一份写死的系统模块黑名单(kernel32.dllkernelbase.dllntoskrnl.exe 等),见 Shellcode.cpp#L5——往这些模块里注入是高危操作,务必保持默认。

🔗 JMP 跳转:14 个字节调用任意函数

想让目标进程"替你跑代码",最经典的手法就是劫持函数入口。call_function 的实现(Shellcode.cpp#L89-L121)构造了一条 14 字节的间接跳转指令:

BYTE jmp_bytes[14] = {
    0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp [RIP+0x00000000]
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 8 字节目标地址
};

FF 25jmp [RIP+偏移] 指令,后 8 字节填入你指定的跳板地址(通常是 Code Cave)。整体流程是一个安全的"借道还路":

  1. 读原字节:先把目标函数入口的 14 字节保存下来;
  2. 写入跳转:把 JMP 指令写入函数入口(Shellcode.cpp#L105);
  3. 短暂等待Sleep(100),给目标进程留出执行窗口;
  4. 还原现场:写回原始字节,恢复函数(Shellcode.cpp#L114)。

参数含义注意区分:hook跳转落点(你的 Shellcode 地址),function被劫持的函数入口

⚡ 完整流程演练:找 Cave → 写码 → 挂钩

三个 API 串起来只有几行:

auto shell = mem.GetShellcode();
uint64_t cave = shell.find_codecave(0x100, "explorer.exe", "explorer.exe");
mem.Write(cave, myShellcode, 0x100);
shell.call_function((void*)cave, (void*)(base + 0x1234), "explorer.exe");

先找 0x100 字节的 Cave 并写入 Shellcode,再把 base + 0x1234 处的函数入口劫持到 Cave——当目标进程调用该函数时,你的代码就被执行了。若需要枚举全部可用位置,可直接调用 find_all_codecave 拿到地址列表。

🧯 常见坑与排错技巧

  • 返回 0 表示失败find_codecave 在进程信息获取失败、节区不足、空间非空三种情况下都会返回 0,注意看 LOG 输出的具体原因;
  • 大小要对齐预期:余量判断以页(0x1000)为单位,Shellcode 超过节区末页余量必然失败,可适当调小 function_size 或换模块;
  • 黑名单别乱动:默认排除 kernel32.dll 等系统模块,强行注入这些模块极易蓝屏;
  • 100ms 窗口是经验值:若你的 Shellcode 执行较慢,可仿照 call_function 自行调整延时;
  • 还原失败看日志:日志中出现 Failed to write memory 说明现场还原失败,函数仍停留在钩子状态,需重新写入原始字节。

📂 文件索引

掌握"找位置 → 写代码 → 搭跳板"这套组合拳后,你就可以基于 DMALibrary 自由拓展自己的注入逻辑了。祝实验顺利 🚀

【免费下载链接】DMALibrary Simple but extensive library for DMA users, made for gamehacking 【免费下载链接】DMALibrary 项目地址: https://gitcode.com/gh_mirrors/dm/DMALibrary

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值