通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

2024-06-06 09:32

本文主要是介绍通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刘子健 

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000




对一下代码进行反汇编分析:

int g(int x)
{return x + 42;
}int f(int x)
{return g(x);
}int main(void)
{return f(42) + 42;
}


我的主机是64位的Linux,所以使用的反汇编代码也是64-bits的.

	.file	"2015_03_01.c".text.globl	g.type	g, @function
g:
.LFB0:.cfi_startprocpushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	%edi, -4(%rbp)movl	-4(%rbp), %eaxaddl	$42, %eaxpopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE0:.size	g, .-g.globl	f.type	f, @function
f:
.LFB1:.cfi_startprocpushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$8, %rspmovl	%edi, -4(%rbp)movl	-4(%rbp), %eaxmovl	%eax, %edicall	gleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1:.size	f, .-f.globl	main.type	main, @function
main:
.LFB2:.cfi_startprocpushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	$42, %edicall	faddl	$42, %eaxpopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE2:.size	main, .-main.ident	"GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"

反汇编得到的代码里面有很多提示信息,提示信息以 . 开头,程序执行时这些提示信息不是指令,我们在这个反汇编样例里面可以精简代码,把这些提示信息删除.有些信息不能剔除,这些信息是编译器必须的,否则你过不了编译链接.

下面是精简后的反汇编代码:以下代码可以通过 gcc ./2015_03_01.s -o ./a.out

	.text.globl	g.type	g, @function
g:pushq	%rbpmovq	%rsp, %rbpmovl	%edi, -4(%rbp)movl	-4(%rbp), %eaxaddl	$42, %eaxpopq	%rbpret.size	g, .-g.globl	f.type	f, @function
f:pushq	%rbpmovq	%rsp, %rbpsubq	$8, %rspmovl	%edi, -4(%rbp)movl	-4(%rbp), %eaxmovl	%eax, %edicall	gleaveret.size	f, .-f.globl	main.type	main, @function
main:pushq	%rbpmovq	%rsp, %rbpmovl	$42, %edicall	faddl	$42, %eaxpopq	%rbpret.size	main, .-main



关于基本汇编指令的分析,我之前有笔记,可以去看这里:

http://blog.csdn.net/cinmyheart/article/details/25558911

我们这里着重分析反汇编代码:

g: , f:, main: 均用来指示函数的入口.

对于函数main.

首先压栈,pushq 指令将rsp寄存器的值减去一个指针长度,在64-bits机器上即8byte,然后将 rbp寄存器的值写入到rsp指向的地址处.

movq %rsp, %ebp指令则将rsp寄存器的值赋值给rbp寄存器.这样一来,属于main函数的栈区域便构建好了.

接着movl 把立即数42赋值给寄存器edi, 然后call指令调用函数f.函数f的返回值会储存在eax寄存器中,等待f调用完之后,会把eax寄存器的值和立即数42相加,并储存在eax寄存器中.最后把rbp寄存器处的值弹栈.然后ret指令返回.

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

call  f 

指令就相当于

push %eip #把当前指令指针寄存器压栈,然后跳转到f处
jump f

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

ret 指令就相当于

popl %eip  #把当前esp寄存器指向地址处的值,赋值给eip

然后把esp寄存器的值减去一个指针长度,即8-byte

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



看看函数f都干了神马.

还是和上面介绍main函数一样的"老规矩",构建函数f的堆栈,

pushq %rbp

movq %rsp, %rbp

接着使用subq $8, %rsp把rsp寄存器的值减去8.

接着把edi寄存器的值赋值给rbp寄存器指向地址处减去4byte的地址处

紧接着,把这个地址处的值赋值给eax寄存器.

把eax寄存器的值又赋值给edi寄存器(其实我想说,这不是吓折腾么...这编译器啊..这期间edi寄存器的值没变)



然后调用函数g

一句话概括就是把edi寄存器的值加上42赋值给eax寄存器,然后返回.(不改变edi寄存器的值)




阐明自己对“计算机是如何工作的”理解:

对于规范化后的程序指令,逐一的对程序指令进行"解释处理".不同的CPU,可能有不同的汇编指令集,比方说Intel -- X86 /X64平台,ARM平台,PowerPC等等,但是他们最基本的的思想都是近似的--冯诺依曼体系结构.

数字计算机的数制采用二进制;计算机应该按照程序顺序执行


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






这篇关于通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1035725

相关文章

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求