函数栈帧的创建和销毁,带动图详细解析,带你大致分析汇编代码

news2024/11/23 22:08:35

目录

1.什么是函数栈帧

2.理解函数栈帧有什么用?

3.函数栈帧的创建和销毁解析

3.1什么是栈?

3.2 认识相关寄存器和汇编指令

3.3函数栈帧的创建和销毁解析过程

3.4函数的调用

3.5汇编代码

3.5.1函数栈帧的创建

3.5.2main函数部分

3.5.3Add函数部分

3.5.4main函数剩下部分


1.什么是函数栈帧

  1. 在写C语言程序的时候,经常为了实现一个功能来封装一个函数,C语言是以函数为基础的基本单位
    1. 函数是怎么调用的?
    2. 函数是怎么传参的?
    3. 函数的返回值怎么带回?
  2. 上面这些问题都与函数栈帧有关系
  3. 函数栈帧(stack frame),在函数调用时,系统会调用栈(call stack)所开辟空间,这些空间用来存放:
    1. 函数参数和函数返回值
    2. 临时变量(函数静态局部变量和编译器自动产生的其他临时变量)
    3. 保存上下文信息(需要保持不变的寄存器)

2.理解函数栈帧有什么用?

  1. 只要理解了函数栈帧的创建和销毁,以下的问题可以很好的理解了
    1. 局部变量是如何创建的?
    2. 局部变量在不初始化内容的情况下为什么是随机的?
    3. 函数调用时参数如何传递的?
    4. 传参的先后顺序是怎样的?
    5. 函数的返回值如何返回?
  2. 以上问题学习完,下面的都可以得到答案了

3.函数栈帧的创建和销毁解析

3.1什么是栈?

  1. 现在计算机程序都会用到栈,有了栈才有了函数和局部变量,才会有现在的计算机语言
  2. 这个栈是内存中的栈和数据结构中的不一样的,要区分开;栈的入数据(Push),入数据只会在栈顶,栈的出数据(Pop),这只会在堆顶;遵循着后进先出的原则
  3. esp:是指向栈顶的,Pop数据或者Push数据,esp的指向会刷新
  4. 栈是由高到低增长的(高地址处向低地址处增长)

3.2 认识相关寄存器和汇编指令

  1. 相关寄存器
    1. eax:通用寄存器,保留临时数据,常用于返回值
    2. ebx:通用寄存器,保留临时数据
    3. ebp:栈底寄存器
    4. esp:栈顶寄存器
    5. eip:指令寄存器,保存当前指令的下一条指令的地址
  1. 相关汇编命令
    1. mov:数据转移指令
    2. push:数据入栈,同时esp栈顶寄存器也要发生改变
    3. pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
    4. sub:减法命令
    5. add:加法命令
    6. call:函数调用,1. 压入返回地址 2. 转入目标函数
    7. jump:通过修改eip,转入目标函数,进行调用
    8. ret:恢复返回地址,压入eip,类似pop eip命令

3.3函数栈帧的创建和销毁解析过程

  1. 每个编译器下,栈帧的创建是略有差异的
  2. 寄存器:eax、ebx、ecx、edx;重点是ebp和esp
    1. esp:栈顶寄存器
    2. ebp:栈底寄存器
  3. ebp、esp 这2个寄存器中存放的是地址使用这两个地址来维护函数栈帧
    1. 每个函数都有自己ebp和esp来维护函数
  4. 每一个函数调用,都要在栈区上开辟一块空间
  5. 寄存器是用来存储数据的,不管是什么,只用来存储数据

3.4函数的调用

  1. 其实main函数在最开始是被其他函数调用的,有另外一个函数在调用
  2. 图片中调试窗口调用堆栈,是有invoke_main()函数在调用我们的main函数,看第二张图
  3. 如果想打开,调用堆栈这个调试窗口可以这么做:调试 -->窗口 --> 调用堆栈

图二:从调试图中确实如此,的确是invoke_main函数在调用main函数;

图三:调用的invoke_main函数

