[ASM C/C++] C函数调用分析

2023-11-19

  在执行程序时,操作系统为进程分配一块栈空间来保存函数栈帧,esp寄存器总是指向栈顶。x86平台上这个栈是从高地址向低地址增长的,每次调用一个函数都要分配一个栈帧来保存参数和局部变量,C函数参数是按从右到左的顺序入栈的。各个堆栈桢之间是通过把ebp和eip压栈,而串在一起的。参数和局部变量是以栈帧基址(即ebp)为准+内存偏移量来定位和存取的。

下面用GCC 和 GDB 做一下分析

--------------------------

C源代码

int TestB(int c, int *d)
{
    int e = c + (*d);
    *d = e;
    return e;
}

int TestA(int a, int *b)
{
    return TestB(a, b);
}

int main(void)
{
    int i = 5;
    int r = TestA(2, &i);
    return 0;
}

------------------------------

 编译

abeen@ubuntu:~/Documents/C$ gcc -g -o test test.c

启动GDB调试
abeen@ubuntu:~/Documents/C$ gdb test
GNU gdb (GDB) 7.0-ubuntu
...

显示源码
(gdb) list TestB
1    int TestB(int c, int *d)
2    {
3        int e = c + (*d);
4        *d = e;
5        return e;
6    }
7    
8    int TestA(int a, int *b)
9    {
10        return TestB(a, b);

设置断点,使其在TestB的"*d = e"处停止
(gdb) b 4
Breakpoint 1 at 0x80483c5: file test.c, line 4.

开始运行调试程序
(gdb) r
Starting program: /home/abeen/Documents/C/test

Breakpoint 1, TestB (c=2, d=0xbffff444) at test.c:4
4        *d = e;

此时已运到行断点处,查看调用堆栈帧信息
(gdb) bt
#0  TestB (c=2, d=0xbffff444) at test.c:4
#1  0x080483ea in TestA (a=2, b=0xbffff444) at test.c:10
#2  0x0804840c in main () at test.c:16
(gdb) info frame
Stack level 0, frame at 0xbffff420:
 eip = 0x80483c5 in TestB (test.c:4); saved eip 0x80483ea
 called by frame at 0xbffff430
 source language c.
 Arglist at 0xbffff418, args: c=2, d=0xbffff444
 Locals at 0xbffff418, Previous frame's sp is 0xbffff420
 Saved registers:
  ebp at 0xbffff418, eip at 0xbffff41c
(gdb) info frame 1
Stack frame at 0xbffff430:
 eip = 0x80483ea in TestA (test.c:10); saved eip 0x804840c
 called by frame at 0xbffff450, caller of frame at 0xbffff420
 source language c.
 Arglist at 0xbffff428, args: a=2, b=0xbffff444
 Locals at 0xbffff428, Previous frame's sp is 0xbffff430
 Saved registers:
  ebp at 0xbffff428, eip at 0xbffff42c
(gdb) info frame 2
Stack frame at 0xbffff450:
 eip = 0x804840c in main (test.c:16); saved eip 0x4c6b56
 caller of frame at 0xbffff430
 source language c.
 Arglist at 0xbffff448, args:
 Locals at 0xbffff448, Previous frame's sp is 0xbffff450
 Saved registers:
  ebp at 0xbffff448, eip at 0xbffff44c
(gdb) p $esp
$1 = (void *) 0xbffff408

查看分析得知堆栈帧内存布局范围
#0  ~~   0xbffff420
#1 0xbffff420 ~~ 0xbffff430
#2 0xbffff430 ~~ 0xbffff450 

查看此时内存情况
(gdb) x/20xw 0xbffff400
                                                                                        (ebp)        (eip)
0xbffff400:    0x005efff4    0x08049ff4                              0xbffff418    0x080482c4
0xbffff410:    0x00c72d20    0x00000007(TestB:e)             0xbffff428    0x080483ea
0xbffff420:    0x00000002(2再次压栈)    0xbffff444( i地址)    0xbffff448    0x0804840c
0xbffff430:    0x00000002( 2压栈)    0xbffff444( i地址)         0x0804843b    0x005efff4
0xbffff440:    0x08048430    0x00000005( i = 5)                 0xbffff4c8    0x004c6b56
              
继续执行完(*d = e)
(gdb) s
5        return e;
(gdb) x/20xw 0xbffff400
                                                                                        (ebp)        (eip)
0xbffff400:    0x005efff4    0x08049ff4                              0xbffff418    0x080482c4
0xbffff410:    0x00c72d20    0x00000007(TestB:e)             0xbffff428    0x080483ea
0xbffff420:    0x00000002(2再次压栈)    0xbffff444( i地址)    0xbffff448    0x0804840c
0xbffff430:    0x00000002( 2压栈)    0xbffff444( i地址)         0x0804843b    0x005efff4
0xbffff440:    0x08048430    0x00000005( i = 5)                 0xbffff4c8    0x004c6b56
可以看出,C函数参数是按从右到左的顺序入栈的。堆栈桢之间是通过把ebp和eip压栈,来串在一起的。
Frame.jpg

 

后面查看更多参数信息
(gdb) info local
e = 7
(gdb) s
6    }
(gdb) s
TestA (a=2, b=0xbffff444) at test.c:11
11    }
(gdb) info local
No locals.
(gdb) info arg
a = 2
b = 0xbffff444
(gdb) s
main () at test.c:17
17        return 0;
(gdb) info local
i = 7
r = 7
-----------------------------------------------

