北邮CSAPP第三章之数据格式与程序编码

程序的机器级表示

本章学习内容:汇编代码

高级语言屏蔽了程序的机器级实现。
用高级语言编写的程序可以在不同的机器上运行,汇编代码则于特定机器密切相关

学习汇编代码能理解编译器优化能力,并分析代码中隐含的低效率
此外,高级语言提供的抽象层会隐藏我们想要了解的程序的运行时行为
此外,很多攻击都涉及到程序存储运行时控制信息的方式的细节

逆向工程:通过研究系统和逆向工作,试图了解系统的创建过程

IA32:x86-64的32位前身(机器可以向后兼容IA32程序)

  1. C语言、汇编代码以及机器代码之间的关系
  2. x86-64的细节
  3. C语言的控制结构的实现
  4. 过程的实现
  5. 数组、结构、联合等数据结构的实现
  6. 内存越界问题
  7. 系统易遭受缓冲区溢出攻击的问题
  8. GDB调试器检查机器级程序运行时的行为技巧

历史观点

Intel处理器俗称x86,最开始的时候是单芯片

每个后继处理器都是向后兼容的:较早版本的代码可以在较新的机器上运行

  • 摩尔定律
    晶体管数量以每年大约37%的速率增加=>每26个月翻倍
    摩尔定律:1965年,摩尔预测在未来10年,芯片上的晶体管数量每年都会翻倍。在超过50年的事件里,半导体工业能使得晶体管数目每18个月翻倍

这些年出现了很多与Intel处理器兼容的处理器,例如AMD

经过了数十年的发展,曾经的晦涩难懂的特性已经不再会出现了

数据格式

byte:8位
word:16位
double word:32位
quad word:64位
标准int为双字,64位的指针为四字

x86-64指令集包括对完整的针对字节、字、双字的指令

CIntel数字精度汇编代码后缀大小(字节)
char字节b1
shortw2
int双字l(long word)4
long四字q8
char *四字q8
float单精度s4
double双精度l8

大多数GCC生成的汇编代码指令带有一个字符的后缀,表明操作数的大小
例如:数据传送指令有四种:

  1. movb
  2. movw
  3. movl
  4. movq

浮点数的双精度也是使用l代表的,但是并不会引起歧义,因为浮点数使用一组完全不同的指令与寄存器


假设有两个C程序,使用以下命令进行编译:
gcc -Og -o p p1.c p2.c

  1. gcc:GCC编译器
  2. -Og:生成复合原始C代码整体结构的机器代码的优化等级(使用高等级编译产生的代码会严重变形,以至于产生的机器代码与源代码之间的关系难以理解)

编译流程(见第一章):
.c -> .i -> .s -> .o -> 可执行
.o:目标代码文件,是机器代码的一种形式,包含所有指令的二进制表示,但是未填入全局值的地址

机器级代码

计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏实现的细节
两种最重要的抽象:

  1. 指令集体系结构或指令集架构(ISA)定义机器级别程序的格式与行为。它定义了处理器状态、指令的格式以及每条指令对状态的影响。大多数ISA将程序的运行描述得好像是一条一条执行的,但是事实上它们是并发执行的
  2. 机器级程序使用的内存地址是虚拟地址,提供的内存模型看上去像一个很大的字节数组

存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来的

汇编代码表示非常接近机器代码,且比起二进制格式,文本格式可读性更好

一些通常对C语言程序员隐藏的处理器状态在机器代码中都是可见的

  1. 程序计数器(PC):在x86-64中使用%rip表示
  2. 整数寄存器文件:包含16个命名的位置,可以存储指针或整数数据,也可以用来记录某些重要的程序状态。其它寄存器用来保存临时数据,例如过程参数、局部变量、函数返回值
  3. 条件码寄存器:最近执行的算术或逻辑指令的状态信息。实现数据流中的条件变化。
  4. 一组向量寄存器:存放一个或多个整数或浮点数值