3.5汇编代码   

  1. 汇编调试所用到的代码
  2. 我是用的环境是vs2022,x86

int main()
{
009C25B0  push        ebp  
009C25B1  mov         ebp,esp  
009C25B3  sub         esp,0E4h  
009C25B9  push        ebx  
009C25BA  push        esi  
009C25BB  push        edi  
009C25BC  lea         edi,[ebp-24h]  
009C25BF  mov         ecx,9  
009C25C4  mov         eax,0CCCCCCCCh  
009C25C9  rep stos    dword ptr es:[edi]  
009C25CB  mov         ecx,9CC008h  
009C25D0  call        009C1320  
int a = 10;
009C25D5  mov         dword ptr [ebp-8],0Ah  
int b = 5;
009C25DC  mov         dword ptr [ebp-14h],5  
int ret = 0;
009C25E3  mov         dword ptr [ebp-20h],0  
ret = Add(a, b);
009C25EA  mov         eax,dword ptr [ebp-14h]  
009C25ED  push        eax  
009C25EE  mov         ecx,dword ptr [ebp-8]  
009C25F1  push        ecx  
009C25F2  call        009C13CA  
009C25F7  add         esp,8  
009C25FA  mov         dword ptr [ebp-20h],eax  
printf("%d\n", ret);
009C25FD  mov         eax,dword ptr [ebp-20h]  
009C2600  push        eax  
009C2601  push        9C7BCCh  
009C2606  call        009C13CF  
009C260B  add         esp,8  
return 0;
009C260E  xor         eax,eax  
}

所用到的代码

#include <stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
 {
	int a = 10;
	int b = 5;
	int ret = 0;
	ret = Add(a, b);
	printf("%d\n", ret);
	return 0;
}
  1. invoke_main以上的函数就不考虑了,接下来转到反汇编,从main函数的第一行开始

3.5.1函数栈帧的创建

注意:每次调试的地址名字不一样,但是逻辑是一样的

int main()
{
002F17E0  push        ebp//把ebp寄存器中的值进行压栈,此时的ebp中存放的是
    //invoke_main函数栈帧的ebp,esp-4  
002F17E1  mov         ebp,esp //将esp的地址给ebp,ebp走到esp位置 
002F17E3  sub         esp,0E4h//esp - 0E4h esp向低地址走,为main函数预开辟空间 
002F17E9  push        ebx     //push ebx esi edi 三个寄存器到栈顶 这三个值随时有可能被修改
002F17EA  push        esi  
002F17EB  push        edi  
002F17EC  lea         edi,[ebp-24h]  // 刷新edi寄存器的位置,就是为main开辟的那块空间进行
002F17EF  mov         ecx,9          // 初始化操作
002F17F4  mov         eax,0CCCCCCCCh // 三行代码结合理解,将这块空间edi 到 ebp之前的9个值全部初始化成0CCCCCCCCh
002F17F9  rep stos    dword ptr es:[edi]  
002F17FB  mov         ecx,2FC008h    // 把对应地址的内容放到寄存器中,寄存器只用来存储数据
002F1800  call        002F1320       // 在执行对应地址指向的函数之前,会先存储下一个指令的地址
    //下一个指令的地址就是 002F1805
	int a = 10;
002F1805  mov         dword ptr [ebp-8],0Ah  
}
  1. 画图理解图,当然后面也有动图理解;编译器部分的调试,大家就多试试,结合着理解

  1. 寄存器是存储数据的,是获取地址中的内容保存到寄存器,或者把寄存器的值保存到内存的对应地址中

3.5.2main函数部分

  1. 经过上面操作,main函数的函数栈帧开辟好了
