1.栈的介绍-C语言调用函数(一)

news2025/1/15 13:14:58

目录

源代码变为可执行文件的过程

栈是什么

入栈过程

出栈过程

C语言函数调用栈 

寄存器

EAX,EBX,ECX,EDX

寄存器使用约定

栈帧

调用的解释

释放的解释

堆栈操作

函数调用的操作

堆栈的主要指令

push/pop

call/leave/ret

函数序和函数跋


我们在数据结构中学习过栈

最基本的 前进后出 我们pwn中的栈溢出就是这里的运用栈的特点

源代码变为可执行文件的过程

在高级语言进行汇编的时候 例子为gcc编译

 这四个步骤

gcc处理c语言过程

预处理 :对头文件进行处理 删除注释等处理
编译:将预处理完的代码进行语法分析 语法优化 语义分析  然后生成 汇编语言
汇编 : 通过汇编指令 和机器码的的对照表进行翻译
转换为机器语言
链接:分为静态链接和动态链接 gcc默认使用动态
    静态链接 : 将目标代码直接复制到可执行文件代码中 生成的代码会很大 但是可以运行在没有任何依赖库的机器上
    动态链接 :需要的函数从共享库中动态分配到内存中,程序执行的时候会去调用函数,生成的代码会较小,但是需要运行在有共享库的机器上

链接是将汇编后还没有解析的符号 通过库进行配对 然后再生成可执行文件的过程

我们了解完 文件是如何变为可执行文件后

我们要知道 我们汇编完 栈是什么

栈是什么

在汇编后 程序中不止包含着栈 还有很多东西

可执行文件在运行的时候开辟的虚拟内存空间
栈就是虚拟内存空间一部分 通常用来存储函数调用信息和局部变量
要注意 
程序的栈是从进程地址空间的高地址向低地址增长的

入栈过程

出栈过程

C语言函数调用栈 

我们现在来具体说明C语言函数调用栈

程序的执行 我们可以把他看做一个连续的过程
程序执行完后要回到调用函数的下一条指令(在call后面的那一条)继续执行

 执行函数puts

执行完后

是回到下一个add的

 所以我们来梳理一下函数堆栈的过程

1. 主函数调用函数  call 函数名
2. 被调用函数通过 mov ebp esp 来保存现在的栈顶
3. 被调用函数通过堆栈的方式 存入所需要的局部变量和寄存器
4. 被调用函数执行完 将返回值存入某个寄存器 并且执行ret操作 会将栈顶弹出 并且把控制器返回主函数


ebp是栈底
esp是栈顶 

mov ebp esp 是因为要执行函数了 所以把栈底ebp指向call 然后开始进行堆栈

这里堆栈的过程我们后面进行说 

我们先看看 调用函数必不可少的 寄存器

寄存器

寄存器是处理器用来加工,运行函数必不可少的东西
用于存放函数的 数据和指令


所以函数调用栈 离不开 寄存器
intel 32 包括8个4字节的寄存器

 前6个寄存器均可作为通用寄存器使用

但是某些功能又需要特定的寄存器来使用

例如

函数返回值通常保存在%eax

开始解读

EAX,EBX,ECX,EDX

这四个可以进行拆分

可以分为两个独立的十六位寄存器

高寄存器/高地址 : AX BX CX DX
低寄存器/低地址 :AL,AH  BL,BH  CL,CH  DL,DH

当EAX为低寄存器时 又可以拆分为 两个独立的八位寄存器

高字节 : AH,BH,CH,DH
低字节 : AL,BL,CL,DL

在汇编语言的使用中 会使用 %或者直接调用

mov $5 %eax
mov eax 5


两个都是将立即数5 赋值给eax

在 64位和32位中 寄存器的称呼也不一样

64位中 以R开头 例如 RAX RBX
32位中 以E开头 例如 EAX EBX

寄存器使用约定

EAX,ECX,EDX 为主函数调用的寄存器 在代码执行后 主函数希望有这些寄存器的控制



EBX,ESI,EDI 为被调用函数的寄存器 所以主函数如果使用了这些寄存器
要先将这些寄存器的值压入栈内 然后被调用函数使用这些寄存器 

然后再把主函数的值 返回给这些寄存器 因为这些寄存器 主函数可能也要用



