栈(Stack),一种为了优化内存运用,分配效率而产生的单程序数据大锅饭。
这个字的本义是仓储建筑,对于理解其含义没有任何帮助,而英文的 Stack 个人还是认为更加形象。无论如何,其标志性特征为”先进先出,后进后出“的规则。
除此之外,还需要了解栈的生长方向。栈由高地址向低地址生长。如
push rbp后,64位下rsp- 8。
栈帧
栈是一种叠叠乐。 为了让每一个函数拥有相对独立的运行环境,大锅饭升级了“鸳鸯锅”,为每一个函数调用安排了一层供它们自己折腾,运行结束返回时收回。为此,程序做了以下工作:
在函数调用前,
call fun调用该函数,等价于
push rip
jmp fun先将此时程序运行的位置压入栈中暂存,以备之后还原,然后跳转到call的函数的起始位置,此时的栈状态如下:
返回地址(旧rip) < rsp
...
初始化时,
push rbp
mov rbp, rsp
sub rsp, 30h其先会将旧的栈底入栈,储存起来等待函数运行结束后还原。然后将旧的栈顶作为新的栈底,然后将栈顶减去特定数值为该战阵分配空间。这些步骤完成后,此时的栈状态如下:
栈顶(低地址) < rsp
[30字节的位置以供数据使用]
旧栈底的地址 < rbp
返回地址(旧rip)
...
在返回时,
leave
retn也就是
mov rsp, rbp
pop rbp
pop rip程序会将栈顶移回栈底,然后弹出先前储存的就栈底,最后在弹出call命令压入的地址,继续运行先前的函数。此时的栈结构:
*返回地址* (数据并没有被删除,只是框定的rsp和rbp离开了,其成为了废数据)
... < rsp
风险
不受限制的输入非常危险。未加限制的输入可能会越过数据分配的边界,污染到其他的数据;更有甚者,数据会横冲直撞,覆盖返回地址,导致栈溢出。这就让别有用心的人可以控制程序流,构造ROP链。