### NX 是怎么绕过的 **绕过 NX 的基本思路** NX 阻止了攻击者在数据段上执行**自己的代码**。那么,绕过 NX 的基本思路就是:**不注入代码,而是利用程序本身已有的、具有执行权限的代码** 这个思路催生了多种绕过技术,其中最主要、最著名、最通用的就是 **ROP** **ROP** ROP 是目前最主流的 NX 绕过技术。它的原理是利用程序中已有的、以 `ret` 指令结尾的短小代码片段,这些片段被称为 **“gadgets”** **ROP 的工作原理** 1. **寻找 Gadgets:** 攻击者首先在目标程序或其依赖的共享库(如 `libc`)中寻找一系列以 `ret` 指令结尾的“gadgets”。一个 gadget 可能是一条或几条汇编指令,例如:`pop edi; ret;` 或 `mov eax, [ebx]; ret;` 2. **构建 ROP 链:** 攻击者利用漏洞(如缓冲区溢出),用一系列精心挑选的 gadget 地址来覆盖栈上的返回地址这些地址按顺序排列,形成一个 **“ROP 链”** 3. **劫持控制流:** 当函数返回时,它不再返回到正常调用的地方,而是返回到 ROP 链中的第一个 gadget 4. **链式执行:** - 第一个 gadget 执行完后,其末尾的 `ret` 指令会从栈上弹出下一个地址,也就是 ROP 链中的第二个 gadget - 这样,一个 gadget 接一个 gadget 地执行,每个 `ret` 指令都将控制流转移到下一个 gadget - 通过这种方式,攻击者可以利用程序中已有的代码,间接地执行任意恶意逻辑 **ROP 攻击的最终目标** 一个典型的 ROP 攻击,其最终目标通常是调用某个函数,比如 `system()`,并将一个指向 Shell 命令字符串(例如 `"/bin/sh"`)的指针作为参数传递给它 一个完整的 ROP 链通常包含: - **`pop` gadget:** 用于将栈上的参数值弹出到寄存器中,为函数调用做准备 - **`system()` 的地址:** ROP 链的末尾,用于最终调用 `system()` - **字符串 `/bin/sh` 的地址:** 作为 `system()` 的参数 **绕过 NX 的其他方法** 除了 ROP,还有其他一些不那么常见,但同样能绕过 NX 的技术: **1. JIT Spraying(JIT 喷射)** 这种方法主要用于绕过浏览器中的 NX 保护 - **原理:** JIT(Just-In-Time)编译器会动态地生成可执行代码。攻击者可以利用 JavaScript 等语言的 JIT 特性,构造大量的 `nop` 指令(无操作指令),然后在其末尾附上 Shellcode - **工作方式:** JIT 引擎会将这些指令编译成原生机器码并存放在一个**可执行的**内存区域。攻击者只需找到这个可执行区域的地址,并跳转过去即可 **2. Return-to-libc(返回到 libc)** Return-to-libc 是 ROP 的前身和简化版。它不需要复杂的 gadget 链,而是直接劫持程序流,跳转到已加载的 `libc` 库中的一个函数 - **原理:** 攻击者利用漏洞,用 `libc` 中 `system()` 函数的地址覆盖栈上的返回地址 - **工作方式:** 当函数返回时,程序流会直接跳转到 `system()` 函数。攻击者在栈上预先放置好 `system()` 函数所需的参数(如 `"/bin/sh"` 的地址),即可实现代码执行 - **局限性:** 这种方法非常简单,但它只能调用 `libc` 中已有的函数,而不能像 ROP 那样组合出更复杂的逻辑