[CSAPP学习笔记] 栈帧

来源:互联网 发布:淘宝什么零食好吃 编辑:程序博客网 时间:2024/05/16 13:42

CSAPP第三章的重点在我看来就集中在对栈帧的理解上了。IA32过程调用就是通过各种栈帧来实现的。

 

栈帧(Stack Frame):为单个过程(Procedure)分配的那部分栈。

因此栈帧存在于栈上,每个过程又各自拥有自己独自的栈帧。栈帧以两个指针定界:栈指针%esp寄存器和帧指针%ebp寄存器,%esp在执行时需要不停移动,因此信息一般是靠单个过程中固定不变的帧指针%ebp来访问。下图是一个典型的栈帧结构(注意栈结构是地址向下增长的):

假设调用帧(Caller’sframe)为过程P的栈帧结构,而当前帧(Currentframe)为所调用的过程Q的栈帧结构。

由图中可以看出,在调用Q之前,P要把传递给过程Q的参数Argument1到Argument n按照地址增长的顺序存放(注意此时帧指针%esp指向Argument1)。

一旦执行call 指令调用过程Q,返回地址会被立即压入栈中(注意push指令会将减少%esp的值)。

而在开始执行Q过程前,要把P的帧指针%ebp保存起来(这样从Q返回之后才能找到P栈帧的位置),然后把%esp栈指针赋值给%ebp,让帧指针%ebp指向这个新栈帧的帧底,接着减小栈指针%esp的值使其指向新栈帧的帧顶,过程Q的栈帧空间也就分配好了。

这些空间还可以用来保存Q过程中一些局部变量(这一点需要注意,后面会进行扩展),被调用者保存的寄存器(Callee_saved Registers),以及(假如Q过程还要调用其他过程)新调用过程所需要的一些参数。

 

以前我一直不理解栈溢出带来的危害,不懂为什么gets函数因为没有对输入范围进行限制会导致很严重的后果,直到学习到这里才明白。正如上面所说,栈帧空间有一部分是用来保存过程中的局部变量。假如我们在Q中定义一个数组buf[2](编译器会自动给过程分配合适的空间),这样在Q的栈帧中会有这么一个2字节的空间。我们使用gets(buf),如果输入数据有很多的话,那么2字节之后的数据就会向上继续填入(注意栈是地址向下增长),可以看出,新填入的数据会将之前保存的寄存器和P的帧指针%ebp甚至返回地址都覆盖了,整个程序也就因此变得混乱了。所谓的蠕虫、病毒往往就是攻击程序中这些漏洞。

 

扩展到x86-64的话,栈帧与IA32的不同之处在于没有帧指针%ebp了,一旦分配好栈帧空间,%esp保持不变,信息的访问也变成了相对于栈指针%esp来进行。还有一点就是对于IA32来说,过程不能使用其栈指针之外的空间,而x86-64可以使用%esp之外128字节的范围(成为Red Zone)。