栈帧结构概览
- 父函数栈帧的栈底指针的值与子函数栈帧紧邻,以方便恢复父函数栈底指针
- 局部变量保存在栈帧的
local variabls
,这里也是最容易发生栈溢出的位置,这里受用户控制,如果写入的超长,会覆盖到其他的控制信息 - 子函数所用的参数保存在父函数的
arguments
- 在
ebp
和esp
中间是一个栈帧。紧邻高地址就是父函数的栈帧
- 定义
esp
用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化ebp
用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置eip
用来存储即将执行的程序指令的地址
将被调用的函数的参数压入栈内是倒序压入
发生函数调用时,栈发生的变化
- 先将被调用函数的参数按照逆序依次压入栈。如果被调用函数不需要参数,则没有这一步骤
- 这些参数会保存在调用函数的函数状态内,之后压入栈内的数据都会作为被调用函数的函数状态来保持
- 在压入所有参数后,会将调用函数进行调用后的下一条指令地址作为返回地址压入栈内,使得调用函数的`eip`信息得以保存
- 再将当前的`ebp`寄存器的值即调用函数的基地址压入栈内,并更新`ebp`寄存器的值为当前栈顶的地址
- 再之后将被调用函数的局部变量压入栈内
栈顶永远是当前正在执行的指令所属的函数对应的栈帧
程序执行完毕返回的过程
- 先将被调用函数的局部变量弹出栈外,将esp增大到ebp,相当于esp-ebp中间的空间不再使用
- 将调用函数的ebp弹出栈外,并存到eip寄存器内
- 至此调用函数的函数指令就全部恢复了