DbgNexum 是一个概念验证项目,它利用 Windows 调试 API 和共享内存(文件映射)来实现 shellcode 的注入。其核心思路是避免直接对远程进程内存进行读写操作,转而通过精细的线程上下文操作,强制目标进程自行加载并执行有效载荷。
注入器会首先附加到目标进程,并创建一个处于挂起状态的线程。随后,它通过调试循环设置硬件断点,在特定的函数返回地址处捕获程序的执行流。每当成功捕获一次执行后,注入器便会巧妙地修改 CPU 寄存器来模拟函数调用,从而在目标进程内部“编排”出一系列连续的 Windows API 函数调用。
主要特点
- 免用 WriteProcessMemory/VirtualAllocEx:有效载荷通过
CreateFileMapping 创建共享内存,并利用目标进程调用 MapViewOfFile 自行映射。
- 免用 ReadProcessMemory:所有关键信息均通过读取和操作被调试线程的上下文来获取。

工作原理
整个执行流程是注入器的调试循环与目标进程之间不断交互、步步为营的结果。
注入阶段
DebugLoop 函数包含了主要的注入逻辑,它像一个“状态机”一样协调各个步骤:
0. 准备工作
- 注入器会保存当前的堆栈指针(RSP),以便在后续每个阶段重复使用这片栈区域。
- 为了获取一个用于“锚定”栈的返回地址,我们设置一个陷阱标志,并将执行流设置为立即进行
ret 返回。
1. 分配缓冲区
- 在锚定栈的返回地址上设置硬件断点(HWBP),这样当被调用的函数返回时,调试器便能收到通知。
- 准备并强制线程调用
LocalAlloc 函数,在目标进程内部分配一个小缓冲区。
2. 设置数据
- 准备并强制线程调用
memcpy 函数,将一个特定的字符串(这里是 MZ)复制到上一步分配的缓冲区中。这个字符串将作为共享内存对象的名称。
3. 准备堆栈
- 强制线程调用
memset 函数,将栈上的一个槽位清零。这是为第五阶段做准备,届时将调用 MapViewOfFile。由于该函数参数可能超过四个,部分参数需要通过栈来传递,此处清零就是在设置栈参数。
4. 打开文件映射
- 强制线程调用
OpenFileMappingA,使用第二阶段设置的 MZ 名称来打开已创建的共享内存对象。
5. 映射有效载荷
- 强制目标进程调用
MapViewOfFile。这一步会将共享内存区域(其中包含了我们的 shellcode)映射到目标进程的地址空间中,并赋予其 EXECUTE 执行权限。
6. 执行与清理
- 将线程指令指针(RIP)重定向到
MapViewOfFile 调用返回的地址(即映射进来的 shellcode 起始地址)。
- 清除之前设置的调试寄存器,然后与目标进程分离,完成注入。
这种方法在漏洞利用与逆向工程中提供了一种更为隐蔽的进程注入思路。更多类似的技术探讨与工具分享,欢迎访问云栈社区。
|