机器代码只是简单地将内存看成一个很大的、按字节寻址的数组,C语言的各种结构在机器代码中用一组连续的字节表示,不区分有符号或者无符号的整数、指针。

程序内存:

  1. 可执行机器代码
  2. 操作系统需要的信息
  3. 管理过程调用和返回的栈
  4. 用户分配的内存块

在任意的给定的时刻,只有一部分虚拟地址被认为是合法的。

一条机器指令只执行一个非常基本的操作。

代码示例

在命令行上使用-S选项就能看到C语言编译器产生的汇编代码

  • helloWorld的编译结果:
	.file	"hello.c"
	.text
	.def	__main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
.LC0:
	.ascii "Hello, world!\0"
	.text
	.globl	main
	.def	main;	.scl	2;	.type	32;	.endef
	.seh_proc	main
main:
	subq	$40, %rsp
	.seh_stackalloc	40
	.seh_endprologue
	call	__main
	leaq	.LC0(%rip), %rcx
	call	puts
	movl	$0, %eax
	addq	$40, %rsp
	ret
	.seh_endproc
	.ident	"GCC: (x86_64-win32-seh-rev0, Built by MinGW-W64 project) 8.1.0"
	.def	puts;	.scl	2;	.type	32;	.endef

每一个缩进去的行都相当于一条机器指令
机器执行的只是一个字节序列,它对于产生这些序列的源代码一无所知

  • 反汇编器:用于查看机器代码的文件内容
    在Linux中objdump可以作为反汇编器使用
    linux> objdump -d hello.o

  • 使用反汇编器反汇编hello.o的结果

0000000000000000 <main>:
   0:	48 83 ec 08          	sub    $0x8,%rsp
   4:	48 8d 35 00 00 00 00 	lea    0x0(%rip),%rsi        # b <main+0xb>
   b:	bf 01 00 00 00       	mov    $0x1,%edi
  10:	b8 00 00 00 00       	mov    $0x0,%eax
  15:	e8 00 00 00 00       	callq  1a <main+0x1a>
  1a:	b8 00 00 00 00       	mov    $0x0,%eax
  1f:	48 83 c4 08          	add    $0x8,%rsp
  23:	c3                   	retq   
  • 每组都是一个指令。右边是等价的汇编语言

  • 机器代码与反汇编表现的特性:

  1. x86-64的指令长度从1到15个字节不等,常用的指令以及操作数较少的指令的字节数较少
  2. (没看懂)设计指令格式的方式是,从某个给定位置开始,可以将字节唯一地解码成机器指令。例如只有pushq %rbx是以字节53开头的
  3. 反汇编器只是基于机器代码文件中的字节序列来确定汇编代码的。
  4. 反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有细微差别。它省略了很多指令结尾的q。
  • 生成可执行的代码需要对一组目标代码文件运行链接器
    进行链接后文件变大了,因为它不仅包含着两个过程的代码,还包含着用来启动和终止程序的代码以及与操作系统交互的代码

关于格式的注解

GCC产生的汇编代码一方面包含一些我们不关心的信息,另一方面不提供任何程序的描述
所有以.开头的都是指导汇编器与链接器工作的伪指令,可以忽略

对于一些应用程序,程序员必须用汇编代码来访问机器的低级特性
一种方法是用汇编代码编写整个函数
另一种方法是利用GCC的支持,直接在C程序中嵌入汇编代码

ATT与Intel汇编代码格式

ATT是GCC和一些其它工具的默认格式
其它工具,包括Microsoft的工具,使用Intel格式
GCC可以产生Intel格式的代码:
linux> gcc -Og -S -masm=intel mstore.c

Intel与ATT在格式方面的不同:

  • Intel代码省略了指示大小的后缀
  • Intel代码省略了寄存器名字前的%
  • Intel代码使用不同的方式描述内存中的位置:
    使用QWORD PTR [rbx]而不是(%rbx)
  • 在带有多个操作数时,列出的操作数顺序相反