分析参数和局部变量定位

abeen@ubuntu:~/Documents/C$ gdb a.out

(gdb) set disassembly-flavor intel //设置反汇编指令格式
(gdb) disass main
Dump of assembler code for function main:
0x080483ec <main+0>:    push   ebp
0x080483ed <main+1>:    mov    ebp,esp
0x080483ef <main+3>:    sub    esp,0x18
0x080483f2 <main+6>:    mov    DWORD PTR [ebp-0x4],0x5
0x080483f9 <main+13>:    lea    eax,[ebp-0x4]
0x080483fc <main+16>:    mov    DWORD PTR [esp+0x4],eax
0x08048400 <main+20>:    mov    DWORD PTR [esp],0x2
0x08048407 <main+27>:    call   0x80483d2 <TestA>
0x0804840c <main+32>:    mov    DWORD PTR [ebp-0x8],eax
0x0804840f <main+35>:    mov    eax,0x0
0x08048414 <main+40>:    leave  
0x08048415 <main+41>:    ret    
End of assembler dump.
(gdb) disass TestA
Dump of assembler code for function TestA:
0x080483d2 <TestA+0>:    push   ebp
0x080483d3 <TestA+1>:    mov    ebp,esp
0x080483d5 <TestA+3>:    sub    esp,0x8
0x080483d8 <TestA+6>:    mov    eax,DWORD PTR [ebp+0xc]
0x080483db <TestA+9>:    mov    DWORD PTR [esp+0x4],eax
0x080483df <TestA+13>:    mov    eax,DWORD PTR [ebp+0x8]
0x080483e2 <TestA+16>:    mov    DWORD PTR [esp],eax
0x080483e5 <TestA+19>:    call   0x80483b4 <TestB>
0x080483ea <TestA+24>:    leave  
0x080483eb <TestA+25>:    ret    
End of assembler dump.
(gdb) disass TestB
Dump of assembler code for function TestB:
0x080483b4 <TestB+0>:    push   ebp
0x080483b5 <TestB+1>:    mov    ebp,esp
0x080483b7 <TestB+3>:    sub    esp,0x10
0x080483ba <TestB+6>:    mov    eax,DWORD PTR [ebp+0xc]
0x080483bd <TestB+9>:    mov    eax,DWORD PTR [eax]
0x080483bf <TestB+11>:    add    eax,DWORD PTR [ebp+0x8]
0x080483c2 <TestB+14>:    mov    DWORD PTR [ebp-0x4],eax
0x080483c5 <TestB+17>:    mov    eax,DWORD PTR [ebp+0xc]
0x080483c8 <TestB+20>:    mov    edx,DWORD PTR [ebp-0x4]
0x080483cb <TestB+23>:    mov    DWORD PTR [eax],edx
0x080483cd <TestB+25>:    mov    eax,DWORD PTR [ebp-0x4]
0x080483d0 <TestB+28>:    leave  
0x080483d1 <TestB+29>:    ret    
End of assembler dump.
(gdb)
看汇编指令,参数和局部变量是以ebp为基准+偏移量来定位的。最后用eax寄存器返回值。