int main()
{
	int a = 10;
002F1805  mov         dword ptr [ebp-8],0Ah   //把10放到ebp - 8的位置上
	int b = 20;
002F180C  mov         dword ptr [ebp-14h],14h //把20放到ebp - 14h的位置上 

	int ret = add(a, b);                      //实参对形参的拷贝
002F1813  mov         eax,dword ptr [ebp-14h] //拷贝ebp - 14h位置的值,放到eax寄存器
002F1816  push        eax  //然后压栈
002F1817  mov         ecx,dword ptr [ebp-8]   //拷贝ebp - 8位置的值,放到ecx
002F181A  push        ecx  
002F181B  call        002F1023  //调用Add函数前,栈顶保存下一条指令的值,就是002F1820
002F1820  add         esp,8     // 调用函数回来后要销毁 形参拷贝的值
002F1823  mov         dword ptr [ebp-20h],eax   // 放到 eax寄存器里的值,给ebp - 20h,ret
	return 0;
002F1826  xor         eax,eax  
}
  1. 这里有个知识的分享,是不是有时候会打印出或报错出现中文 “烫烫烫烫----”这些字,为什么会这样?其实都是有原因的,
  2. 在内存中就是CCCCCC的初始化,翻译成中文就是“烫烫烫烫----”了,如果出现这样的报错,多半是使用了未初始化的空间
  3. 在执行call指令是要按F11逐行调试,最后会到Add函数中,下面也会讲到

这里可以看看调试图确实是和说的一样,注意:ebp-14h,-减的16进制的,14h == 20

在执行Add函数前到底会不会存储下一条指令的地址,看下面图片解析

这里为什么是倒着存储的,可以去看看这篇博客  整形数据与浮点型的数据在内存中存储的形式,以及大小端字节序(笔记版)

3.5.3Add函数部分

  1. 最开始就可以观察到,Add函数前面部分和main函数逻辑一样的函数栈帧的创建
  2. 最重要的是自己去调试!!!
int Add(int x, int y)
{
009C1790  push        ebp  
009C1791  mov         ebp,esp  
009C1793  sub         esp,0CCh  
009C1799  push        ebx  
009C179A  push        esi  
009C179B  push        edi  
009C179C  lea         edi,[ebp-0Ch]  
009C179F  mov         ecx,3  
009C17A4  mov         eax,0CCCCCCCCh  
009C17A9  rep stos    dword ptr es:[edi]  
009C17AB  mov         ecx,9CC008h  
009C17B0  call        009C1320       //前面函数栈帧的创建就略过了
	int z = 0;
009C17B5  mov         dword ptr [ebp-8],0   //创建变量Z,把值放到ebp - 8的位置上
	z = x + y;
009C17BC  mov         eax,dword ptr [ebp+8] //先取到ebp+8,也就是变量a的拷贝,也就是现在的x,值放到eax寄存器
009C17BF  add         eax,dword ptr [ebp+0Ch] //再取到变量b的拷贝,然后相加,结果放到eax
009C17C2  mov         dword ptr [ebp-8],eax   // 把最终的结果放到ebp - 8,创建的Z变量的位置
	return z;
009C17C5  mov         eax,dword ptr [ebp-8]  //出函数就会销毁变量,所以暂时放到eax寄存器中
}
009C17C8  pop         edi  //Pop,三次,因为变量销毁,空间回收
009C17C9  pop         esi  
009C17CA  pop         ebx  
009C17CB  add         esp,0CCh  //回收之前预开辟的空间
009C17D1  cmp         ebp,esp   //比较ebp和esp位置的值
009C17D3  call        009C1244  //这里调用了其他函数,我也不太清楚
009C17D8  mov         esp,ebp   //把ebp的地址给esp,esp走到ebp的位置
009C17DA  pop         ebp       //Pop ebp,也就是压栈的main函数的ebp
009C17DB  ret                   //ret,回到call下一条指令的位置,可以发现这个程序很严谨
  1. 板书解释
  2. 到了这个,再回看到之前提出的问题,现在应该都可以得到解释了
  3. 前面的Add函数栈帧部分大概的说一下

3.5.4main函数剩下部分

  1. 剩下的部分就粗略说明一下了,函数栈帧的销毁上面的很详细
	ret = Add(a, b);
009C25EA  mov         eax,dword ptr [ebp-14h]  
009C25ED  push        eax  
009C25EE  mov         ecx,dword ptr [ebp-8]  
009C25F1  push        ecx  
009C25F2  call        009C13CA  
009C25F7  add         esp,8  
009C25FA  mov         dword ptr [ebp-20h],eax  
	printf("%d\n", ret);
