当前位置: 首页 > article >正文

反汇编一个简单的C程序

基于最简单的代码示例,一步步分析每一段汇编代码,对于堆栈变化

一、编译C代码

int g(int x) {
    return x + 3;
}
int f(int x) {
    return g(x);
}
int main(void) {
    return f(8) + 1;
}

编译

gcc -S -o main.s main.c -m32

得到汇编代码

g:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    addl    $3, %eax
    popl    %ebp
    ret
 f:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    g
    leave
    ret
main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $4, %esp
    movl    $8, (%esp)
    call    f
    addl    $1, %eax
    leave
    ret

二、关键信息

%ebp:栈帧基址,用于指向当前函数的栈帧基址,使得函数内部可以方便地访问参数和局部变量。
%esp:栈顶指针,始终指向当前栈顶的位置
%eax:寄存器
leave:恢复调用者的栈帧

movl %ebp, %esp
popl %ebp

ret:将 %eax 中的值作为返回值。

三、压栈出栈详细步骤

第一步:进入main函数

pushl %ebp:将基指%ebp 压入栈,保存上一层栈帧基址
堆栈内容

[调用者的 %ebp]

movl %esp, %ebp:将当前栈顶(%esp)存入 %ebp,设定新的栈帧基址
[调用者的 %ebp]

subl $4, %esp:栈顶指针%esp向下移动 4 字节,为局部变量分配空间

[调用者的 %ebp]
[局部变量空间:4字节]

movl $8, (%esp):将立即数 8 压入栈顶,为 f 函数调用提供参数

[调用者的 %ebp]
[参数 x = 8]

call f:调用 f,请看f的堆栈变化,将返回值存入 %eax,栈在跳转到 f 时变化

addl $1, %eax
leave
ret

第二步:f 函数堆栈变化

pushl %ebp:保存调用者的 %ebp 到栈中

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]

movl %esp, %ebp:设置新的栈帧基址,将%esp赋给%ebp

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]

subl $4, %esp:为局部变量分配 4 字节的栈空间

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间:4字节]

movl 8(%ebp), %eax:将%ebp的值 x(即 8)加载到 寄存器%eax
movl %eax, (%esp):将 %eax(x = 8)压入栈,为 g 函数调用提供参数

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]

call g:调用 g,栈跳转到 g 的堆栈变化,可以获得%eax=11

leave
ret

第三步:g 函数堆栈变化

pushl %ebp:保存调用者的 %ebp

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]
[f 的 %ebp]

movl %esp, %ebp:设置新的栈帧基址 堆栈内容不变

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]
[f 的 %ebp]

movl 8(%ebp), %eax:从栈中取出参数 x(8),存入 %eax
addl $3, %eax:将 3 加到 %eax,计算 x + 3 = 8 + 3 = 11

popl %ebp:恢复调用者的 %ebp,清理当前栈帧 %ebp。

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]
[参数 x = 8]

ret:返回调用者,%eax 中的值 11 作为返回值,看回f

第四步:回到 f 函数堆栈变化

Call g :返回值 11 存入 %eax,并释放掉x,恢复调用者的栈帧

[调用者的 %ebp]
[参数 x = 8]
[main 的 %ebp]
[局部变量空间]

leave:恢复调用者的栈帧。

[调用者的 %ebp]
[参数 x = 8]

ret:返回调用者,%eax 中的值 11 作为返回值

第五步:回到 main 函数堆栈变化

call f 返回:f 返回值 11 存入 %eax。

[调用者的 %ebp]

addl $1, %eax:将 1 加到 %eax 中,计算 f(8) + 1 = 11 + 1 = 12。
leave:恢复调用者的栈帧。

堆栈内容为空

ret:返回调用者,%eax 中的值 12 作为程序最终返回值。

最终结果:程序返回值为 12,存储在寄存器 %eax 中。


http://www.kler.cn/a/451367.html

相关文章:

  • 《计算机组成及汇编语言原理》阅读笔记:p86-p115
  • 小程序app封装公用顶部筛选区uv-drop-down
  • 系统压力测试助手——stress-ng
  • 汽车IVI中控开发入门及进阶(44):杰发科智能座舱芯片
  • WebDavClient 安装和配置指南
  • 谷歌浏览器的网络安全检测工具介绍
  • MySQL的架构设计和设计模式
  • 面试记录24年新
  • 乐乐音乐Flutter版
  • OceanBase之primary_one概念学习
  • call、bind、apply的区别
  • Python OCR 文字识别
  • 基于若依的ruoyi-nbcio-plus支持VForm3表单字段数据保存到数据库的一种方法——全网首创(二)
  • 外包干了两年,技术退步明显。。。。
  • 时钟芯片入门指南:从原理到实践
  • 作业帮基于 Apache DolphinScheduler 3_0_0 的缺陷修复与优化
  • HarmonyOS Next 应用元服务开发-分布式数据对象迁移数据文件资产迁移
  • 力扣-图论-18【算法学习day.68】
  • 掌握 Ansys ACP 中的参考方向:简化复杂的复合材料设计
  • 怎么设置电脑密码?Windows和Mac设置密码的方法
  • RPC入门教学(一) ———— RPC介绍与protobuf的介绍与使用
  • QT--信号与槽机制
  • k8s dashboard可视化操作界面的安装
  • 利用Converge许可分析提高软件使用效率
  • windows安装java
  • 深度学习day5|用pytoch实现运动鞋识别