C语言代码的x86-64汇编指令分析过程记录

先通过Xcode创建一个terminal APP,语言选择C。代码如下:

#include <stdio.h>int main(int argc, const char * argv[]) {int a[7]={1,2,3,4,5,6,7};int *ptr =(int*)(&a+1);printf("%d\n",*(ptr));return 0;
}

在return 0处打上断点,并且Xcode菜单里选择Debug|Debug Workflow|Always Show Disassembly,点击运行。这时候断点会跳到汇编代码里,得到汇编代码如下:

Terminal`main:0x100003ec0 <+0>:   pushq  %rbp0x100003ec1 <+1>:   movq   %rsp, %rbp0x100003ec4 <+4>:   subq   $0x50, %rsp0x100003ec8 <+8>:   movq   0x131(%rip), %rax         ; (void *)0x00007ff84ef2f8a0: __stack_chk_guard0x100003ecf <+15>:  movq   (%rax), %rax0x100003ed2 <+18>:  movq   %rax, -0x8(%rbp)0x100003ed6 <+22>:  movl   $0x0, -0x34(%rbp)0x100003edd <+29>:  movl   %edi, -0x38(%rbp)0x100003ee0 <+32>:  movq   %rsi, -0x40(%rbp)0x100003ee4 <+36>:  movq   0xa5(%rip), %rax0x100003eeb <+43>:  movq   %rax, -0x30(%rbp)0x100003eef <+47>:  movq   0xa2(%rip), %rax0x100003ef6 <+54>:  movq   %rax, -0x28(%rbp)0x100003efa <+58>:  movq   0x9f(%rip), %rax0x100003f01 <+65>:  movq   %rax, -0x20(%rbp)0x100003f05 <+69>:  movl   0x9d(%rip), %eax0x100003f0b <+75>:  movl   %eax, -0x18(%rbp)0x100003f0e <+78>:  leaq   -0x30(%rbp), %rax0x100003f12 <+82>:  addq   $0x1c, %rax0x100003f16 <+86>:  movq   %rax, -0x48(%rbp)0x100003f1a <+90>:  movq   -0x48(%rbp), %rax0x100003f1e <+94>:  movl   (%rax), %esi0x100003f20 <+96>:  leaq   0x85(%rip), %rdi          ; "%d\n"0x100003f27 <+103>: movb   $0x0, %al0x100003f29 <+105>: callq  0x100003f5a               ; symbol stub for: printf0x100003f2e <+110>: movq   0xcb(%rip), %rax          ; (void *)0x00007ff84ef2f8a0: __stack_chk_guard0x100003f35 <+117>: movq   (%rax), %rax0x100003f38 <+120>: movq   -0x8(%rbp), %rcx0x100003f3c <+124>: cmpq   %rcx, %rax0x100003f3f <+127>: jne    0x100003f4d               ; <+141> at main.c
->  0x100003f45 <+133>: xorl   %eax, %eax0x100003f47 <+135>: addq   $0x50, %rsp0x100003f4b <+139>: popq   %rbp0x100003f4c <+140>: retq   0x100003f4d <+141>: callq  0x100003f54               ; symbol stub for: __stack_chk_fail0x100003f52 <+146>: ud2    

首先介绍下面会用到的几个寄存器:

rip :程序计数寄存器
rsp : 栈指针寄存器,指向栈顶
rbp : 栈基址寄存器,指向栈底
edi : 函数参数
rsi/esi : 函数参数
eax : 累加器或函数返回值用

1、 pushq %rbp
将rbp的地址压栈,rsp继续指向栈顶

2、 movq %rsp, %rbp
将栈顶rsp的值赋值给栈底rbp,

3、 subq $0x50, %rsp
栈顶往下移5*16个字节,可以理解成给后面预留的80字节的空间。X64中栈开辟的大小都是0x10的倍数。

4、movq   0x131(%rip), %rax         ; (void *)0x00007ff84ef2f8a0: __stack_chk_guard

0x131(%rip)的意思是将下一条指令的地址(0x100003ecf)加上0x131得到目标地址(0x100004000),然后取得其中的8字节值,设置给rax寄存器。

选中Debug workflow | View Memory,在Address里输入‘0x100004000’然后回车,我们可以看到此处的内容为0x00007ff84ef2f8a0(注意大小端问题):

 5、movq   (%rax), %rax

将rax寄存器所存地址指向的值,传给rax寄存器。类似前面的操作,我们发现此处的值为0x55d1d55afee700d6:

 6、movq   %rax, -0x8(%rbp)

把rax中的值,也就是上面这个图上所示的8个字节,存入rbp-0x8的位置。我们先打印下rbp和rsp的值,然后跳到rsp处查看内存:

 

红框处即是我们存放值的地方。右边8字节则是rbp指向的位置。

7、movl   $0x0, -0x34(%rbp)

将4字节0设置到rbp-0x34的位置,这里的目的是将下一条指令中-0x38(%rbp)的高字节清零。该位置为0x7ff7bfeff3ac:

 8、movl   %edi, -0x38(%rbp)

该命令保存edi寄存器的值到rbp-0x38位置,也就是上图的0x7ff7bfeff3a8,值是1。前面我们说edi是用来保存函数参数的,也就是int argc,在这个例子中argc的值为1,所以edi寄存器的值为1。

9、movq   %rsi, -0x40(%rbp)

该命令保存rsi寄存器的值到rbp-0x40位置,也就是上图的0x7ff7bfeff3a0,值是0x7ff7bfeff518。这里是参数argv的值0x7ff7bfeff708。由于argv是const char **,因此这也是个地址值,我们前往该地址查看其内容。

10、movq   0xa5(%rip), %rax

 获取位于0x100003eeb+0xa5=0x100003f90处的8字节内容,然后存入rax寄存器:

 11、movq   %rax, -0x30(%rbp)

 将rax寄存器的值存入rbp-0x30的位置:

12-17跟上面两步相同,就是存3,4,5,6,7的值,注意7是单独存的,因为movl表示4字节,而movq代表8字节,也就是2个int。

0x100003eef <+47>:  movq   0xa2(%rip), %rax0x100003ef6 <+54>:  movq   %rax, -0x28(%rbp)0x100003efa <+58>:  movq   0x9f(%rip), %rax0x100003f01 <+65>:  movq   %rax, -0x20(%rbp)0x100003f05 <+69>:  movl   0x9d(%rip), %eax0x100003f0b <+75>:  movl   %eax, -0x18(%rbp)

18、leaq   -0x30(%rbp), %rax

获取rbp-0x30=0x7ff7bfeff3b0,然后存入rax寄存器。

19、addq   $0x1c, %rax

将rax寄存器中的值0x7ff7bfeff3b0,加上0x1c后(0x7ff7bfeff3cc)存入rax寄存器。这里对应代码`int *ptr =(int*)(&a+1);`0x1c是28,也就是数组的大小28字节,说明指针的加法是在编译阶段将数值替换为具体的值,也就是该值乘以指针类型大小后的值。

20、movq   %rax, -0x48(%rbp)

接下去把rax的值0x7ff7bfeff3cc存入rbp-0x48的位置:

21、movq   -0x48(%rbp), %rax

接着又把刚存入的这个值存入寄存器rax

22、movl   (%rax), %esi

把rax寄存器所存地址对应的值(1)存入寄存器esi,这是作为下面要调用的print方法的第二个参数。

23、leaq   0x85(%rip), %rdi          ; "%d\n"

rip=0x100003f27,加上0x85后为0x100003fac,然后设置给rdi寄存器,也就是print调用的第一个参数。0x100003fac这个位置的值刚好是字符串"%d\n"。

24、movb   $0x0, %al

将立即数0存储到al寄存器中。那么如何理解eax,ax,al(ah)之间的关系
专业点可以这样解释:eax是32位寄存器,ax是16位寄存器,al(ah)是八位寄存器。

对于变长参数的函数,要用%al指明用到的vector registers的个数 ,比如printf,这里我们没有用到可变参数,所以要给al寄存器设置0。参考:Why are the %al register and stack modified before calling printf x86 assembly from C "Hello World" program compiled by gcc

汇编函数调用的参数传递 

25、callq  0x100003f5a               ; symbol stub for: printf

调用printf函数。call有一个作用:将call指令的下一条指令地址压栈:

26、movq   0xcb(%rip), %rax          ; (void *)0x00007ff84ef2f8a0: __stack_chk_guard

27、movq   (%rax), %rax

26和27步骤跟4和5处类似,这里不再赘述。

28、movq   -0x8(%rbp), %rcx

将栈底8字节存入rcx寄存器

29、cmpq   %rcx, %rax

比较rcx寄存器和rax寄存器的值是否相等,并把结果写入状态寄存器

30、jne    0x100003f4d               ; <+141> at main.c

如果29的比较结果为不相等,就跳转0x100003f4d处继续执行,也就是35处。相等的话执行31处。这里主要是使用__stack_chk_guard_ptr来判断是否发生栈溢出,导致栈底开始8字节被篡改。可以参考關於__stack_chk_guard_ptr的理解

31、xorl   %eax, %eax

将eax寄存器清零,作为main函数的return值

32、addq   $0x50, %rsp

这句正好对应前面的subq $0x50, %rsp。通过给栈顶指针加上开辟栈的大小,回收栈顶指针开辟的空间。

33、popq   %rbp

这句指令表示出栈,同时将出栈的值放入寄存器rbp

34、retq

这句表示退出main函数,会恢复rip值。本例没有体现这一点,由main函数的调用者保存rip。

35、callq  0x100003f54               ; symbol stub for: __stack_chk_fail

调用__stack_chk_fail函数

36、ud2

UD2 指令的字节编码为 0F 0B,它是一个两字节的指令。在汇编语言中,可以使用 UD2 指令来实现一些特殊的功能,比如触发调试断点或者中断程序的执行。 UD2 指令常用于调试程序。具体可查看:ud2 汇编指令

最终的栈布局

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

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

相关文章

Linux usb设备固定端口号

Linux usb设备固定端口号 一:/sys/bus/usb/devices/二:设备信息三:固定usb设备名方法 一:/sys/bus/usb/devices/ 信息显示如下 1-0:1.0 1&#xff1a;表示 1 号总线&#xff0c;或者说 1 号 Root Hub0&#xff1a;表示端口号1&#xff1a;表示配置号0&#xff1a;表示接口号命…

错过NFT投资,你可能会后悔吗?

在过去的几年里&#xff0c;非同质化代币&#xff08;NFT&#xff09;以其独特性和创新性&#xff0c;迅速成为数字资产领域的热门话题。NFT代表了数字资产的所有权和独特性&#xff0c;为艺术、音乐、游戏和虚拟世界等领域提供了全新的可能性。然而&#xff0c;对于那些错过了…

冠达管理投资前瞻:三星加码机器人领域 大信创建设提速

上星期五&#xff0c;沪指高开高走&#xff0c;盘中一度涨超1%打破3300点&#xff0c;但随后涨幅收窄&#xff1b;深成指、创业板指亦强势震动。截至收盘&#xff0c;沪指涨0.23%报3288.08点&#xff0c;深成指涨0.67%报11238.06点&#xff0c;创业板指涨0.95%报2263.37点&…

gin框架学习

文章目录 配置go环境实现一个简单的web响应服务验证功能gin增加页面以及传递数据 配置go环境 去go官网下载对应的版本 go下载地址 tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz 我们可以编辑 ~/.bash_profile 或者 /etc/profile&#xff0c;并将以下命令添加该文件的末…

【排序算法】python之冒泡,选择,插入,快速,归并

参考资料&#xff1a; 《Python实现5大排序算法》《六大排序算法&#xff1a;插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序》 --代码似乎是C语言 ———————— 本文介绍5种常见的排序算法和基于Python实现&#xff1a; 冒泡排序&#xff08;Bubble Sort&am…

pocky-request网络请求插件

插件下载地址&#xff1a;https://ext.dcloud.net.cn/plugin?id468 插件&#xff1a;https://www.yuque.com/pocky/aaeyux/irx7u0#Oosbz 使用教程&#xff1a; 下载插件main.js中配置&#xff1a; // 导入 import axiosRequest from ./js_sdk/pocky-request/pocky-request…

鉴源实验室丨SOME/IP协议安全攻击

作者 | 张昊晖 上海控安可信软件创新研究院工控网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 引 言 随着汽车行业对于数据通信的需求不断增加&#xff0c;SOME/IP作为支持汽车以太网进程和设备间通信的一种通信协议应…

【Git】Git切换地址

如何切换git代码地址&#xff1f; 1、查看当前远程 url git remote -v执行命令后&#xff0c;可以看见当前有2个URL。 远程 URL 在一般情况下有两个&#xff0c;分别是 fetch 和 push。 fetch URL 是用于从远程仓库获取最新版本的数据。当您运行 git fetch 命令时&#xf…

Oracle-ORA-00600:[ktspffbmb:objdchk_kcbnew_3]

问题背景: 应用执行存储过程报错ORA-00600: 内部错误代码, 参数: [ktspffbmb:objdchk_kcbnew_3], [0], [3303775], [4], [], [], [], [], [], [], [], []&#xff0c;导致过程无法正常执行 ORA-00600: 内部错误代码, 参数: [ktspffbmb:objdchk_kcbnew_3], [0], [3303775], [4]…

结构体和 Json 相互转换(序列化反序列化)

关于 JSON 数据 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也 易于机器解析和生成。RESTfull Api 接口中返回的数据都是 json 数据。 Json 的基本格式如下&#xff1a; { "a": "Hello", "b": "…

机器视觉赛道持续火热,深眸科技坚持工业AI视觉切入更多应用领域

随着深度学习等算法的突破、算力的不断提升以及海量数据的持续积累&#xff0c;人工智能逐渐从学术界向工业界落地。而机器视觉作为人工智能领域中一个正在快速发展的分支&#xff0c;广泛应用于工业制造的识别、检测、测量、定位等场景&#xff0c;相较于人眼&#xff0c;在精…

学习才是测试猿的永动力!超详细的 pytest 钩子函数 之初始钩子和引导钩子来啦

前 言 前几篇文章介绍了 pytest 点的基本使用&#xff0c;学完前面几篇的内容基本上就可以满足工作中编写用例和进行自动化测试的需求。从这篇文章开始会陆续给大家介绍 pytest 中的钩子函数&#xff0c;插件开发等等。仔细去看过 pytest 文档的小伙伴&#xff0c;应该都有发现…

Visual Studio 2022的MFC框架——应用程序向导

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Visual Studio 2022开发工具下的MFC框架知识。 MFC(Microsoft Foundation Class&#xff0c;微软基础类库&#xff09;是微软为了简化程序员的开发工作所开发的一套C类的集合&#xf…

RabbitMQ的安装

RabbitMQ的安装 1、Windows环境下的RabbitMQ安装步骤 使用的版本&#xff1a;otp_win64_23.2 rabbitmq-server-3.8.16 版本说明&#xff1a;https://www.rabbitmq.com/which-erlang.html#compatibility-matrix 1.1 下载并安装erlang RabbitMQ 服务端代码是使用并发式语言…

【vim 学习系列文章 4 - vim与系统剪切板之间的交互】

文章目录 背景1.1.1 vim支持clipboard 检查1.1.2 vim的寄存器 上篇文章&#xff1a;【vim 学习系列文章 3 - vim 选中、删除、复制、修改引号或括号内的内容】 背景 从vim中拷贝些文字去其它地方粘贴&#xff0c;都需要用鼠标选中vim的文字后&#xff0c;Ctrlc、Ctrlv&#x…

怎么绘制汤姆索亚历险记思维导图?掌握这几个绘制步骤就可以

怎么绘制汤姆索亚历险记思维导图&#xff1f;如果你正在为学习汤姆索亚历险记而感到困惑&#xff0c;或者你想要更好地理解小说中的人物关系、情节和舞台背景&#xff0c;那么一个清晰的思维导图就可以帮助你梳理思路。那么下面就给大家介绍一下绘制步骤。 在进行思维导图绘制的…

docker容器监控:Cadvisor +Prometheus+Grafana的安装部署

目录 Cadvisor PrometheusGrafana的安装部署 一、安装docker&#xff1a; 1、安装docker-ce 2、阿里云镜像加速器 3、下载组件镜像 4、创建自定义网络 二、部署Cadvisor 1、被监控主机上部署Cadvisor容器 2、访问cAdvisor页面 三、安装prometheus 1、部署Prometheus…

Clion开发Stm32之存储模块(W25Q64)驱动编写

前言 涵盖之前文章: Clion开发STM32之HAL库SPI封装(基础库) W25Q64驱动 头文件 #ifndef F1XX_TEMPLATE_MODULE_W25Q64_H #define F1XX_TEMPLATE_MODULE_W25Q64_H#include "sys_core.h" /* Private typedef ---------------------------------------------------…

LNMP搭建

LNMP&#xff1a;目前成熟的企业网站的应用模式之一&#xff0c;指的是一套协同工作的系统和相关软件 能够提供静态页面服务&#xff0c;也可以提供动态web服务。 这是一个缩写 L linux系统&#xff0c;操作系统。 N nginx网站服务&#xff0c;也可也理解为前端&#xff0c…

c++画出分割图像,水平线和垂直线

1、pca 找到图像某个区域的垂直线&#xff0c;并画出来 // 1、 斑块的框 血管二值化图&#xff0c;pca 找到垂直血管壁的直线, 还是根据斑块找主轴方向吧// Step 1: 提取斑块左右范围内的血管像素点坐标&#xff0c;std::vector<cv::Point> points;for (int y 0; y <…