将C程序与汇编代码结合起来(找一个时间进行试验)

有一些机器特性是C程序访问不到的
例如:每次x86-64处理器执行算术或者逻辑运算时,若得到的计算结果的低8位中有偶数个1,则将一个名为PF(parity flag)的1位条件码设为1,否则设为0
而在C语言中,需要得到这些信息至少需要7次移位、掩码或者异或计算

在程序中插入汇编代码指令就可以轻松完成该任务

在C程序中插入汇编代码:

  • 编写完整的函数,放入一个独立的汇编代码文件,让汇编器与链接器将其与C语言代码合并
  • 使用GCC内联汇编的特性,使用asm伪指令在C程序中包含简短的汇编代码(好处:减少了与机器相关的代码量)

热门文章

暂无图片
编程学习 ·

Python/PTA--第2章 分段计算居民水费 (10分)

第2章-13 分段计算居民水费 (10分) 为鼓励居民节约用水&#xff0c;自来水公司采取按用水量阶梯式计价的办法&#xff0c;居民应交水费y&#xff08;元&#xff09;与月用水量x&#xff08;吨&#xff09;相关&#xff1a;当x不超过15吨时&#xff0c;y4x/3&#xff1b;超过后…
暂无图片
编程学习 ·

那些貌似真诚的人

是不是经常遇到这种人&#xff0c;大家的评价都是他很真诚&#xff0c;人也很简单&#xff0c;事情好&#xff0c;事情往往会有利于他&#xff0c;而且他过的很快乐。 残酷的现实是没有简单的快乐&#xff0c;只要是丰富的快乐&#xff0c;他就不会简单。一个人貌似真诚不见得…
暂无图片
编程学习 ·

JetBrains软件怎么设置中文?值得一看!

JetBrains是一家捷克的软件开发公司&#xff0c;此公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境&#xff1a;IntelliJ IDEA。jetbrains全家桶基本都是英文的&#xff0c;有的朋友使用起来很不方便&#xff0c;那么jetbrains全家桶怎么汉化呢&#xff1f;…
暂无图片
编程学习 ·

ElasticSearch应用篇-搜索增强

ES有一些非常强大的能力&#xff0c;例如&#xff1a;根据用户搜索的时候&#xff0c;也可以搜索同义词&#xff0c;也可以基于语义进行分词&#xff0c;返回最最适合的结果&#xff0c;ElasticSearch是如何实现这种能力的呢&#xff1f; 一、ES搜索与Analyzer 1.ES搜索过程 …
暂无图片
编程学习 ·

框架的诞生-零:为什么写框架?

框架的诞生-零&#xff1a;为什么写框架&#xff1f;题外话什么是框架&#xff1f;框架解决什么问题&#xff1f;为什么写框架&#xff1f;为了造一个更好更适合的轮子为了学习和实践总结一些游戏客户端框架参考心里话框架开发系列文章最后题外话 大家好&#xff0c;很高兴&am…
暂无图片
编程学习 ·

7、外观模式

外观模式 文章目录外观模式概述结构结构实现练习源代码抽象外观类案例外观模式优/缺点与适用环境优点缺点适用环境概述 外观模式&#xff1a;为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 外观模式…
暂无图片
编程学习 ·

动态规划之股票问题123

问题描述&#xff1a; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意: 你不能同时参与多笔交易&#xff08;你必须在再次购买前出售掉之前的股票&#xff09;。 来源&a…
暂无图片
编程学习 ·

安装gradle

安装gradle1、下载二进制包解压2、配置环境变量3、验证&#xff0c;如下成功4、配置Gradle仓库源5、idea配置1、下载二进制包解压 2、配置环境变量 3、验证&#xff0c;如下成功 4、配置Gradle仓库源 在Gradle安装目录下的 init.d 文件夹下&#xff0c;新建一个 init.gradle …
暂无图片
编程学习 ·

CodeGen标记循环

