核心隐喻

栈是程序执行的“临时舞台”。

  • %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)。