如何绕过DEP等现代防护机制实现缓冲区溢出?

2 人参与

在对抗现代防护时,攻击者往往把注意力放在如何让非执行内存段重新获得执行权。DEP(Data Execution Prevention)通过在页面表项中标记 NX(No‑Execute)位,阻止 CPU 在标记为不可执行的区域跑代码;而 ASLR(Address Space Layout Randomization)则把关键模块的基址随机化,削弱硬编码地址的可靠性。两者联手,使得传统的直接覆盖返回地址后写入 shellcode 的思路几乎失效。

常见的绕过路径

  • 利用不含 DEP 的旧版 DLL(如 user32.dll 的某些子模块)中的 jmp espcall esp 指令,实现 “代码跳转”。
  • 构造 ROP 链,调用 VirtualProtectNtProtectVirtualMemory 等 API,将栈或堆页标记为可执行,然后转向自定义 shellcode。
  • 返回到系统库函数(Return‑to‑libc),直接执行已有的系统调用,如 system("cmd.exe"),绕过代码注入。
  • 利用堆喷射或 JIT‑spray,在 JIT 编译器生成的内存块中植入可执行指令,借助 JIT 的执行权限突破 DEP。

实战示例:ROP 关闭 DEP

假设已定位到缓冲区溢出点,偏移量为 260 字节,且目标进程使用了 kernel32.dll(ASLR 已关闭的版本)。攻击者可以在泄露的模块基址基础上,挑选以下 ROP 小碎片:

// ROP 链(伪代码)
// 1. pop eax ; ret          -> eax = 0x40 (PAGE_EXECUTE_READWRITE)
// 2. pop ebx ; ret          -> ebx = 0x1000 (size)
// 3. pop ecx ; ret          -> ecx = 0x0 (oldProtect)
// 4. pop edx ; ret          -> edx = 0x0 (lpflOldProtect)
// 5. push 0x7c801d7b ; ret // VirtualProtect 地址
// 6. int 0x80               // 调用
// 7. jmp esp                // 进入后续 shellcode

在 ROP 完成后,VirtualProtect 将受控的栈页标记为可执行,随后的 jmp esp 把执行流导向后续的自定义 shellcode。此时可以使用 msfvenom -p windows/meterpreter/reverse_tcp -b "x00x0ax0d" -f c 生成不含空字符的负载,直接拼接到溢出缓冲区末端。

辅助工具与调试技巧

  • Mona.py:在 Immunity Debugger 中搜索 jmp espcall esp 或者可用于 ROP 的 pop reg ; ret 指令。
  • Ropper:快速生成符合目标平台的 ROP 链,支持自动排除已开启 ASLR 的模块。
  • procmon / Process Explorer:监控目标进程的 DEP 状态,确认是否真的被关闭或可以通过 SetProcessDEPPolicy 进行修改。

从理论到实践,绕过 DEP 的核心在于“让代码跑在可执行的内存”。无论是借助未受保护的 DLL、借助 ROP 调用系统 API,还是利用 JIT‑spray,思路总是围绕同一条线索展开:先取得执行权限,再把控制权交给攻击者准备的负载。真正的挑战在于在 ASLR 与 DEP 双重压制下,快速定位可靠的 gadget 并拼装出不含禁用字节的链,往往决定了攻击是否能在实战环境中落地。

参与讨论

2 条评论
  • LagoonDrifter

    这方法在Windows 10上还能用吗?

    回复
  • 不羁之刃

    用ROP链挺麻烦的,有没有更简单的绕过方式?

    回复