ESP EBP 被调用函数要保持的寄存器 并且在被调用函数运行完 要恢复为调用前的样子
就是恢复为主函数的栈帧

栈帧

在执行程序的时候 栈不一定是一个函数 而是很多函数的嵌套

同一时刻 栈内会有很多函数的信息

每一个没有执行完毕的函数都占有一片连续的空间 我们把这叫做栈帧

栈帧是堆栈的片段

在调用函数的时候  
1.逻辑栈帧压入堆栈内 就是一个函数的栈帧
2.函数返回的时候 逻辑栈帧被弹出堆栈

逻辑栈帧里面存放着 函数的参数  局部变量 还有恢复为前一个栈帧所需要的数据

 

 栈帧的边界是esp和ebp确定

EBP为栈底 在栈内位置固定

ESP为栈顶 每次有新的参数加入 都要对栈顶做减法

 注意 这里的主函数和被调用函数都是该函数的栈帧

 其中参数和局部变量可以没有

这里可以发现函数入栈的顺序

实参n-1 -->主函数返回地址 --> 主函数的ebp地址 --> 局部变量1-n

 这里给出一个我学习的困惑 但是已经解决了 就是栈溢出

从这里就可以清晰明了是如何栈溢出

记住我的图是从下面进行入栈和出栈的
参数:垃圾字符填充
局部变量 垃圾字符填充
ebp  垃圾字符填充
返回地址 : shellcode

调用的解释

1.将 实参 由 N-1 进行入栈
2.进入被调用函数
3.把主函数的ebp的值压入栈内
4.把主函数的esp的值 赋值给 被调用函数的ebp 作为被调用函数的栈底
5.esp进行规划空间

 这个时候 被调用函数的ebp

这里的前一个栈帧的地址 就是主函数地址

向上可以得到主调函数的返回地址,实参

向下可以得到局部变量和参数

释放的解释

在函数执行完后

1.将被调用函数的ebp 赋值给 被调用函数的esp
这样 局部变量和参数 都被释放了
2.把前一个栈帧的ebp地址弹出给EBP
让他返回没有调用函数前的栈底
3.esp继续上移 返回到主函数的栈帧
注意这些

1. 在当前函数执行的时候 EBP始终不改变位置 都是在栈底
2.在函数调用前 ESP指向EBP 即他既指向栈底 又指向栈顶
3.当函数开始执行后 ESP 会一直指向该函数的栈顶
4.如果在函数中开始了另一个函数
那么就会把 第一个函数的EBP作为 旧的EBP压入栈内 然后 新的函数开始从第一个函数的ESP开始压入栈

如果主函数中需要保存 被调用函数的寄存器和临时变量 那么他的栈图应该是

 

堆栈操作

函数调用的操作

(1)主函数将被调用函数的参数 按照约定压入栈内 这个时候 主函数的esp开始移动
x86是直接压入栈内 x64是先将6个通用寄存器存储 后才进行压入栈
(2)主函数把控制器交给被调用函数(call指令)函数的返回地址(call自动压入)压入栈
返回地址是call函数下一条指令
(3)如果有必要 被调用函数会设置ebp,然后保存被调用函数保存的寄存器
(4)被调用函数通过修改esp来为局部变量进行开辟空间 并且存入局部变量和临时变量
(5)被调用函数要调用主函数传入的值 如果被调用函数返回一个值 多半是从EAX存入
(6)被调用函数执行结束 就先进行修改esp=ebp 然后把旧ebp赋值给ebp 然后就能释放空间
(7)恢复被调用函数控制的寄存器
(8)把控制器交还给主函数 (这个操作有可能清除参数)
(9)主函数清除之前存入的参数 把esp修改到(1)前的值

通过9个步骤 我们的函数调用和使用完函数后的操作 就结束了

堆栈的主要指令

push/pop

push 压入栈内  esp-4个字节 以字节为单位将寄存器数据压入栈内
从高字节到低字节依次存入
esp-1 esp-2 esp-3 esp -4的地址上


pop 出栈 栈顶的数据存储到寄存器中  esp+4个字节

很显然
存入数据 栈顶变小 提出数据 栈顶变大
esp


但是esp始终是指向 下一条数据的

call/leave/ret

eip寄存器