CodeGen标记循环 标记循环是一个模板文件构造&#xff0c;它允许您迭代CodeGen拥有的标记信息的集合。为了使用标记循环&#xff0c;必须基于至少定义了一个字段标记的存储库结构生成代码。 标记循环由一对匹配的<Tag_LOOP>和</Tag_LOOP>标记分隔&#xff0c;它们围…
暂无图片
编程学习 ·

CodeGen按钮循环

CodeGen按钮循环 按钮循环是一个模板文件构造&#xff0c;它允许您迭代CodeGen拥有的按钮信息集合。 在按钮循环中处理的按钮的定义可以来自两个位置之一。 如果基于UI工具箱输入窗口定义进行处理&#xff0c;则默认情况下&#xff0c;按钮集合由该输入窗口定义中的按钮确定。否…
暂无图片
编程学习 ·

CodeGen准备存储库

CodeGen准备存储库 CodeGen几乎总是与提供用于生成源文件的元数据的存储库结构一起使用&#xff0c;并且许多令牌需要使用存储库结构。 基本要求是有一个结构定义&#xff0c;并且该结构定义包含一个或多个字段定义。有些标记还要求定义键&#xff0c;有些则需要具有结构赋值的…
暂无图片
编程学习 ·

2020年PMP笔记归纳第六章项目进度管理

学习目标&#xff1a; 掌握第六章项目进度管理 学习内容&#xff1a; 内容章节 6.1 规划进度管理 6.2 定义活动 6.3 排列活动顺序 6.4 估算活动持续时间 6.5 制定进度计划 6.6 控制进度 第六章PMBOK概述中的重点内容 具有未完成项的迭代型进度计划。基于适应型生命周期的滚动…
暂无图片
编程学习 ·

无人机项目跟踪记录--失败

查看了源代码&#xff0c;发现无法往下进行了&#xff0c;原因如下&#xff1a; 1、这个第二代机移植了cleanfly开源的源码程序&#xff0c;似乎这个开发板的目的是为了学习cleanfly源码的&#xff0c;而不是为了初步了解无人机的&#xff0c;已经是个很高阶段了&#xff0c;超…
暂无图片
编程学习 ·

Python3正则表达式之:(?(id/name)yes-pattern|no-

Python3正则表达式之&#xff1a;(?(id/name)yes-pattern|no-pattern)条件性匹配 1. 用途 (?(id/name)yes-pattern|no-pattern)的作用是&#xff1a; 对于给出的id或者name&#xff0c;先尝试去匹配 yes-pattern部分的内容&#xff1b; 如果id或name条件不满足&#xff0…
暂无图片
编程学习 ·

socket简单使用(Android、c、QT不同场景下使用)

一&#xff1a;Socket使用场景&#xff1a; socket做网络通信使用&#xff0c;例如游戏中的聊天&#xff0c;IM聊天&#xff08;QQ微信等社交&#xff09;&#xff0c;这些是大型的场景&#xff1b;还有一些是次一等的场景&#xff0c;例如一套本地使用的软件&#xff0c;需要…
暂无图片
编程学习 ·

网络信号与数制转换

文章目录信号与传输介质信号双绞线双绞线的连接规范光纤概述光纤的分类光纤接口无线传输介质无线电波微波天线计算机的数质信号与传输介质 信号 什么是信号&#xff1a;信息&#xff0c;数据&#xff0c;信号 信号的分类&#xff1a;①模拟信号②数字信号 模拟与数字的区别&a…
暂无图片
编程学习 ·

樊昌信版通信原理期末复习第一章绪论

第1章 绪论 一、知识点梳理 1、通信的目的&#xff1a;传递消息中所包含的信息。 2、消息&#xff1a;是物质或精神状态的一种反映。 3、信息&#xff1a;是消息中包含的有效内容。 4、通信系统的一般模型 信源输入变换器&#xff1a;将非电物理量变成电信号。 发送设备&…