目录
系列文章目录
一、程序编码
1.机器级别代码
2.不同级别的优化
3.数据格式
4.访问信息
4.1 操作数指示符
4.2 操作数示例
4.3 代码中的操作数
4.4 数据传输指令
4.5 数据传输扩展示例
4.6 数据传输扩展重要示例
4.7 数据传输在C与汇编间的转换
4.8 压入弹出栈数据
5.算数与逻辑操作
5.1 加载有效地址
5.2 一元操作与二元操作
5.3 移位操作
5.4 算数运算函数转换示例
5.5 特殊算数操作
5.6 特殊算数操作转换示例
6.控制
6.1 条件码
6.2 访问条件码
6.3 跳转指令
6.4 跳转指令的编码
6.5 跳转指令反汇编示例
6.6 用条件控制来实现条件分支
6.7 条件控制示例
6.8 用条件传送来实现条件分支
6.9 条件传送示例
6.10 循环
6.11 switch
7.过程
7.1 运行时栈
7.2 数据传送
7.3 栈上的局部管理
7.4 局部管理重要案例
7.5 寄存器中的局部存储空间
7.6 递归过程
8.数组
8.1 数组示例
8.2 嵌套数组(多维数组)
8.3 多级数组(指针数组)
8.4 变长数组
8.5 嵌套数组示例
9.异质的数据结构
9.1 结构(体)
9.2 结构体示例
9.3 联合(体)
9.4 数据对齐
10.结合控制和数据
10.1 理解指针
10.2 内存越界引用与缓冲区溢出
10.3 缓冲区溢出示例
10.4 对抗缓冲区攻击
总结
本系列博客重点在深圳大学计算机系统(2)课程的核心内容梳理,参考书目《深入理解计算机系统》(有问题欢迎在评论区讨论指出,或直接私信联系我)。
第一章 深入理解计算机系统01——计算机系统漫游_@李忆如的博客-CSDN博客
第二章 深入理解计算机系统02——信息的表示与处理_@李忆如的博客-CSDN博客
第三章 深入理解计算机系统03——程序的机器级表示
梗概
本篇博客主要介绍深入计算机系统书目第三章程序的机器级表示的相关知识。
Tips:注意后缀(b、w、l、q对应8、16、32、64位)!!!
从左至右分别为64(r开头)、32(e开头)、16、8位(l结尾)大的包括小的 非所有。
ra(b)x ea(b)x a(b)x a(b)l 有规律寄存器须记住。
寄存器中,1、2字节赋值,剩余字节不变。4字节赋值,高位4字节清零。
Tips:1、2字节赋值为低字节赋值,r8~r15为64位系统新增。
大多数指令有一或多个操作数,指出源数据值与目标地址。
寻址方式:1.立即数($开头(找自己) 无$表示绝对地址(找对应地址值)) 2.寄存器(%开头) 3.内存引用(括号括起来)
Tips:比例因子必须是2的幂。
一维数组寻址:基址 + 比例寻址(a【n】= a【0】+ i(数据类型所占字节) x n)
二维数组寻址(假设按行存储):基址 + 比例寻址(b【i】【j】 = b【0】【0】+ i * n * m(一行多少个元素) + j * n(数据类型所占字节))
汇编代码示例(注意后缀!!):
ebx是专用寄存器,不能直接传值。
Tips:内存之间不能对拷 即不能movq (%rdi),(%rsi),应该使用中介寄存器,例如:
Tips:选择有符号扩展和无符号扩展取决于原数据的有无符号。
Tips:一般默认函数的第一参数存入rdi寄存器(与位有关 edi、di、dil),第二个参数存入rsi寄存器(esi、si、sil) ,第三个放入rdx寄存器(edx、dx、dl)
Tips:注意是取寄存器(数与变量)还是寄存器对应的内存(指针值)。
核心原则:先进后出(地址是从栈顶指针递减)
压入弹出栈数据知识总结:
1.栈顶指针为%rsp,与mov的位无关,与系统有关。
2.sub(减)为从栈顶指针向下开辟空间并将栈顶指针下移方便mov数据(push操作),add为从栈顶指针向上寻址取出所需栈中元素(pop操作)。
3.push与pop操作均可用bwlq实现不同类型数据的进出。
常见操作如下:
lea(l和q与计算机系统位数有关)不访问内存,直接将寻址作为值赋给目标地址。
一元操作:只有一个操作数,既是源操作数,又是目的操作数。
二元操作:有两个操作数,第一个为源,第二个为目标。
Tips:lmulq 为 乘,lncq、decq为+1、-1
C语言符号:<< 与 >> 加数字表示移动的位数
左移:按位左移并在右端补0
右移
① 逻辑右移(shl/shr):按位右移在往左端补0(对无符号整数使用)
② 算术右移(sal/sar):按位右移在往左端补最高位有效值(对有符号整数使用)
Tips:移位量只能是立即数或%rcx的%cl中!!
移位规则:
TIps:imull有单操作数和双操作数两种(双操作数的只取低32位),我们可以根据后面的操作数个数作出区别。
除了顺序执行外,还存在着其他方式改变控制与数据流。
条件码(condition code)寄存器,其值描述最近的算术或逻辑操作的属性
leaq不会改变条件码,5中常见指令均会设置条件码。
影响标志位的指令总结:
1、leal/leaq指令是计算地址,不影响标志位
2、INC(+1)/DEC(-1)设置OF和ZF,不设置CF
3、算术和逻辑指令会设置标志位
逻辑操作,CF=0和OF=0
4、移位操作 CF=最后一个移出的位,OF=0
而如下两类指令只设置条件码,不改变其他寄存器:
cmp常用于比较两数是否相等,test常用于判断某数的正负或是否非零。
条件码一般不会直接读取,常用方法如下:
1.利用条件码状态设置某个字节(低位单字节寄存器、一个字节的内存地址)
2.有条件地完成跳转
3.有条件地完成数据传送
标号: 跳转目的地址的标识(label)
1.无条件跳转:jmp + 标号直接跳转或jmp + 寄存器或内存目标中读出的的跳转目标(* + 操作数指示符)
2.条件跳转:je/jne/js/jns/jg/jge/jl/jle/ja/jae/jb/jbe
跳转目的地址:PC相对跳转、绝对跳转
跳转列表指令如下:
Tips:条件跳转只能是直接跳转,后缀与set一一对应。
理解跳转指令的目标如何编码,对第七章链接的的研究非常重要。
以下为汇编及反汇编示例:
链接后的反汇编版本如下:
GCC编译模板如下:
条件控制来实现条件分支简单但也低效,数据的条件转移在一些受限的情况下可行,更符合现代处理器特点。
简单例子及其汇编如下:
条件跳转指令如下:
汇编用条件测试与跳转组合实现。
1.do-while循环
模板如下:
do-while示例
2.while循环
模板如下:
while示例(编译方法1)
while示例(编译方法2)
3.for循环
核心:for转while转goto
模板如下:
for示例代码
switch根据整数索引值实现多重分支。
GCC编译器翻译开关语句(什么时候使用switch)
Tips:稀疏程度过大,建表大量存入&&loc_def,浪费空间。
C语言switch程序示例分析
switch转换示例
Tips:第五行为间接跳转到索引表对饮内存位置(寄存器与变量寄存器一致)。
过程调用机制如下:
Tips:使用callq进行转移控制,原函数下一步地址压栈,返回原函数后出栈。
Tips:当函数所需函数大于6个时才需要从栈申请空间,地址随参数增加增大,参数大小向8对齐(每个参数首地址是8的倍数)。
示例如下:
Tips:%rsp为栈顶指针,保存了返回地址。
Tips:call后会自动向下开空间存返回地址,sub(开空间)一定要add(删除)回来。
Tips:局部变量起始地址和类型对齐。
为了保证程序的正确性(防止值的覆盖),对于某些值需要在过程中保存(也需要开空间)。
Tips:函数返回值一般保存在%rax。
Tips:同样注意值的保存,防止覆盖。
数组占用L(数据类型对应字节)*N(个数)的连续内存(虚拟内存)。
数组的基本原则如下:
Tips:注意是算地址还是算值。
Tips:注意最后一题为求第几个元素,而不是地址差。
Tips:数组内存为连续的。
Tips:指针的地址连续(每个指针占8字节),值为数组首地址。
Tips:分配空间时一定要注意对齐,局部变量对齐数据类型所占字节,如上图。
Tips:注意大小端对数据对齐的影响。
Tips:可以设置不对齐,但是需要以时间换取空间。
Tips:结构体定义中空间与数据类型顺序有关(大数据类型先定义,节省空间)
Tips:指针定义都会自动分配8个字节存本身。上图A2未初始化,所以读取A2【0】会出错。
x86-64 linux内存布局如下:
缓冲区溢出(越界访问了原本已使用的空间)示例如下:
缓冲区溢出定义如下:
Tips:部分库函数也存在缓冲区溢出可能(gets、scanf、strcpy、strcat等等)。
反汇编观察汇编代码可以调查错误原因(字符串默认加‘\0’)
Tips:缓冲区溢出是否报错与数组位并无直接关系,与开辟栈空间(subq)的大小有关(覆盖返回地址才有可能报错)!!!
Tips:由于缓冲区溢出可能改变返回地址,故可以在栈空间内注入攻击代码完成黑客攻击。
攻击示例如下:
代码解读:要完成简单的缓冲区攻击,需要将buf的首地址覆盖原返回地址,并在buf有效位数中填入攻击内容(填充区可放入除‘EOF’与‘\n’外其他字符,gets会结束)
如本程序中将buf定义为全0,将buf地址覆盖原gets返回地址,使gets每次返回都是0。
对抗缓冲区攻击有三大方面
1.避免溢出漏洞(eg.避免使用有漏洞的(库)函数)
2.采用系统级保护
2.1 随机叠加偏移
2.2 划分权限
缺点:失去部分程序灵活性。
3.编译器使用“栈金丝雀”(哨兵)
核心:在栈空间中分配一个特定值(哨兵),在回调前检查哨兵值是否改变,若改变则报错。
GCC实施:-fstack-protector
金丝雀使用实例
Tips:金丝雀能报的错有限,只溢出部分缓冲区(未涉及金丝雀位置)不会报错。
以上便是《深入理解计算机系统》第三章——程序的机器级表示的核心知识。在第三章为计算机系统(2)中的重要内容,主要介绍了程序的各种数据格式、C与汇编的各种转换、各种不同结构的汇编表示,以及程序运行的过程、控制与风险。