栈帧与函数调用

1.什么是栈帧

在理解栈帧之前,我们需要理解什么是栈

栈在数据结构中是一种运算受限的线性表,即我们只能对表尾进行操作,称之为栈顶,相对的,另一端称为栈底。它按照先进后出的原则存储数据,先进入的数据保存在栈底,后来的数据保存在栈顶。

在计算机系统当中,栈是拥有在数据结构当中所有属性的一块动态内存区域,用来存储函数内部的局部变量和返回地址。

而栈帧,就是在程序运行的过程当中,保存函数调用时所需要维护的信息。

 

栈帧

简言之,栈帧是利用EBP寄存器来访问函数内部局部变量、参数、函数的返回地址等。

栈帧结构

复制代码
PUSH  EBP                 ;函数开始(使用EBP先把已有值保存在栈中)
MOV  EBP  ESP             ;保存当前ESP值到EBP中


                          ;函数体
***                       ;无论ESP值如何变化,EBP值不改变,可以安全访    
                                   问函数内的局部变量,参数


MOV ESP  EBP              ;将函数的起始地址(返回地址)返回到ESP中
POP  EBP                  ;弹出保存在栈中的EBP值
RETN                      ;函数终止
复制代码

利用实例来理解栈帧(逆向工程核心原理:栈帧章)

复制代码
#include<stdio.h>
long add(long a , long b)
{
   long x = a , y = b;

    return (x+y);
    }


int main()
{
    long a = 1 , b = 2 ;
    
    printf(" %d \n" , add( a , b )) ;

    return 0;
    }
复制代码

 

汇编语言如下所示

 

 

①我们先从main()函数来进行程序分析

我们需要密切关注栈的变化

当前ESP、EBP指针如图所示(如下);

 

栈顶指针保存19FF2C保存着main()函数执行完后的返回地址(如下);

 这条指令(如下)把EBP(栈帧指针)保存在栈中(main()函数执行完毕,返回之前,该值会再次恢复)

 

main()函数的esp的值被保存在ebp当中(下图),当做开辟新栈之后的基址(同时作为执行完add()函数的返回地址)。从这条命令开始,ebp与esp持有相同的值,main()函数的栈帧就生成好了。

 

栈内ebp的指针保存着main()函数开始执行时的初始值

 

 

②设置变量

 

 

 

 sub是减法指令,esp-8意思是在栈内开辟一个八字节的空间(long 型的a,b一个占四字节)

 

 把ebp-4与ebp-8处各有一个四字节的内存空间,用来保存数据1和2。

执行完后栈内空间如下所示

 

 

 ③调用add()函数

 

 以上五行汇编描述了调用main()函数的整个过程

函数add()接收a、b两个长整型,所以调用前先把两个参数压入栈,如下图所示

 

 

返回地址

 

 

执行call命令进入函数之前,需要把函数的返回地址压入栈。在地址40103C中执行了call,它的下一条指令的地址是401041,即执行完add()函数后程序的执行流接着执行地址401041的命令,此处即为返回地址。

执行完call命令后,栈内如图所示

 

 

④进入add()函数

 

 

 生成add()函数的栈帧

栈内情况如图所示:

 

 

 

⑤设置add()函数的局部变量

 

 

 

 [ebp+8]在栈内表示a的值,[ebp+c]表示b的值,[ebp-8]与[ebp-4]指向add()函数的两个参数x,y

 

 

⑥add运算

 

 运算完成后值被储存在eax当中

 

 

 

 

⑦删除add()函数的栈帧&函数的返回值

在返回值之前,需要先删除栈帧。

 

 mov指令用于将储存在ebp当中的main()函数的esp的值恢复到esp当中

pop指令将add()函数开始执行时储存的ebp的值弹出

这两条指令完成后,栈内情况如下

 

 

 

 ebp指向main()函数开始执行时的初始值

esp保存函数执行完毕后的返回地址401041

 

⑧从栈中删除add()函数的参数

retn执行后,程序重新运行到main()函数内执行add  esp+8

 

 这条命令的作用是删除参数a,b

 

⑨printf()与return 0;

⑩删除main()函数的栈帧

 

 执行完后指针与栈内空间如下,main函数栈帧被删除

 

 

 

 

retn指令执行后,返回到主函数执行完毕后的状态

 

posted @   e1ectronic  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示