009C25FD  mov         eax,dword ptr [ebp-20h]  
009C2600  push        eax  
009C2601  push        9C7BCCh  
009C2606  call        009C13CF  
009C260B  add         esp,8  
	return 0;
009C260E  xor         eax,eax  
}
009C2610  pop         edi  
009C2611  pop         esi  
009C2612  pop         ebx  
009C2613  add         esp,0E4h  
009C2619  cmp         ebp,esp  
009C261B  call        009C1244  
009C2620  mov         esp,ebp  
009C2622  pop         ebp  
009C2623  ret

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1846521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无线模块433MHz和2.4GHz的功能与适用性比较

433MHz和2.4GHz这两个频段常用于无线通信中的模块&#xff0c;今天我们就来介绍这两种频段无线模块各自的特点。433MHz和2.4GHz无线模块工作频段都属于国内免许可的ISM开放频段&#xff0c;因此二者使用较为广泛。 433MHz频段无线模块位于超高频(UHF)范围内&#xff0c;具体频…

人类启发的一区新算法|旅行徒步优化算法HOA原理及代码实现(Matlab/Python)

文章来源于我的个人公众号&#xff1a;KAU的云实验台&#xff0c;主要更新智能优化算法的原理、应用、改进 MATLAB PYTHON 目前&#xff0c;元启发式算法按其灵感来源可以分为&#xff1a; &#xff08;i&#xff09;群体启发&#xff0c;如粒子群PSO和灰狼优化算法GWO等&a…

wvp-GB28181-pro 源码分析-点播流程(三)

文章目录 一 、28181-2016标准文档中的点播流程二 、点播流程源码分析2.1 页面发起点播请求2.2 与ZLM协商SSRC信息2.3 订阅zlmediakit的hook消息及发送invite信令2.4 处理invite信令响应并应答2.5 收到ZLM的推流通知2.6 播放成功2.7 停止点播流程2024年6月20日下载的wvp-GB2818…

如何在华为 Ascend 设备上运行模型

模型转换:使用华为的模型转换工具 ATC ATC 在 ascend-cann-toolkit 包里 环境 Docker Image: ascendhub.huawei.com/public-ascendhub/ascend-pytorch:24.0.RC1-A2-2.1.0-ubuntu20.04 镜像版本CANN版本Pytorch版本变更项24.0.RC18.0.RC12.1.0基础镜像变更为 ubuntu20.04。p…

SAP ScreenPersonas

https://developers.sap.com/mission.screen-personas.html 跟着这个练习做一遍就了解了Personas 访问SAP提供的Personas练习系统 申请用户 登录练习系统 随便找一个可以支持Personas的程序搞起来&#xff0c;比如IW51 执行后等它出现这个图标就可以开始了.

Rocky9使用cockpitweb登陆时root用户无法登陆

Rocky9使用cockpitweb登陆时root用户无法登陆 [rootlvs ~]# vim /etc/cockpit/disallowed-users [rootlvs ~]# systemctl restart cockpit 取消disallowed-users中的root&#xff0c;即可访问 ip:9090 登陆。

模式分解的概念(下)-无损连接分解的与保持函数依赖分解的定义和判断、损失分解

一、无损连接分解 1、定义 2、检验一个分解是否是无损连接分解的算法 输入与输出 输入&#xff1a; 关系模式R&#xff08;U&#xff0c;F&#xff09;&#xff0c;F是最小函数依赖集 R上的一个分解 输出&#xff1a; 判断分解是否为无损连接分解 &#xff08;1&#x…

leetcode 动态规划(基础版)三角形最小路径和

题目&#xff1a; 题解&#xff1a; 一种可行的方案是从下到上&#xff0c;避免了从上到下的下标特判。走到每一个位置的最小值等于该位置的上两个位置中的最小值加上该位置的值。 int minimumTotal(vector<vector<int>>& triangle) {int dp[205][205]{0};f…

