函数栈帧详解(代码片段)

正义的伙伴啊 正义的伙伴啊     2022-12-12     754

关键词:

在了解函数栈帧之前先要了解一些基础概念:

  • 首先是程序在编译阶段发生的处理 参考一下博客:程序环境和预处理
    总结一下就是 编译过程的最终产品是可执行程序——由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机的内存中,因此每条指令都有特定的内存地址。在生成可执行程序时程序已经变成了供机器识别的二进制代码,二进制代码晦涩难懂,所以我们采用观察汇编语言来窥探函数栈帧的底层原理。
  • 还有就是要了解一下内存的分区:

我们今天讨论的函数栈帧主要是在栈区上进行的。而栈区的结构是从高地址到低地址开辟空间

  • 对一些汇编的基本符号的了解:

    1. 基本的寄存器:
      esp: 始终指向栈顶空间
      ebp: 是在堆栈中录地址用的,始终指向系统栈最上面一个栈帧的底部
      eax/ebx/ecx/edx: 这些寄存器类似一个容器用啦储存一些数值
  • 对一些汇编的指令的了解:
    1. mov esp,ebp 这里的mov 的意思是将ebp 的值赋值给 esp ——也就是把右边的值赋值给左边
    2. pop edi 这里是把栈顶元素先出栈,再赋值给寄存器edi
    3. push ecx 这里是把ecx的值压入栈顶
    4. dword ptr [地址] 就是 该地址所指向的四字节数据、
    5. rep stos dword ptr es:[edi] 的意思是覆盖从edi开始的内存为eax,edi减小4;循环ecx次。
    6. sub esp,0E4h 这里的意思是将esp减去0E4h(16进制)

我们选取一个比较简单的函数调用来从汇编层面观察函数再栈区上面的创建:

#include<stdio.h>
int add(int x, int y)

	return x+y;

int main()

	int a = 10;
	int b = 20;
	int c = 0;
	c = add(a, b);
	return 0;

调试函数,转到反汇编

这就是add函数的汇编代码,下面来逐步解读:
– ❤️

  • 第一步: 这里先将ebp压入栈,这时因为esp始终是指向栈顶的所以esp也指向ebp,
  • 第二步: 将esp的值赋值给ebp,所以ebp也指向了栈顶了

– ❤️

  • 第一步:将esp减去0E4h,也就是向上位移了0E4h
  • 第二三四步:向栈里压入了ebx、esi、edi三个值,每压一个值esp都要向上移动,因为esp始终指向栈顶
  • 最后一步: ebp-0E4h的值存入edi中

– ❤️

  • 第一步: 将39h(也就是十进制的57)存入寄存器ecx
  • 第二步:0CCCCCCCCh 存入寄存器eax
  • 第三步: 文章上面也介绍过了这个语句的用法:

rep stos dword ptr es:[edi] 的意思是覆盖从edi开始的内存为eax,edi增加4;循环ecx次。

因为edi存的内存地址是ebp-0E4h ,而0E4h的十进制的值为 228 ,循环次数ecx 39h 是十进制的57。然而228是57 的四倍,所以这个指令的含义就是将ebpebp-0E4h 的每四个字节的空间都拷贝成eax的值也就是0CCCCCCCCh



这里用来填充中间没存的cc cc cc cc 实际上就是我们有时访问内存越界的时候看到的 烫烫烫烫烫烫烫烫烫烫烫烫…

– ❤️

  • 第一步:向ebp-8 的位置赋值0Ah,十进制里的10 也就是a的值
  • 第二步:向ebp-14h 的位置赋值14h 十进制里的20,也就是b的值
  • 第三步:向ebp-8 的位置赋值0 也就是c的值

– ❤️

  • 第一步 :前两行是首先把ebp-14h 的值赋给寄存器eax,并把eax压入栈
  • 第二步:后两行是首先把ebp-8h 的值赋给寄存器ecx,并把ecx压入栈

    这里其实就是所谓的函数传参,将函数传给一个创建的临时变量:

– ❤️

  • 这里就是调用add函数,call是指访问add所在的内存的地址,但是这里要注意的一点是,在访问add所在函数的地址的时候,编译器将call指令的下一条指令压入栈中(记住这里后面会很有用)

    按f11访问函数内部

    跳转到函数的代码的汇编

接下来就要访问add函数内部了!
– ❤️

由于创建该函数的栈帧与创建main函数时的一模一样,所以这里就省略了创建函数栈帧的具体过程


这里寄存器 esp 和 ebp之间所夹的区域为add的函数栈帧

– ❤️

  • 第一步: 将 ebp+8 所指向的值,(也就是传进来的形参10) 存入寄存器eax中
  • 第二步:将ebp-0Ch 所指向的值,(也就是传进来的形参20) 与寄存器eax相加,这时寄存器eax存储的就是30

– ❤️

  • 第一步: 这里分别将寄存器edi esi ebx 出栈
  • 第二步:将esp的地址加上0C0h ,也就是在初始化的时候esp减去的大小,所以这时esp和ebp指向同一块空间

– ❤️

  • 第一步:将ebp的值赋给esp
  • 第二部 :将栈顶的元素弹出,并赋给ebp,注意:这里栈顶存的ebp实际上是main函数栈帧ebp所指向的ebp的地址

– ❤️

  • 这时进行到了return ,也就是add函数执行完成,返回main函数

    但是返回main函数并不知道从哪个指令开始执行,所以这里就体现了—将call下一条指令存入函数栈帧的作用,使得调用函数结束之后依然能找到下一条指令,事实也是如此:

– ❤️

  • 第一步:将esp的地址加上8,由于每个都是四字节,也就是向下移动两位
  • 第二步:将eax也就是我们在add里得到的返回值,赋值给ebp-20h 也就是c所在的地址空间

    第二步将返回值成功的带回了主函数!

– ❤️

后面对于main函数的销毁和 对于add的销毁是一模一样的 ,这里就不在赘述了

总结:

函数栈帧让我们充分的意识到函数建立的过程包括:

  1. 函数的形参是如何传递进函数:在函数栈帧上拷贝给一个临时变量
  2. 返回值是如何传递回主函数的:由一个寄存器储存带回主函数,与形参 和 函数内创建的临时变量无关(出函数就会被销毁)

函数栈帧详解(代码片段)

在了解函数栈帧之前先要了解一些基础概念:首先是程序在编译阶段发生的处理参考一下博客:程序环境和预处理总结一下就是编译过程的最终产品是可执行程序——由一组机器语言指令组成。运行程序时,操作系统... 查看详情

c语言深入逐汇编详解函数栈帧的创建和销毁过程(代码片段)

【C语言深入】逐汇编详解函数栈帧的创建和销毁过程一、图解大概过程二、函数栈帧的创建过程1、简介一些需要用到的汇编指令和寄存器2、调用main函数的函数3、局部变量的初始化4、形成临时拷贝5、函数调用6、形成栈帧7、提... 查看详情

函数栈帧的创建与销毁(代码片段)

文章目录1.函数栈帧的概念2.函数栈帧的创建2.1main函数函数栈帧的创建过程2.2main函数中创建变量2.3Add函数函数栈帧的创建2.4Add函数栈帧的销毁1.函数栈帧的概念函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活... 查看详情

图解函数栈帧-函数的创建与销毁(代码片段)

函数栈帧🎂前言🌹栈帧的概念💖准备工作😀main函数栈帧的创建及初始化😁main函数的被调用😂main函数栈帧的开辟🤣main函数栈帧的初始化👩临时变量的创建。👨Add函数栈帧的创建🧑Add... 查看详情

函数栈帧的创建与销毁(代码片段)

...什么是栈帧C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈... 查看详情

函数栈帧的创建与销毁(代码片段)

目录写在前面函数栈帧的创建与销毁了解两个寄存器ebp和esp函数栈帧创建与销毁的具体过程main函数的函数栈帧变量的创建Add函数栈帧的创建与销毁回到main函数总结写在前面在我们前期的学习编程的过程中,我们会遇到许多... 查看详情

