函数调用过程及栈帧分析

Linux内核程序boot/head.s执行完基本初始化操作之后,就会跳转去执行init/main.c程序。那么head.s程序是如何把执行控制转交给init/main.c程序的呢?即汇编程序是如何调用执行C语言程序的?这里我们首先描述一下C函数的调用机制、控制权传递方式,然后说明head.s程序跳转到C程序的方法。

函数调用操作包括从一块代码到另一块代码之间的双向数据传递和执行控制转移。数据传递通过函数参数和返回值来进行。另外,我们还需要在进入函数时为函数的局部变量分配存储空间,并且在退出函数时收回这部分空间。Intel 80x86 CPU为控制传递提供了简单的指令,而数据的传递和局部变量存储空间的分配与回收则通过栈操作来实现。

1.栈帧结构和控制转移权方式

大多数CPU上的程序实现使用栈来支持函数调用操作。栈被用来传递函数参数、存储返回信息、临时保存寄存器原有值以备恢复以及用来存储局部数据。单个函数调用操作所使用的栈部分被称为栈帧(stack frame)结构,其一般结构如图3-4所示。栈帧结构的两端由两个指针来指定。寄存器ebp通常用做帧指针(frame pointer),而esp则用作栈指针(stack pointer)。在函数执行过程中,栈指针esp会随着数据的入栈和出栈而移动,因此函数中对大部分数据的访问都基于帧指针ebp进行。即esp负责数据的存储与丢弃,而ebp负责数据的读取。

                                                                

于函数A调用函数B的情况,传递给B的参数包含在A的栈帧中。当A调用B时,函数A的返回地址(调用返回后继续执行的指令地址)被压入栈中,栈中该位置也明确指明了A栈帧的结束处因为随后PC指针跳转到函数B内执行。而B的栈帧则从随后的栈部分开始,即图中保存帧指针(ebp)的地方开始,保存的ebp为上一函数(即函数A)的栈帧起始地址。再随后则用于存放任何保存的寄存器值以及函数的临时值。

因此B函数栈帧中的内容可以理解为:1. ebp的值(保存的是A函数的栈帧)。2. B函数保存的寄存器、局部变量、临时值。3.调用其他函数时,传给其他函数的参数。4. B函数的返回地址。

我们知道,函数执行时,最主要的就是代码+数据。每个函数均对应一段代码和一个栈帧。因此,在栈帧的组成结构中,最关键的就是保存的ebp与返回地址:ebp保存的是调用者的栈帧,返回地址保存的是接下来要执行的调用者的代码。缓冲区溢出攻击可以利用的就是返回地址的覆盖。我的一种猜想是入侵者程序通过将返回地址覆盖为自己编写的函数地址,将保存的ebp地址覆盖为自己可以利用的区域。这样入侵者就能执行自己的代码,拥有自己的栈帧。

栈是往低(小)地址方向扩展的,而esp指向当前栈顶处的元素。通过使用push和pop指令我们可以把数据压入栈中或从栈中弹出。对于没有指定初始值的数据所需要的存储空间,我们可以通过把栈指针递减适当的值来做到。类似地,通过增加栈指针值我们可以回收栈中已分配的空间。

指令CALL和RET用于处理函数调用和返回操作。调用指令CALL的作用是把返回地址压入栈中并且跳转到被调用函数开始处执行。返回地址是程序中紧随调用指令CALL后面一条指令的地址。因此当被调函数返回时就会从该位置继续执行。返回指令RET用于弹出栈顶处的地址并跳转到该地址处。在使用该指令之前,应该先正确处理栈中内容,使得当前栈指针所指位置内容正是先前CALL指令保存的返回地址。可以发现,CALL和RET指令均完成两项工作,从栈中把返回地址存储或恢复,跳转PC到指定位置,可以说,两者的操作是相反的。若返回值是一个整数或一个指针,那么寄存器eax将被默认用来传递返回值。

尽管某一时刻只有一个函数在执行,但我们还是需要确定在一个函数(调用者)调用其他函数(被调用者)时,被调用者不会修改或覆盖调用者今后要用到的寄存器内容。因此Intel CPU 采用了所有函数必须遵守的寄存器用法统一惯例。该惯例指明,寄存器eax、edx和ecx的内容必须由调用者自己负责保存。当函数B被A调用时,函数B可以在不用保存这些寄存器内容的情况下任意使用它们而不会毁坏函数A所需要的任何数据。另外,寄存器ebx、esi和edi的内容则必须由被调用者B来保护。当被调用者需要使用这些寄存器中的任意一个时,必须首先在栈中保存其内容,并在退出时恢复这些寄存器的内容。因为调用者A(或者一些更高层的函数)并不负责保存这些寄存器内容,但可能在以后的操作中还需要用到原先的值。还有寄存器ebp和esp也必须遵守第二个惯例用法。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值