ZJZJ / ICS NOTES
学习空间整本阅读
COMPUTER SYSTEMS · PEKING UNIVERSITY

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 讲课件到齐后,应优先用覆盖索引补差。