转载于:https://www.cnblogs.com/abeen/archive/2009/11/24/1609731.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[ASM C/C++] C函数调用分析 的相关文章

  • TscanCode C/C++静态分析开源分析工具安装与使用

    TscanCode是腾讯静态分析团队开发的一款开源免费的C C 静态分析工具 由于其比较简单实用 准确率较高 并且扫描C C 代码不需要进行编译 所以个人觉得对C C 项目开发挺有帮助的 就简单介绍一下该工具的安装与使用 1 Tscanco

随机推荐

  • 文件包含漏洞-日志注入

    文件目录 一 文件包含漏洞 1 文件包含概述 2 文件包含类型 二 文件包含 日志注入 1 日志注入概述 2 环境准备 3 配置环境 4 模拟网站环境 三 日志注入流程 一 文件包含漏洞 1 文件包含概述 文件包含漏洞是 Web 应用程序中
  • springboot的优化

    在SpringBoot的Web项目中 默认采用的是内置Tomcat 当然也可以配置支持内置的jetty 内置有什么好处呢 在SpringBoot的Web项目中 默认采用的是内置Tomcat 当然也可以配置支持内置的jetty 内置有什么好处
  • 互联网JAVA面试常问问题(三)

    一 volatile原理和使用场景 volatile 原理 volatile变量进行写操作时 JVM会向处理器发送一条Lock前缀的指令 将这个变量所在缓存行的数据写会到系统内存 Lock前缀指令实际上相当于一个内存屏障 也成内存栅栏 它确
  • LED用DMX512协议整个系统怎么连接?

    提问1 EIA485规范只支持 雏菊链 或每段上最多以32个 单元负载 所构成的串行网络 DMX512不是可以支持512个通道吗 那是不是说 超过32个的情况下需要使用中继 提问2 控制器 接收端1 接收端2 接收端n 电阻 GND 这样的
  • BIO、NIO、AIO理解

    一 到底什么是BIO NIO AIO 这些可以理解为是Java语言对操作系统的各种IO模型的封装 程序员在使用这些API的时候 不需要关系操作系统层面的知识 也不需要根据不同操作系统编写不同的代码 只需要使用Java的API就可以了 二 B
  • Eclipse搭建stm32+jlink开发环境全攻略(进阶篇一)

    Eclipse搭建stm32 jlink开发环境全攻略 进阶篇 一 本篇开始讲解一些比较实用的东西 在前面的两章中 我们讲解了eclipse开发stm32的大部分问题 然而 在实际使用过程中 我们仍然会遇到一些不太理想的地方 比如 ecli
  • Leetcode力扣题解 - 30.串联所有单词的子串

    地址 30 串联所有单词的子串 力扣 LeetCode 一 思路 本题关键点是 1 所有关键词长度一致 2 匹配的是所有关键词连接起来的 大体思路 那么我们就可以从字符串头开始 每次只匹配关键词总长度个字符 如果匹配成功 在返回的数组中保存
  • HDMI中的视频时序分析

    一 前言 建立层次观念 说到时序 我们首先想到的例子是IIC SPI 串口等接口的例子 以我们之前的理解 时序就是传输线上电平随时间变化的顺序 但是但是但是 在HDMI这里 我们应该建立一个新的观念 即时序不一定对应到物理层 即传输线上 这
  • python--- end=“ , 单独的一行print()是什么意思

    有如下一道练习题 编写代码打印出下列图形 代码如下 for i in range 4 for j in range 5 print end print 其中end 意思是为末尾end传递一个空字符串 这样print函数不会在字符串末尾添加一
  • 工频干扰频谱测量_【鼎阳硬件智库译文

    英文原文 by Mratin Miller 汪进进 译 鼎阳硬件设计与测试智库发起人之一 简介 多通道串行数据链路容易受到串扰的影响 这些串扰可能来自于相邻通道 也可能是外部的干扰源 Aggressor 其结果是增加了受干扰通道 Victi
  • leetcode数组刷题总结与分析

    文章目录 小结 数组中元素的计算 子序列 任意元素 题目一 两数之和 题目15 三数的和 17 四数之和 16 最接近三数之和 167 两数之和 输入有序数组 560 和为k的子数组 523 连续的子数组的和 53 最大子数组和 713 乘
  • Shell脚本到底是什么高大上的技术吗?

    本文介绍shell脚本知识 学习前最好有linux命令知识储备 一篇文章看完 下次找工作时简历上请写上会shell脚本 栓Q shell脚本是什么 shell脚本就是一个包含shell命令的脚本 常说的linux命令 也可以认为是shell
  • ArrayList与顺序表

    目录 编辑 一 线性表 二 顺序表 1 接口的实现 1 打印顺序表 2 新增元素 3 判定是否包含某个元素 4 查找某个元素对应的位置下标 5 获取 pos 位置的元素 6 获取顺序表长度 7 给 pos 位置的元素设为 value 更新的
  • C++ 一些学习笔记(三) 内存区域

    C 一些学习笔记 三 内存区域 主要是针对之前学习C的时候一些知识点的遗漏的补充 还有一些我自己觉得比较重要的地方 本文章的主要内容是关于程序内存模型的 内存的分区模型 1 程序运行前 2 程序运行后 3 new操作符 主要是针对之前学习C
  • 华为OD机试 - 路灯照明问题(Java)

    题目描述 在一条笔直的公路上安装了N个路灯 从位置0开始安装 路灯之间间距固定为100米 每个路灯都有自己的照明半径 请计算第一个路灯和最后一个路灯之间 无法照明的区间的长度和 输入描述 第一行为一个数N 表示路灯个数 1 lt N lt
  • 课程设计总结

    1 政府职能部门 望细分 具体 课程压缩所致 2 企业家 结构好 利于规划 参考 强烈希望协调与管理融合进来 3 工程师 技术人员 指导行强 望精化 深化 细化 4 学生 利于未来规划 创业 就业 发展 学习方向等等 老师总结课程缺陷 1
  • 虚拟机VMware的安装及使用

    一 虚拟机VMware的安装 1 准备工作 1 需要软件VMware安装包 VMware下载地址 http www uzzf com soft 51188 html 2 需要一个系统镜像 windows系统 http www xitongc
  • 【计算机视觉】最后显示的CIFAR-100数据集照片很模糊怎么解决?

    文章目录 一 前言 二 如何解决 2 1 使用图像增强技术 2 2 使用插值方法 2 3 使用更高分辨率的图像数据集 2 4 手动调整图像尺寸 三 总结 一 前言 如果从CIFAR 100数据集加载的图像显示模糊 可能有几个可能的原因 分辨
  • 小程序中里的bindinput_微信小程序中input标签的使用方法(附代码)

    本篇文章给大家带来的内容是关于微信小程序中input标签的使用方法 附代码 有一定的参考价值 有需要的朋友可以参考一下 希望对你有所帮助 在开发过程中经常遇到这样的需求 用户只能输入数字并且只保留小数点两位 虽然我们可以在提交表单的时候进行
  • [ASM C/C++] C函数调用分析

    在执行程序时 操作系统为进程分配一块栈空间来保存函数栈帧 esp寄存器总是指向栈顶 x86平台上这个栈是从高地址向低地址增长的 每次调用一个函数都要分配一个栈帧来保存参数和局部变量 C函数参数是按从右到左的顺序入栈的 各个堆栈桢之间是通过把