底层理解函数调用实现过程 栈结构 栈过程

首先寄存器使用惯例:

eip :指令地址寄存器,保存程序计数器的值,当前执行的指令的下一条指令的地址值,16位中为ip,32位为eip。eip不可以直接赋值,一般都是cpu自动加1来更新,指令call和ret以及jmp可以改变eip的值。

另外汇编代码格式有ATT和intel格式,gcc和objdump的默认格式就是ATT。几个小区别,1首先是指令ATT汇编指令后面有一个l,比如intel格式为mov,ATT格式为movl

2寄存器,ATT格式有%,比如intel格式为ebp,ATT格式为%ebp

3还有一个最主要的区别就是操作指令的,操作数的顺序是相反的,源操作数和目的操作数的顺序相反。

关于栈中的寄存器:

esp :栈指针,又叫栈顶寄存器,总是指向栈顶元素,已经压入栈的最顶上的那个元素,而不是待压入的。栈指针可以移动,通过上下移动实现栈的开辟和释放。栈是向小地址方向生长的。esp寄存器中保存的是当前栈的栈顶元素的地址。

ebp :帧指针,又叫栈基址寄存器,总是指向当前栈的栈底元素。保存的是当前栈的栈底元素的地址。帧指针不可以移动,用来作为当前栈的基址,通过对基址ebp的偏移来寻址访问栈中的其他元素的值。比如:0x8(%ebp)ebp所指向的地址值在加上0x8的地址。

ebp永远都是针对当前栈的,所以当一个函数调用了另外一个函数的时候,就需要先将调用函数的栈的ebp给入栈保存,这样避免了与被调用函数的栈ebp冲突,然后在被调用函数的栈结束调用释放的时候,恢复现场的时候,在将调用函数的ebp弹出来,这样又可以回到调用函数的栈了。

栈通过栈指针栈顶esp和帧指针栈底ebp来固定栈框。

eax :保存函数的返回值的惯用寄存器

%   :直接寻址寄存器

( )  :内存间接寻址

$ :立即数 

例如:movl $8, %eax   #把立即数8存到寄存器eax中

     movl $8, (%esp)  #把立即数8存到内存esp所指的内存地址中。

这里主要来通过函数调用来彻底弄清楚栈的过程,这里直接利用bufbomb中的一段简单的c代码,通过反汇编来分析一下其汇编代码。这个c代码很简单,就是test函数中调用了getbuf函数。

其反汇编代码为:


这里的汇编代码比较长我们来截取一下只看关于栈和函数调用的部分:




来看一下不管是test还是getbuf都有的几句汇编语言:


函数的实现过程都是通过栈过程,一开始我们已经讲过了栈有两个指针用来固定栈框的,ebp栈底指针和而esp栈顶指针。

push   %ebp  #保存旧的ebp的值,也就是保存当前函数的调用者的栈的栈基址。(因为每一个栈都有一个ebp是不可以移动的,但是名字又一样,怎么区分呢,那就是开始时先把原来的ebp保存起来,然后生成自己的,调用结束后,在把原来的恢复,弹出来,自己的释放掉,这样人家原来的ebp又可以继续在自己的栈中作为基址了。不然就覆盖了回不去了)相对于test来说可能就是main之类的调用test的函数的栈的ebp,对于getbuf来说就是test的ebp。将ebp压入栈中,这个时候esp自动-4,指向新压入的ebp元素的位置处。

mov    %esp,%ebp #使帧指针ebp指向当前的esp处,也就是初始化生成一个当前栈的基址帧指针ebp,也就是当前栈的栈底表示出来固定住。对于test来说就是test的栈底,对于getbuf来说就是getbuf的栈的栈底。

sub    $0x38,%esp #通过栈指针esp向下移动为当前函数开辟自己的栈,esp指向了栈顶,从ebp到esp为当前函数的栈空间。

以上三条指令就是所有栈都有的栈开辟汇编指令。

接下来来看栈释放的汇编指令:



 

这里是两个函数中不同的栈释放恢复指令,有点不一样但是原理是一样的。

add    $0x24,%esp #释放当前栈开辟的空间,在test函数开始的时候通过栈指针esp减0x24移动来开辟了当前test函数自己的栈空间。现在将esp加上0x24也就是使esp从新移动到了栈底ebp处,就将原来开辟的栈空间释放掉了。另外一条常用的汇编指令是:

mov    %ebp,%esp #将ebp的值给esp,也就是把esp指向当前的栈底

pop    %ebx #把之前保存的寄存器ebx恢复

pop    %ebp #弹出旧的ebp,也就是把调用test函数的函数的栈的ebp弹出恢复。

ret     # 弹出返回地址。这一条指令相当于

pop    %eip 将指令寄存器恢复,也就是让调用test函数的函数的栈知道接下来应该继续执行的哪一条指令。

leave  # 是将当前栈的空间释放掉,弹出旧的ebp,相当于下面两条汇编指令:

mov    %ebp,%esp

pop    %ebp

由此可见栈的释放就是三个过程:

释放当前栈的空间

弹出旧的ebp

弹出返回地址

 

过程与esp的移动结合:(有很多人不明白,我这里已经用自己的话写的非常白话了,我觉得最后的理解方式是通过gdb调试一下,那里不明白就调试出来里面到底是什么就会自己豁然开朗)

 

栈释放主要包括:1将当前开放的栈空间释放掉,通过esp的向上移动,移动到自己的栈底ebp处(保存的旧的调用者的ebp的位置处),2恢复原来的现场,主要包括两部分,一部分是将原来的栈基址帧指针ebp复原,也就是在自己栈中保存的ebp弹出来,这个时候esp自动加4,变到原来保存旧ebp处位置上上面一个位置(一般为调用者的栈中保存的返回地址的地方,返回地址是调用当前函数时,结束后回来继续应该执行的下一条指令的eip的地址。比如call指令的下一条指令的eip的地址


比如说上面test中call getbuf这条eip的下一条eip的地址为0x8048e50

在执行call指令的时候相当于:

push eip(0x8048e50)   (系统自动将返回地址压入栈,输入调用者的一部分,比如这里在test的栈中,而getbuf的栈是从getbuf压入testebp开始,也就是getbuf的栈底元素是testebp)

jmp  getbuf(0x8049262)

),这个时候保存的旧的ebp已经弹出,之前为当前栈生成的栈基址帧指针ebp也就没有了已经。也就是被调用函数开辟的栈已经完全复原了,像什么没有发生一样。第二个部分也是最后一个部分,就是调用者需要知道我应该继续执行那一条指令(不然回来了找不到原来的指令执行到哪里了),也就是把返回地址eip的地址弹出来,esp自动加4指向原来保存返回地址的位置上面一个位置。这样调用函数又继续正常执行了。

 

图解函数调用过程:

1首先是test函数的栈结构,其中黄色是test函数的栈,绿色是调用test函数的函数的栈,比如main函数之类的。


2 test执行到call指令:

call  8049262 <getbuf>

首先  系统自动压入返回地址 push  eip  这里call的下一条eip的地址是0x8048e50

然后  Jmp到jmp  getbuf(0x8049262)

随着返回地址的入栈,esp自动下移,esp-4:这个时候仍是黄色的,因为我们在前面已经分析过,返回地址是属于调用者的栈结构的。


3跳到getbuf的函数的入口地址以后开始getbuf的栈,蓝色的代表getbuf的栈

push   %ebp  #保存旧的ebp的值,也就是保存当前函数的调用者的栈的栈基址。对于getbuf来说就是test的ebp。将ebp压入栈中,这个时候esp自动-4,指向新压入的ebp元素的位置处。

mov    %esp,%ebp #使帧指针ebp指向当前的esp处,也就是初始化生成一个当前栈getbuf栈的基址帧指针ebp,也就是当前栈的栈底表示出来固定住。对于getbuf来说就是getbuf的栈的栈底。


4 getbuf继续栈开辟

sub   $0x38,%esp 通过esp移动开辟一个getbuf的栈空间,esp此时指向getb这个栈的栈顶,此时getbuf的栈框已经固定住。


接下来再来看函数调用完以后返回到test函数,现场恢复:

1leave的第一步:

mov    %ebp,%esp #将ebp的值给esp,也就是把esp指向当前的栈底,把开辟的蓝色空间收回


2 leave的第二步:

pop    %ebp #弹出旧的ebp,也就是把调用test函数的函数的栈的ebp弹出恢复。此时蓝色框已经完全没有了,ebp也没有了,为了getbuf开的空间也已经完全释放了。


3 ret 弹出返回地址以后:


这里以上仅是简单的函数调用,调用的函数不需要传入参数,还有调用的函数需要传入参数的时候等,在bufbomb中我们会具体遇到。再具体分析。


  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: lwIP协议源码详解pdf是一本关于lwIP协议源代码的介绍和解析的书籍。lwIP协议是一款开源的、小型的TCP/IP协议,主要用于嵌入式系统中,在许多嵌入式领域都有广泛的应用。这本书详细介绍了lwIP协议源码的结构实现原理,对深入理解lwIP协议的工作原理和实现方法具有很大的帮助。 该书主要分为7章,内容涵盖了lwIP协议的基本架构、源码框架、数据结构、协议初始化、数据包发送和接收、TCP协议的实现、以及调试工具与示例代码等方面。其中,对于数据结构的介绍较为详细,包括全局变量、链表、缓冲池等的实现方法和意义,让读者更加深入地理解lwIP协议的内部实现。 此外,该书还介绍了lwIP协议的调试工具和实例代码,可以帮助读者更加快速地理解lwIP协议的使用和应用。例如,书中讲解了如何使用Wireshark抓取TCP/IP通信数据包,并包含了示例代码,可以由读者自行测试。 总体来说,该书是一本非常好的lwIP协议源码解析书籍,对于需要深入学习和了解lwIP协议的工作原理以及如何使用它来实现嵌入式网络通信的开发人员非常有价值。 ### 回答2: 《lwip协议源码详解》是一本讲解lwip协议源码的书。这个协议是一个开源的TCP/IP协议,适用于嵌入式系统。它提供了包括TCP、UDP、IPv4、IPv6、ICMP、SNTP、DHCP等常用协议,并支持各种网络接口。这本书详细介绍了lwip协议的内部结构,包括数据结构函数调用方式以及各个组件的实现方法。 首先,书中介绍了lwip的内存管理机制。lwip协议使用连续内存管理,通过定义内存池来管理内存。这个机制非常专业,并且能够提供内存保护,提高数据传输的效率。其次,书中介绍了lwip协议的各个模块的实现,包括TCP、UDP、ISP、IP、ICMP等。这些模块是lwip协议的基本构成部分。通过深入的了解这些模块的原理和实现方式,读者可以更好地掌握lwip协议的使用方法。 书中还介绍了lwip协议的一些高级功能,比如多线程支持、SNTP服务等。这些功能虽然不是lwip协议的基本功能,但是在网络应用中很常见。通过书中的介绍,读者可以学习如何在自己的应用程序中使用这些功能。 总之,《lwip协议源码详解》是一本非常好的加深对lwip协议理解的书籍,能够帮助读者深入了解lwip协议实现原理和使用方法。同时,它也是一本好的参考书,可供嵌入式系统开发工程师和网络协议开发人员参考。 ### 回答3: lwIP协议是一个基于C语言开发的轻量级、高效的协议,广泛应用于嵌入式系统中,其源码详解可以帮助我们更好地了解其实现原理和工作流程。 该PDF文件详细介绍了lwIP协议的基本结构、各个模块的实现原理、相关API和接口函数等。其中,该协议的基本结构包括实现TCP/IP协议的核心模块和各种外部功能模块,如数据通道层、网络接口层、协议接口等等。 在协议实现中,数据的传输过程可以看作是一条管道,数据从上层通过协议的接口函数进入中,经过一系列处理,最终从底层网络接口层发送出去。该PDF中详细说明了数据的传输过程和相关函数的调用流程,为学习者深入了解lwIP的实现提供了帮助。 此外,该文件还介绍了lwIP协议的多线程实现原理实现方法,以及各种协议实现的具体方式和相关函数的调用流程。其中,针对常用的TCP和UDP协议,该文件还介绍了它们的具体原理和如何在lwIP协议实现。 总之,这份源码详解是一份非常实用的资料,可以帮助我们更好地理解lwIP协议实现原理和工作流程,对于在嵌入式系统开发中使用和调试该协议都十分有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值