EIP是x86架构CPU中的一个寄存器,它存储当前正在执行的指令的地址。EIP代表"Extended Instruction Pointer",可以理解为指向下一条要执行的指令的地址。当CPU执行完一条指令后,会根据当前EIP的值计算出下一条指令的地址,并开始执行下一条指令。程序员可以通过修改EIP的值来改变指令执行的顺序,从而实现程序跳转等操作。

call : 指令寄存器EIP会先将 call指令下一条压入栈内 然后再重新指向被调用函数开始处
leave:用于恢复主函数的栈帧来准备返回
类似于两条汇编指令
mov esp,ebp  把栈底赋值给栈顶 释放空间 返回到主函数
pop ebp 因为栈顶存放的是上一栈帧的地址 所以pop出ebp 就可以返回上一个栈帧地址
ret:和call配套使用 用于从函数或过程返回
从栈顶弹出 返回地址(下一条指令地址)给eip 程序开始指向下一条指令地址
esp会指向 被调用函数返回主函数的地址处

函数序和函数跋

函数调用之初常一同出现 的 我们叫做函数序 类似上面的 (3)(4)


函数调用最后常一同出现 的 我们叫做函数跋 类似上面的 (6)(7)(8)

这里给出常用的函数序和函数跋的汇编

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

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

相关文章

事件抽取:Document-level Event Extraction via Parallel Prediction Networks

