08 · 机器级进阶与内存安全
**补充章:本地尚无第 8 讲课件。**依据课程日程中的 Machine Prog: Advanced、CS:APP §3.9~3.10 和 2025 第一次阶段测验第 23~24 题整理。最终范围、例子和课堂拓展需拿到第 8 讲后核对。
union:共享存储而非转换数值#
union 所有成员从相同偏移开始共享存储,总大小至少容纳最大成员,并按最严格成员对齐要求取整。写某个成员会改变其他成员所看到的底层字节。
union U { unsigned char c[8]; unsigned int i[2]; };
/* 本平台常见 sizeof(U)=8,alignment=4 */
若 c 依次写 C0 C1 C2 C3 C4 C5 C6 C7:大端下 i[0]=C0C1C2C3,小端下 i[0]=C3C2C1C0。(int)float_value 进行数值转换;以整数解释 float 的原位串通常是完全不同的数。C 与 C++ 对读取非活动 union 成员的语言规则也不同,不能把课上的位重解释练习泛化成跨语言保证。
需要检查 float 对象的位时,使用等大小的无符号整数和 memcpy 可避免通过不兼容指针别名直接读取的问题。IEEE 格式和端序仍须明确。
C 指针的完整概念#
指针类型决定解引用宽度和指针算术步长;强转类型不会移动对象,也不会自动创建目标对象。void* 可持有对象指针,但标准 C 不定义 void 的大小,因此 void* 算术不是可移植的标准写法;GCC 的相关扩展不能当成语言一般规则。
函数指针指向可调用函数,类型还包含参数和返回类型。数组可以存函数指针;函数不能按普通对象数组直接存储。空指针、未初始化指针、悬空指针、越界指针是不同情况;只检查非空不足以保证可访问。
调试与反汇编#
| 命令概念 | 用途 |
|---|---|
| break / run / continue | 断点、运行、继续 |
| step / next | 源代码级步入/步过 |
| stepi / nexti | 指令级步入/步过 |
| info registers | 观察寄存器 |
| disassemble | 反汇编函数 |
| x/Nfu address | 查看 N 个单元,f 为显示格式,u 为单元宽度 |
| backtrace / frame | 调用栈和栈帧选择 |
常见 x 显示宽度 b/h/w/g 是 1/2/4/8 字节;与 C 的 sizeof(long) 没有自动对应关系。打印地址时区分“地址值”和“该地址处的内容”。优化会让变量消失、合并或移到寄存器,调试器显示不出变量不一定是编译错误。
内存错误分类#
数组越界、错误的指针步长、未初始化读取、对象寿命结束后访问、释放后使用、重复释放、格式化字符串错误、错误分配大小都会导致问题。内存泄漏是已分配对象不再能被正常释放,和悬空指针不同;前者未必立即崩溃,后者可能指向已经无效的对象。
从给定汇编研究栈上 char buf 的边界时,先画出 buf、填充、保存寄存器、返回地址的位置。布局依编译器、优化和保护选项变化,不能固定背“数组长度加 8 就到返回地址”。写入字符串还要计入结尾 NUL。
缓冲区越界与防护#
| 机制 | 主要作用 | 限制 |
|---|---|---|
| 边界检查与正确长度的输入函数 | 阻止越界写本身 | 需要正确传入容量并处理截断/结尾 |
| ASLR | 让栈、堆、映射或代码位置更难预测 | 是地址随机化,不修复越界 |
| NX / XD / DEP | 把数据页设置为不可执行 | 依赖硬件页权限与 OS 配合,不阻止所有代码复用 |
| 栈金丝雀 | 在返回前检查哨兵是否变化 | 需编译插桩,覆盖范围和检查时机有限 |
| PIE | 使可执行文件代码可被随机布置 | 与 ASLR 配合,不能等同于所有程序自动随机化 |
2025 试题把使用安全输入函数视为需改源码、需重编译;栈地址随机化通常无需重编译普通用户程序;金丝雀通常需重编译、不必改源码;执行权限位依赖 CPU 支持。试卷的“是否需要换 CPU”是在比较机制的硬件前提,现代已经支持 NX 的机器不需要为了启用它再换硬件。
fgets(buf,sizeof buf,stdin) 可限制读取量,但仍需要处理保留换行、EOF、截断和缓冲区剩余输入。“使用某个函数”不自动证明整个程序安全。
动态栈与特殊控制#
变长局部数组根据运行时 n 分配空间,通常按对齐向上取整后减少 rsp,可能需要保存稳定帧基址。退出作用域或函数时恢复栈指针。局部数组不因返回其指针而延长寿命。
关于代码注入与代码复用,只需从体系结构角度理解:越界可能改变控制数据;不可执行栈限制执行数据;代码复用使用已有可执行指令,所以与 NX 解决的问题不同。实验中仍需按课程规则独立完成,不把现成攻击字符串当复习目标。
自检#
能计算 union 的大小与字节覆盖;能说明位重解释与强转不同;能分析一段栈图中的对象寿命和边界;能比较 ASLR、NX、金丝雀和边界检查各解决什么问题。第 8 讲课件到齐后,应优先用覆盖索引补差。