核心隐喻
栈是程序执行的“临时舞台”。
%esp(栈指针): 布景师,随数据进出上下移动(低地址增长)。%ebp(帧指针): 舞台总监,作为当前函数的锚点,用于稳定寻址参数和局部变量。
过程调用的生命周期
1. 准备 (Caller P)
- 传参: 将参数放入寄存器 (
%rdi,%rsi…) 或压入栈中。 - Call: 执行
call Q。- 动作: 将 返回地址 (Return Address) 压入栈。
- 跳转:
%rip指向 Q 的起始地址。
2. 建立 (Callee Q Prologue)
- 保存锚点:
pushq %rbp(保存 P 的锚点)。 - 建立锚点:
movq %rsp, %rbp(Q 的新锚点确立)。 - 分配空间:
subq $N, %rsp(为局部变量腾出舞台空间)。
3. 执行 (Body)
- 寻址: * 参数:
8(%rbp),12(%rbp)… (正偏移)- 局部变量:
-4(%rbp),-8(%rbp)… (负偏移)
- 局部变量:
4. 恢复与返回 (Epilogue)
- Leave (清场): 等价于
movq %rbp, %rsp然后popq %rbp。- 释放局部空间。
- 恢复 P 的
%rbp。
- Ret (交权): 从栈顶弹出 返回地址 给
%rip。
地址增长方向 栈内存布局 (Stack Layout) 状态/寄存器
| +-------------------------------+
| | 调用者 (Caller) 的栈帧 |
| +-------------------------------+
| | 参数 N ... 参数 1 | <- 函数调用前压入
| +-------------------------------+
| | 返回地址 (Return Addr) | <- call 指令自动压入
| +-------------------------------+
高 | 保存的旧 EBP (Old %ebp) | <- push %ebp (当前帧开始)
| +-------------------------------+ <--- 当前 %ebp 指向这里
| | |
| | 局部变量 (Local Vars) |
| | (例如 int a, char b[10]) |
| | |
| +-------------------------------+
低 | 保存的寄存器 (esi, ebx等) |
| +-------------------------------+ <--- 当前 %esp 指向这里
| | (待使用的自由空间) |
V +-------------------------------+安全启示 (Pwn)
- 缓冲区溢出: 如果局部变量(负偏移)写入越界,会向高地址覆盖。
- 攻击路径: 局部变量 → 保存的
%rbp→ 返回地址 (Target)。 - 后果: 控制流劫持 (Control Flow Hijacking)。