函数栈帧的创建和销毁(代码片段)

目录各种寄存器的作用main()函数的调用通过汇编观察函数调用过程main()函数栈帧开辟过程Add()函数栈帧开辟过程Add()函数栈帧销毁过程各种寄存器的作用eax是“累加器”(accumulator),它是很多加法乘法指令的缺省寄存器ebx是“... 查看详情

浅谈函数栈帧(代码片段)

...全部上传码云,推荐关注哦~笔芯~ 目录 内存管理和函数栈帧一些准备工作main函数栈帧push压栈操作什么是压栈?出栈?ebp和espmain函数空间的开辟变量的创建以及传参创建变量传参(形式 查看详情

函数栈帧的创建与销毁(代码片段)

文章目录1.函数栈帧的概念2.函数栈帧的创建2.1main函数函数栈帧的创建过程2.2main函数中创建变量2.3Add函数函数栈帧的创建2.4Add函数栈帧的销毁1.函数栈帧的概念函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活... 查看详情

《逆向工程核心原理》读书笔记——第7章栈帧(代码片段)

...帧7.2调试示例:stackframe.exe7.2.1StackFrame.cpp7.2.2开始执行main()函数&生成栈帧7.2.3设置局部变量7.2.4add()函数参数传递与调用7.2.5开始执行add()函数&生成栈帧7.2.6设置add()函数的局部变量(x,y)7.2.7ADD运算7.2.8删除函数add()的栈帧&函... 查看详情

栈帧(代码片段)

C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先,栈是从高地址向低地址延伸的。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶... 查看详情

c语言进阶顶级神功!函数栈帧的创建和销毁(代码片段)

...垫正文开始1.大致轮廓了解(源代码及反汇编)2.函数栈帧的创建和销毁总流程3.函数栈帧的创建3.1main函数的创建(分解)3.2Add函数的创建(分解)4.函数栈帧的销毁4.1main函数的销毁(分解)5.1Add函... 查看详情

超详细函数栈帧(利用反汇编窥探底层原理)+建议收藏(代码片段)

目录前言寄存器主函数是被谁调用的?调用逻辑是什么主函数栈帧的创建栈顶指针,栈底指针函数栈帧创建的预备工作Add函数是怎么被调用的前言学习函数栈帧之前我们得了解一下什么是寄存器,因为关于函数栈帧的... 查看详情

c语言的函数栈帧究竟是什么?你知道吗?(代码片段)

内容导读1.寄存器2.函数栈帧2.1函数栈帧的概述2.2函数栈帧创建过程2.2.1被调用的main函数2.2.2函数栈帧创建与销毁的过程前面的话:作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!博主的... 查看详情

c语言之函数调用及栈帧分析(代码片段)

一.前言 每一次函数调用都是一个过程。这个过程我们通常称之为:函数的调用过程。这个过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存,现场保护。这块栈空间就是函数栈帧。实验代码:#inc... 查看详情

函数栈帧的创建和销毁(待写)(代码片段)

函数栈帧的创建和销毁main函数被调用的过程:具体过程main函数被调用的过程:mainCRTStartup()调用_tmainCRTStartup()再调用main()寄存器:ebp(栈底指针),esp(栈顶指针)(sp是esp的低16位,esp是rsp的低32位,ss是16位堆栈... 查看详情

函数栈帧的创建和销毁(代码片段)

...的说明二、对于创建和销毁的全过程1.对于_mainCRTstarup的函数的创建2.对于main函数的创建(1).为什么有时候会打印出烫烫烫3.对于Add的函数的创建(2).为什么说形参不在函数中(3).函数中return值如何放回的>(4).ebp-main出栈后ebp寄存器... 查看详情

函数栈帧创建(代码片段)

我们在编译代码的时候是不是真的有去想过代码是怎么运行的吗?在代码的最深处到底有什么秘密,就让我来为你们来找找吧。在了解这些内容之前,我们先要了解什么是栈。栈就像是手枪的弹夹,里面的子弹都... 查看详情