Origin电源维修高压发生器GEXUS-3 GEXUS-15R-02U

GEXUS-3电源维修 Origin高压发生器维修 Origin电源维修 光电设备电源维修 Origin高压电源维修故障分析应注意两点&#xff1a; 故障分析检测和故障硬件更换&#xff0c;根据高压电源故障提示和工作表现初步判断故障的类型和哪些硬件出了问题&#xff0c;再根据初步判断缩小检测…

rk3588 cpu npu gpu 定频 变频

网上的资料 cpu: npu&#xff1a; GPU: DDR:

现货黄金应用价格行为交易所需要的环境

在现货黄金投资中&#xff0c;投资者常用价格行为交易法来分析走势。简单来说&#xff0c;这种方法就是只看K线和支撑阻力位&#xff0c;顶多加一些简单的指标&#xff0c;以此构建分析和交易的系统。由于价格行为简单易学&#xff0c;现在的投资者或多或少都在使用这个方法。但…

云服务器可以从哪些方面降低开发运维难度

开发和运维工作面临着诸多挑战&#xff0c;如果说现在市场上有可以快速有效解决的方案&#xff0c;那么云服务器绝对是首选&#xff0c;云服务器从多个方面显著降低了其难度。 具象到云服务器的特质中&#xff0c;不得不提的还是云服务器的弹性伸缩&#xff0c;之前的文章里有…

busybox的基本使用记录壹

内核如何启动init进程 init/main.c static int __ref kernel_init(void *unused) {int ret;kernel_init_freeable();/* need to finish all async __init code before freeing the memory */async_synchronize_full();ftrace_free_init_mem();jump_label_invalidate_initmem()…

docker进阶篇

docker进阶篇 (重点) 1、docker run2、docker ps3、docker restart4、docker pull5、docker stop6、docker logs7、docker stats8、docker rm(重点) 9、docker exec10、查看本机镜像docker image11、发布镜像docker commit12、docker save(例如保存为tar给别人用)13、删除镜像1…

家居行业大洗牌,中国品牌“九牧现象”逆势进阶

两件印字白T&#xff0c;一件可能几十块就能买到&#xff0c;Gucci的售价却可达上千元&#xff1b;款式类似的珍珠耳环&#xff0c;有的可能上百元就能买到&#xff0c;香奈儿的却要过万…… 他们最大的差异在哪里&#xff1f;在品牌&#xff0c;以及品牌背后代表的品质与服务…

ArcGIS实现不同地块分类与面积汇总

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 我们要做一个不同地块面积汇总&#xff01; 你有一批地块&#xff0c;不同面积&#xff0c;我们需…

小程序wx.uploadFile异步问题

问题&#xff1a;小程序上传文件后我需要后端返回的一个值&#xff0c;但这个值总是在最后面导致需要这个值的方法总是报错&#xff0c;打印测试后发现这它是异步的。但直接使用 await来等待也不行。 uploadImg.wxml <view class"upload-wrap"><view clas…

URLDNS利用链

利用链分析在我的Github主页 Java反序列化学习 下面写下POC思路 利用点HashMap的readObject private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {// Read in the threshold (ignored), loadfactor, and any hidden stuffs.de…

大数据学习-Hadoop

介绍 是 Apache 的顶级开源项目&#xff0c;一个分布式框架&#xff0c;主要功能&#xff1a; 分布式大数据存储——HDFS 组件分布式大数据计算——MapReduce 组件分布式资源调度——YARN 组件 可以通过它来构建集群&#xff0c;完成大数据的存储和计算 学习起来相对简单&…

中国计算机学会芯片大会 (CCF Chip 2024)

&#x1f31f; 中国计算机学会芯片大会(CCF Chip Conference&#xff0c;简称&#xff1a;CCF Chip) 将于&#x1f4c5; 2024年7月19日至21日在上海市松江区上海富悦大酒店召开。 &#x1f389; #CCF Chip 2024# 主题前瞻&#xff1a;"发展芯技术&#xff0c;智算芯未来&q…