内存保护与加固:权限位、NX、栈保护与 ASLR
内存保护机制把页表的权限位(读/写/执行)与地址布局随机化(ASLR)组合起来,使"越权访问"从静默成功变成确定性的异常,并大幅提高内存漏洞利用的难度。
一句话定义
内存保护机制把页表的权限位(读/写/执行)与地址布局随机化(ASLR)组合起来,使"越权访问"从静默成功变成确定性的异常,并大幅提高内存漏洞利用的难度。
为什么重要
缓冲区溢出、ROP 攻击的每次利用都要先跨过这层防线。作为开发者,理解保护机制才能读懂编译器的 -fstack-protector、链接器的 PIE、以及渗透报告中"绕过 ASLR"到底绕过了什么;它也是 kp-030 现代攻击面(侧信道)的历史起点。
前置知识
kp-017(PTE 权限位与缺页异常)。
核心概念
- 权限位三元组 R/W/X:PTE 中的可读、可写、可执行标志,违例触发保护性缺页(SIGSEGV)。
- NX(No-eXecute)/ W^X:数据页不可执行——栈与堆上的注入代码不再能直接运行。
- 栈保护(stack canary):编译器在栈帧放"金丝雀值",函数返回前校验,被改写即 abort。
- ASLR(地址空间布局随机化):栈、堆、共享库、代码段的加载地址每次随机偏移。
- PIE(position independent executable):代码段也可重定位,使主程序同样享受 ASLR。
- mprotect 与 seccomp:运行时调整页权限、限制可用系统调用集(延伸到 kp-030 沙箱)。
原理与机制
分层防御示意:
攻击目标: 把控制流劫持到注入代码
第1层 W^X: 栈不可执行 -> 注入的 shellcode 无法直接运行
第2层 canary: 覆盖返回地址必先踩坏金丝雀 -> 校验失败进程终止
第3层 ASLR: 即使改写返回地址, 也不知道 gadget(代码片段)在哪
实现: 内核为 mmap/mbrk/栈基址加随机偏移(熵: x86-64 栈约 30 位)
第4层 CFI/影子栈: 校验间接跳转目标合法性 (现代硬件特性)
关键事实:ASLR 的安全性依赖熵与信息泄露防线。32 位系统熵只有 16~24 位,数千次尝试或一次地址泄露即可击穿;64 位下栈/库随机化达 28~30 位,配合"泄露地址即弃进程"的策略才有效。这就是为什么本节叫"加固(mitigation)"而非"免疫"——每层只是抬升攻击成本。
图示
关 ASLR: libc 永远在 0x7ffff7a00000 <- 攻击脚本写死地址即可
开 ASLR: 每次: 0x7f3a1c..., 0x7f8e52..., 0x7fd09b... <- 需先泄露再利用
直观类比
权限位是房间的门牌规则(机房禁止饮食=数据页禁止执行);canary 是门口的警报线,碰倒了就锁死整栋楼;ASLR 则每天凌晨把所有房间号重排——窃贼昨晚背下的地图今晨作废。
实例或案例
cat /proc/sys/kernel/randomize_va_space # 2=完全开启, 0=关闭
cat /proc/self/maps | grep libc # 连跑两次, 基址每次不同
setarch -R cat /proc/self/maps | grep libc # 关随机化跑, 基址恒定(对照实验)
checksec --file=./a.out # 查看 NX/canary/PIE 是否开启(或 readelf -l | grep GNU_STACK)
写一个 char buf[4]; gets(buf); 的小程序,分别在开/关 canary 下用超长输入触发,观察 "* stack smashing detected *" 的 abort——金丝雀的现场抓捕。
常见误区
- 认为开了 ASLR 就安全:信息泄露漏洞(读任意内存)可直接拿到真实地址,ASLR 只能对抗"盲打"。
- 认为 NX 让代码注入失效:攻击转向 ROP——复用已有代码片段拼接攻击,这也是 R/W/X 必须配合 canary、CFI 的原因。
- 把
const当内存保护:编译期常量与运行时页权限是两个层面,强制只读要 mprotect。
与其他知识点的关系
本节是 kp-030 的前传:隔离与加固的体系、侧信道对"不可观测"假设的破坏都在那里展开;COW 只读页(kp-007)正是权限位机制的日常应用。
延伸阅读
《深入理解计算机系统》第 3 章安全旁注与第 9 章;Aleph One "Smashing the Stack for Fun and Profit"(1996,历史经典,理解攻防起点)。
自测题
- W^X 原则堵住了哪类经典攻击?
答:栈/堆数据页不可执行后,注入到缓冲区的机器码无法被直接执行,迫使攻击转向 ROP 等代码复用技术。
- ASLR 的安全性依赖什么?
答:随机熵的位数与信息不泄露;熵过低可暴力枚举,任何一次地址泄露都会使当次随机化失效。
- 栈保护 canary 检测的是哪一步?
答:函数返回前校验栈帧内金丝雀值是否被覆盖缓冲区溢出破坏,是则在返回前终止进程,防止控制流被劫持。