计算机系统导论 · 第八讲:联合体、地址空间与缓冲区溢出
第三章 第八讲:联合体、地址空间与缓冲区溢出
来源:ICS08,2026 年 10 月 8 日,53 页;配合 Attack Lab 实验讲义。
1. 联合体与位模式
联合体各成员从同一地址开始,大小是容纳最大成员并满足整体对齐所需的字节数。课件中的 char、int[2]、double 联合体为 8 字节,对应结构体为 24 字节。
把浮点位模式解释成整数,与数值类型转换是两种运算:浮点数 1.0 的单精度位模式为 0x3f800000,而把 1.0 转成整数得到 1。位模式分析用于解释编码;数值转换按目标类型重新编码。
小端序将最低有效字节放在最低地址。连续字节 f0 f1 f2 f3 f4 f5 f6 f7 在 x86-64 中解释为 64 位整数 0xf7f6f5f4f3f2f1f0。
2. 进程地址空间
- 代码段:机器指令;只读常量通常置于只读数据区域。
- 静态数据区:全局变量、静态变量,生命周期贯穿程序运行。
- 堆:动态分配对象,由分配与释放操作控制生命周期。
- 栈:活动调用的栈帧,保存局部数据、返回地址和寄存器。
- 共享库及大块映射区:通过映射组织,位置与具体进程布局有关。
地址空间图描述虚拟地址,不能直接当作物理内存布局。递归每层都保留独立栈帧;大局部数组使每层消耗增大。课件中每层约 128 KiB 的数组很快耗尽栈空间。
3. 越界写怎样改变程序状态
C 中数组访问转成地址计算与内存读写,机器指令不记录源代码中的数组边界。结构体中数组越界可能覆盖相邻浮点字段;栈缓冲区越界可能覆盖保存的寄存器或返回地址。
课件 echo 分配 24 字节栈空间,缓冲区从 %rsp 开始,返回地址在偏移 24 处。即使源代码数组仅 4 字节,返回地址偏移仍由实际栈帧确定。输入 24 个普通字符后,字符串终止符写入偏移 24,已经改动返回地址最低字节。
分析顺序:确定缓冲区首址,计算目标字段相对偏移,按小端序解释覆盖后的字节,再跟踪 ret 的取址与跳转。
4. 代码注入与 ROP 的区别
代码注入把指令字节放入输入缓冲区,并把控制流转到该缓冲区。ROP 将返回地址组织成已有指令片段的地址序列;每个片段通常以 ret 结束,栈同时存放后续片段地址与所需数据。
ret 的效果是从栈顶取新 PC,再令 %rsp 增加 8;popq 还会额外消耗一个栈槽。手工追踪 ROP 时,要逐条记录寄存器和 %rsp,区分地址与该地址中的值。
5. 课件中的保护机制
| 机制 | 作用 |
| 长度受限的输入 | 将实际读入长度限制在缓冲区容量内,并保留终止符空间 |
| 地址随机化 | 使栈及映射区域的地址随运行变化 |
| 不可执行栈 | 阻止把栈中的数据直接作为指令执行 |
| 栈金丝雀 | 在返回前比较栈中的检测值,发现覆盖后终止执行 |
这些机制分别作用于输入、地址预测、执行权限和覆盖检测。ROP 使用已有可执行代码,因此不可执行栈本身不阻止 ROP;栈金丝雀仍可能检测到覆盖。
fgets(buf, n, stdin) 最多存入 个字符并写终止符;strncpy 在源串过长时可能不写终止符,使用时需要明确长度语义。