[音乐] 本周主要介绍X86-64架构指令系统
上一讲我们介绍了X86-64的主要特征 特别是通用寄存器的组织结构和
用于SSE指令的XMM寄存器组织 本讲我们将介绍X86-64中常用的指令类型
如传送指令,算术和逻辑运算指令 比较和测试指令等。
并通过基于X86-64指令系统的逆向工程举例 来强化对X86-64指令系统的理解
我们首先介绍传送指令
数据传送指令,实际上我们在32位架构 里面经常会用到,最常用的就是mov指令
实际上64位架构跟32位架构差不多,所不同的是
在64位架构里面多了一个"q"这种长度后缀
表示是64位的数据,传送的是64位的数据
比如说movabsq
左边这个操作数给定的是一个立即数 右边这个操作数给定的是一个64位的通用寄存器
那么这条指令的功能就是把一个64位立即数
送到64位通用寄存器里面
movq跟我们前面讲的这些
movb,movl,movw等等这些指令都差不多
那么以前我们讲的movb那是传送1个字节,movw传送16位,2个字节
或者movl是传送4个字节,那么这儿movq就是传送64位,8个字节的这样一个4字
movsbq表示
把一个字节进行符号扩展 然后传送到一个64位的寄存器里面
那么这个的话是把一个16位的数进行 符号扩展,扩展成64位的数,放到一个64位寄存器里面
这个同样的是对一个32位的数进行符号扩展
扩展成一个64位的数,放到64位寄存器里面
这边这几条指令跟上面这个不同,上面这个是s,表示符号扩展
符号扩展,那么这边是z,就表示是零扩展
这个zbq表示把一个8位寄存器当中的一个字节
进行零扩展,扩展成64位的一个数据,送到64位寄存器里面
这个是把一个16位寄存器里面的操作数 零扩展成64位的数据,送到
64位的寄存器或者是存储单元里面 movl指令在这个64位架构里面
相当于movzlq指令 就是把一个32位寄存器里面的32位数
进行零扩展,扩展成一个64位的操作数 放在64位寄存器里面。
push指令跟前面的 32位架构一样,所不同就是push后面的这个长度后缀
可以是pushq,那么原来是pushb,w或者l
如果是pushq的话,那就是指的要把后面的这个64位的操作数把它压栈
那么在压栈之前通过把栈顶指针
减8,然后呢在栈里面长出64位的一个空间
使得新的栈顶向下移动8个字节,长出一个64位的空间以后
把这个操作数S种到栈顶所在的那个存储单元里面
pop指令跟push指令是相反的,这儿是q的话
说明这个操作数是一个寄存器,通常是一个通用寄存器的编号
表示这个D呢是一个64位的寄存器 把当前栈顶所指出的那个
存储单元里面有那个位置开始的8个字节
把它送到64位的寄存器里面去,同时呢让这个新的栈顶
往上退8个字节
下面是一个例子,这个例子当中给出了一个函数
这个函数把一个source type这种类型的
参数x,把它强制类型转换成 dest
type这样一种类型,转换成这个类型以后 就返回。
这个函数的返回类型就是dest type
当这个source type分别取char,int,long,而dest
type 分别取long,long,long,int等等这样的这个组合的情况下
那么这个赋值语句所用的指令是什么?
这个是这个问题,就是对应于 这每种组合,source
type和dest type 分别取这种组合的时候,那么这条赋值语句对应的汇编指令是什么?
在给出这个汇编指令之前,这边假定
这个参数x是在RDI 对应的适合宽度的寄存器里面
也就是说这个x如果是一个64位的类型,那么它就在这个64位的寄存器里面
这个x如果是一个32位的int型的这种数据的话,那么它就在这个32位的寄存器里面
当x是16位的,比如说short型的数据,它就在这个16位的DI这个寄存器里面
当x是char型的话,那么它就在这个8位寄存器里面。
这个是x所在的寄存器 y呢是放在RAX对应的不同宽度的寄存器里面
如果y是一个32位的数据,比如说int型的,它就在32位的寄存器里面
y如果是一个long型的这种64位的数据的话,它就在64位的 寄存器里面。
16位数就在这个寄存器里面,8位数就在这个寄存器里面 那么在这个前提条件下我们来看这每种情况下
它的这个汇编指令应该是什么。
我们可以看一下这个 第一种组合。
当source是等于char,dest等于long的时候 应该是一个什么样的指令呢?
很显然,这个赋值语句当然是一条mov指令 把x mov到y所在的那个地方去。
这个mov指令后面的前缀应该是什么呢? 这个char显然它是一个带符号数,因此是用符号扩展的
也就是把一个8位的数据要扩展成64位的数据并且是符号扩展
因此这个地方肯定是s,然后呢这个是一个char类型的
long是64位的是4字,所以是 q,然后呢源操作数source
type所指定的这个x 是在RDX对应宽度的这个里面,因为正好是一个
单字节的8位的一个操作数,所以源操作数应该在 DIL寄存器里面,因此这个地方应该是
dil,而目操作数是64位的,因此它是
rax,这边是rax,目操作数y 在rax里面。
然后第二个的话同样的它也是 带符号数,所以应该是符号扩展,因此也是加s
只不过这个地方是int型的,说明它是l l表示32位的,这个long型的所以是q
那么这个地方源操作数当然是在一个32位的寄存器里面
32位的寄存器当然是 edi,这个是64位的,这个是32位的寄存器edi
然后目的操作数y还是在64位的寄存器里面,因为它是long型的 所以还是rax。
然后第三个long long 是long转换成long,当然直接就是mov然后q就可以了
源操作数当然是在rdi当中,这是个64位的寄存器
目操作数也是一个64位的,所以在rax里面
rax,这是rdi里面,因为都是64位转换为64位
下面的这个是64位变成32位。
这个实际上把这个x 低32位,x实际上它是一个64位的值
把这个64位的值当中,低32位,送到一个32位的寄存器里面就可以了
所以直接可以用
L,那么就是
edi,目的操作数放在
eax里面,这个我们刚才在前面讲过
在64位架构里面的movl,它相当于是mov
ZL Q,相当于是一个0扩展,也就是说
eax前面的rax当中 低32位在eax里面
这个寄存器当中的高32位,实际上 是全0,因为我们这个实际上,只取低32位
高32位我们根本就不管,所以呢,我们直接在这个地方就是取低32位就可以了
当然我们也可以用movs
lq,也可以 也就是我们实际上是要取的是
这个低32位的值,这个低32位的值,你用一个符号扩展
扩展到rax里面,那实际上在这个rax里面
实际上也只是用它的低32位,高32位
符号扩展以后,这个符号扩展的东西还是会丢掉,这个地方是0扩展,0扩展的东西也会丢掉 所以这两条指令,都可以实现这个操作
而且这两条指令,最终后续指令用,都只用其中的32位
这个eax里面里面值,这个也是用这个64位寄存器当中的低32位eax这个寄存器- 里面的值
因为这个地方是一个int值,y是32位的 所以这个两条指令都可以。
那下面的这个,实际上 无符号数扩展,那很显然是mov
l,因为movl相当于是mov
因为是无符号数,所以是0扩展,lq 所以这两条指令是完全一样的,因此就是movl
源那就是edi,mov呢就是rax
如果用movl的话就是上面这条指令,如果用
movzlq的话,这边源操作数就是edi
mov操作数就是rax,所以这两条指令是一样的
这个下面的这条指令也是一样的,movl
和movl后面带的操作数就是 edi、
eax 和这条指令也是一样的,反正movl就表示是0扩展
所以这上面两条指令这个是一样的,int变成long,long变成int
都是一样的,这种高位就是0扩展,这个高位实际上就是丢掉的
然后下面的这个的话,很显然是用的movzbq 因为这个char
类型的,然后unsigned的话是表示0扩展 然后源操作数那当然就是
dil这个8位寄存器
mov操作数就是rax 它是64位的,所以rax
因此我们可以看到它的结果 刚才我们讲的这个结果就是这样子,这个是符号扩展
char类型变成intlong类型,这个符号扩展int类型变成long类型
这个是一样的,这个我们前面已经讲过了,这个地方我们前面也讲过了
它可以用这条指令来实现,也可以用这条指令来实现
那不管是这条指令也好这条指令,实际上我们只需取x的低32位
所以我们在这条指令当中,后续的指令用也只用
这个64位寄存器当中的低32位
因为这儿是int型,后面呢这个也不讲,其实前面刚刚才讲过,就是这样子的。
[音乐] [音乐]