任务:给定一篇文章,从中提取出一个或多个结构化的事件 Y { y i } i 1 k Y\{y_i\}_{i1}^{k} Y{yi​}i1k​ ,其中每个事件 t t t事件类型 y i t y_{i}^{t} yit​表示是,包含一系列角色 ( r i 1 , r i 2 , . . . , r i n ) (r_{i}^{1},r_{i}^{…

构建高效数据中台——数据只有被使用起来,才能创造价值

产品经理们时常会碰到这种问题: 我刚上线一个功能,请研发同志们帮我拉个数据出来分析,却被残酷告知需要排期。 我这里急得跺脚,但也只能理解。 数据研发们每天有查不完的数据和写不完的表,业务部门要的数据迟迟拿不到&…

ABAP SM30自定义搜素帮助,且带出相关描述,也可回车带出。

本篇文章用SM30字段BUKRS带出BUTXT为例。 MODULE zfzhdm_f4help INPUT.DATA: lt_dynpfields1 TYPE STANDARD TABLE OF dynpread,ls_dynpfields1 TYPE dynpread,lt_return1 TYPE STANDARD TABLE OF ddshretval,ls_return1 TYPE ddshretval,lv_povstepl1 TYPE sy-stepl…

引领文旅新体验!实时云渲染助力打造“永不落幕”的湾区文采会元宇宙

2022年11月25日至27日,2022年粤港澳大湾区公共文化和旅游产品(东莞)采购会(简称“湾区文采会”)在广东省东莞市文化馆举行。 文采会期间,文采会元宇宙线上虚拟展厅全新亮相,这艘承载着科技与文化…

优秀的FAQ示例及FAQ页面制作技巧

在网页中问答设计中,虽然说客服会话更有人情味、解决效率更高,但从实际的客户使用情况和使用偏好来看,越来越多的人更喜欢自助服务。数据显示,约67%的受访者会优先选择自助服务,91%的客户使用过帮助中心来解决问题。可…

python进阶知识点汇总

一、函数 1、函数的传参: (1)值传递:将实际的参数复制一份传递给形参,函数中修改形参时,不会影响到实际参数; def a(b,c2):return bcprint(a(10)) print(a(123,2)) print(a(2))(2…

Gitlab添加组、创建用户和项目、权限管理

一、创建组 1、使用root管理员权限来创建组,一个组里面包含多个项目分支,可以将用户来添加到组里进行设置权限,不同的组有不同的权限。 2、输入群组名称、描述、和可见性级别,点击创建群组 二、创建用户 1、普通用户只能访问属于…

静态路由综合实验

目录 实验要求 实验步骤 1.子网划分 2.配置IP和环回 3.配置静态路由,实现全网可达 4.配置缺省路由,使R1-R4可以访问5.5.5.5/24 5.配置空接口防环 6.ping测试 实验要求 1.除R5的环回地址固定以外,整个其他所有网段基于192.168.1.0/2…

【C++】二叉搜索树BST

目录 1.二叉搜索树的性质2.二叉搜索树功能的实现1.二叉搜索树的框架2.插入3.查找4.删除(难点)解析 3.二叉搜索树功能的递归实现1.查找递归实现2.插入递归实现递归形式中新建节点的链接问题 3.删除的递归实现 4.二叉搜索树部分默认成员函数实现1.构造函数…

SpringBoot(8)日志监控

日志监控 监控的意义使用监控监控原理 自定义监控指标Metrics端点自定义端点 监控的意义 监控服务状态是否宕机监控服务运行指标(内存,虚拟机,线程,请求等)监控程序运行日志管理服务(服务上下线) 监控的实施方式 1.显示监控信息的服务器&am…

低功耗设计方法学——篇Ⅱ

引言 低功耗设计关乎ASIC芯片的性能稳定。对ASIC 特别是一些Soc芯片的设计有着重要的影响,随着集成规模的大幅度增加,芯片自身的功耗问题暴露也越来越明显。低功耗设计的需求和必要性也越来越值得关注。本文就《Low Power Methodology Manual For Syste…

逍遥自在学C语言 | 赋值运算符

前言 在C语言中,赋值运算符用于将一个值赋给变量 这个过程分为两个步骤: 计算赋值运算符右侧的表达式将结果赋给左侧的变量。 C语言提供了多个不同的赋值运算符,包括基本的赋值运算符、复合赋值运算符以及条件赋值运算符等 一、人物简介…

LeetCode121 买卖股票的最佳时机 遍历法和动态规划

题目地址:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/ 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股…

Python爬虫之MongoDB

目录 一、Mongo概述 二、安装&下载 1.下载: 2.安装 三、基本命令 插⼊数据 查询数据 修改数据 删除数据 索引 四、Python与MongoDB交互 1.安装pymongo 2.使⽤ 一、Mongo概述 MongoDB是什么? MongoDB是⾮关系型数据库(No sql) 为啥需要…

无线传感网络课程作业 1-dijkstra算法计算最短路径并输出经过的节点

无线传感网络课程作业 1 仅供参考-如有需要可订阅专栏 题目有如下网络图: abc三个数以a=3,b=1,c=1为例 运行环境:anaconda3环境管理工具,Python 3.9.12,Windows 10 22H2算法实现原理流程i).

Java——二叉搜索树的后序遍历序列

题目链接 牛客在线oj题——二叉搜索树的后序遍历序列 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回 true ,否则返回 false 。假设输入的数组的任意两个数字都互不相同。 数据范围: 节点数量 0≤n≤1000 …

PLECS的基本介绍

PLECS的基本介绍 一:PLECS的基本介绍简单说明二:多功能的辅助工具三:主要功能介绍1、独特的热分析功能2、功能强大的示波器3、极快的仿真速度4、强大的波形分析工具5、C 语言控制器6、丰富的元件库 一:PLECS的基本介绍简单说明 1&…

QMS-云质说质量 - 7 IATF 16949哪个条款严重不符合项最多?

云质QMS原创 转载请注明来源 作者:王洪石 引言 AIAG 《质量2020》报告的数据是否让你惊讶? AIAG与德勤合作发布的汽车行业《质量2020》报告指出,"OEMs和供应商都将问题解决和CSR(Customer Specific Requirement顾客特定要求…

What...MiniGPT-4居然开源了,提前感受 GPT-4 的图像对话能力

说在前面的话: 一个月前,OpenAI向外界展示了GPT-4如何通过手绘草图直接生成网站,令当时的观众瞠目结舌。 在GPT-4发布会之后,相信大家对ChatGPT的对话能力已有所了解。圈内的朋友们应该已经亲身体验过无论是文本生成、编写代码&…

涨点技巧:基于Yolov5/Yolov7的困难样本挖掘---LRM loss,提升难样本检测精度

1.hard example mining(困难样本挖掘)✨✨✨ 困难例挖掘方法通常可以提高目标检测器的性能,因为它受到不平衡训练集的影响。为了通过RoI正确地挖掘困难例,引入了在线困难例挖掘(OHEM)方法[15]。该方法建议只考虑对反向传播最有利的RoI。给出最高损失值的RoI被认为是最难的…