### 讲讲 ASLR 怎么绕过

ASLR 的核心思想

首先，我们简要回顾一下 ASLR 的原理。ASLR 是一种漏洞缓解机制，它的核心思想是将程序在内存中的关键区域（如可执行文件、共享库、堆和栈）加载到随机的地址

没有 ASLR 时，每次运行程序，其内存布局都是固定的。攻击者可以精确地预测函数和变量的地址，然后利用漏洞（如缓冲区溢出）来劫持程序流，跳转到这些预先知道的地址

有了 ASLR，程序每次运行时地址都会变化，攻击者无法再依赖硬编码的地址，这大大增加了攻击的难度

绕过 ASLR 的基本思路

ASLR 依赖于地址的保密性。如果攻击者能够泄露（也就是获得）任何一个模块的真实地址，那么 ASLR 就会失效。这是因为一个模块内部的相对偏移（RVA）是固定的。一旦知道了一个函数的真实地址，攻击者就可以通过这个地址减去其 RVA，得到模块的基址，进而计算出该模块中所有其他函数和数据的位置

因此，绕过 ASLR 的核心思路就是信息泄露

常见绕过 ASLR 的技术

以下是一些最常见的绕过 ASLR 的技术，从简单到复杂

1. 信息泄露漏洞

这是最直接的绕过方式。如果程序本身存在一个信息泄露漏洞，攻击者就可以利用它来获得内存中的地址

- 格式化字符串漏洞（Format String Bug）： 攻击者可以利用 `%p` 格式符来打印栈上的指针。如果栈上恰好有一个指向模块基址或函数地址的指针，攻击者就可以轻松获得这些关键地址
- 堆溢出或栈溢出： 如果攻击者能够通过溢出漏洞读取栈或堆上的数据，他们可能会读出函数返回地址、栈指针或堆块指针，这些指针都包含有地址信息
- 未初始化变量： 如果一个变量没有初始化，它可能包含之前内存中的残余数据，而这些残余数据可能恰好是一个地址

如何利用：

1. 利用信息泄露漏洞获得某个函数的真实地址，比如 `puts()` 的地址
2. 通过 `puts()` 的地址，计算出 `libc` 库的基址（`puts_addr - puts_libc_offset = libc_base`）
3. 有了 `libc` 的基址，就可以计算出 `system()` 函数和字符串 `"/bin/sh"` 的地址，从而构造 ROP 链来执行 Shellcode

2. 局部 ASLR 绕过

某些系统或老旧的程序只对部分内存区域进行了 ASLR 随机化，或者随机化范围非常小

- 弱 ASLR： 32 位系统上的 ASLR 随机化范围通常只有 16 位（64KB）。攻击者可以通过暴力破解或多次尝试来命中正确的地址，这被称为暴力破解攻击（Brute-force Attack）

3. 非随机化区域利用

有些程序或系统组件没有启用 ASLR 保护，它们总会被加载到固定的地址。攻击者可以利用这些非随机化区域作为跳板

- 静态编译的程序： 如果一个程序是静态编译的，不依赖任何共享库，那么它的所有代码和数据都在一个文件中。即使开启了 ASLR，其自身的基址是随机化的，但如果攻击者能够泄露出一个地址，就能推算出所有其他地址
- 禁用 ASLR 的模块： 有些老旧的 DLL 可能没有启用 ASLR 编译。这些模块每次都会被加载到固定地址，攻击者可以直接利用其中的 gadgets 或函数

4. 通过其他漏洞组合绕过

ASLR 很少被单独绕过，通常需要与其他漏洞（如代码执行漏洞）结合使用

- Return-to-PLT/GOT： 攻击者可以利用漏洞劫持程序流，跳转到 GOT（Global Offset Table）中一个已解析的函数地址。因为 GOT 存放的是外部函数的真实地址，通过读取这个地址，攻击者就能获得 `libc` 等共享库的基址
- ROP Gadget 寻找： 在没有信息泄露漏洞的情况下，攻击者可以使用多次尝试或堆喷射等技术。在现代 64 位系统上，ASLR 随机化范围很大，暴力破解几乎不可行。所以，信息泄露是绕过 ASLR 的首选方式