一、C语言程序的机器级表示
函数P调用函数Q过程执行步骤:
- P将入口参数(实参)存到Q能访问的地方(栈)
- P保存返回地址(下一条指令的位置),将控制权交给Q
- Q保存P的现场(寄存器的内容),为自己分配空间
- Q执行自己的函数
- Q恢复P的现场,释放自己的空间
- Q取出返回地址,返回P
二、栈、栈帧结构 + 缓冲区溢出攻击
缓冲区是一块用于存放数据的临时内存空间,它的长度事先已经被程序或者操作系统定义好。
- 采用小端存储,栈帧中的数组从低地址处向上填充数据
- 虚拟地址空间中栈向下生长(从高地址向低地址生长)
- 当前栈帧底部的上方紧跟着存放了调用者的返回地址(即在当前函数调用结束,当前栈帧空间被释放后,接着执行的地址)
若当前栈帧的数组溢出(数据长度超过定义的范围),进一步填充至调用者的返回地址。
此时若再将希望跳转到的函数地址同样按照小端存储的方式写入溢出的数组,调用者的原返回地址会被修改。
在当前栈帧空间释放回到调用者的返回地址处时,程序会跳转至修改后的地址,而不是原返回地址,完成了缓冲区溢出攻击。
图1 虚拟地址空间(stack部分为栈)
图2 栈帧生长结构