X86-64汇编机器码格式
基本结构
该图说明:主操作码一定有,由于其它内容不一定在一条指令中,所以指令长度不固定
符号说明
G:寄存器
E:寄存器/内存
b:单字节
v:根据操作系统判断位数,一般数默认32位,地址默认64位
MOV Gv Ev:意思是把一个内存或寄存器的值放到一个寄存器中,大小根据操作系统决定
现在依次介绍这几个
指令前缀(Instruction Prefixes)
前缀分为 4 类,使用时,不区分前后顺序,且每组最多出现一个:
1.锁定和重复前缀
前缀 | 效果 |
---|---|
F0 | 使操作变为原子操作 |
F2 | REPNE/REPNZ,重复前缀,用于串和 IO |
F3 | REP 或 REPE/REPZ,用于串和 IO |
2.段重载
前缀 | 效果 |
---|---|
2E | CS 段重载 |
36 | SS 段重载 |
3E | DS 段重载 |
26 | ES 段重载 |
64 | FS 段重载 |
65 | GS 段重载 |
2E | 分支一般不跳转,用于提示 CPU 进行预测 |
3E | 分支一般跳转,用于提示 CPU 进行预测 |
3.操作数大小重载
前缀 | 效果 |
---|---|
66 | 将默认操作数大小从 32 位转为 16 位 |
4.操作地址大小重载
前缀 | 效果 |
---|---|
67 | 将默认的 64 位地址大小转为 32 位 |
REX 前缀
REX 用来扩展指令从 32 位到 64 位,这一部分的内容需要先看后面的几个部分才能看懂
占一字节,可以没有
将对应域置 1 就可以使目标扩展
其中经常使用的就是 48,让操作数变为 64 位大小
操作码
一般来说,主操作码长度为 1 到 3 字节,ModR/M 可能编码附加的 3 位操作码
双字节:0F+第二个操作码字节 或 一个前缀(66H, F2H, 或 F3H)+0F+第二个操作码字节
三字节:0F+后两个操作码字节 或 一个前缀(66H, F2H, 或 F3H)+0F+后两个操作码字节
部分内容需要先看后面的
下面为 1 到 3 字节指令的查表
以上截取自 IA32 手册 2024-12 版的 2929 页至 2938 页
除了上面的查表方式,x86-64 指令还有另一种方式:通过组来查询,这种方式使用了一张新表,叫做单双字节操作码的操作码扩展
先附表
这里是 2940 到 2950
考虑 80 C0 05
80 在表 A-6 中是 Group 1,然后看 ModR/M 字段的 5,4,3 位
C0:11000000,5,4,3 位为 000,即 0,查纵列得 ADD,根据之后的内容可以知道 ModR/M 指定了对应的内存和寄存器,所以得到 add al,5,这和第一种直接查表得到的 0405 的指令相同
ModR/M
如果一个操作码涉及内存,后面一般就会有一个寻址格式说明字节即 ModR/M,而如果 ModR/M 中提到了需要内存寻址,那么就需要 SIB 字节去进一步解释
ModR/M和SIB各涉及三个域:
Mod寻址模式
指明操作码中的E是寄存器还是内存,11是内存,其它都是寄存器
Mod值 | 描述 | 偏移量 |
---|---|---|
00 | 内存,无偏移量(除非 R/M=101) | 无或32 |
01 | 内存,有8位偏移 | [base+8位偏移] |
10 | 内存,有32位偏移 | [base+32 位偏移] |
11 | 寄存器操作数 | 寄存器 |
前三个为内存,后一个为数
Reg/Opcode寄存器或操作码扩展
指定使用的reg或opcode扩展:
值 | 32 位寄存器 | 16 位寄存器 | 8 位寄存器 | 用途 (操作码扩展) |
---|---|---|---|---|
000 | EAX | AX | AL | ADD |
001 | ECX | CX | CL | OR |
010 | EDX | DX | DL | ADC |
011 | EBX | BX | BL | SBB |
100 | ESP | SP | AH | AND |
101 | EBP | BP | CH | SUB |
110 | ESI | SI | DH | XOR |
111 | EDI | DI | BH | CMP |
R/M寄存器或内存
R/M 值 | Mod = 11(寄存器) | Mod ≠ 11(内存基址/索引) |
---|---|---|
000 | EAX | [RAX] |
001 | ECX | [RCX] |
010 | EDX | [RDX] |
011 | EBX | [RBX] |
100 | ESP | [SIB](需要 SIB 字节) |
101 | EBP | [RBP] 或无偏移/disp32 |
110 | ESI | [RSI] |
111 | EDI | [RDI] |
内存索引的标准格式:base+index*scale+disp
SIB
SIB用来确定具体的地址格式
如果ModR/M中Mod不为11,且R/M的值为100,则后一个字节为SIB
ModR/M和SIB各涉及三个域:
Scale比例因子
决定索引寄存器值的倍数
值 | 比例因子 (Scale) |
---|---|
00 | 1 |
01 | 2 |
10 | 4 |
11 | 8 |
Index索引寄存器
值 | 寄存器 (64 位模式) | 寄存器 (32 位模式) |
---|---|---|
000 | RAX | EAX |
001 | RCX | ECX |
010 | RDX | EDX |
011 | RBX | EBX |
100 | 无索引寄存器 | 无索引寄存器 |
101 | RBP | EBP |
110 | RSI | ESI |
111 | RDI | EDI |
Base基址寄存器
值 | 寄存器 (64 位模式) | 寄存器 (32 位模式) |
---|---|---|
000 | RAX | EAX |
001 | RCX | ECX |
010 | RDX | EDX |
011 | RBX | EBX |
100 | RSP(需 SIB 扩展) | ESP(需 SIB 扩展) |
101 | RBP 或 disp32(Mod=00) | EBP 或 disp32(Mod=00) |
110 | RSI | ESI |
111 | RDI | EDI |
练习
8B 41 10
8B对应MOV Gv Ev
Gv说明目标操作数是寄存器,Ev表示源操作数为寄存器或地址
41是ModR/M
01 000 001
01说明这个指令的地址Ev为内存地址且有8bit偏移,000说明Gv为AX类,001说明目标操作数为CX类
由于没有SIB,10为立即数
所以指令为:MOV EAX, [RCX+0x10]
04 FF
04对应ADD AL,Lb
已经指定寄存器,后面直接跟立即数,Lb表明是一个byte,所以是ADD AL ,0xff
67 FF 34 88
67为指令前缀,表明把之后的地址值转为32位
FF表明要去Grp $5^{1A}$
34为ModR/M:00 110 100得到指令是PUSH Ev
00表明为使用地址无偏移,100表示根据SIB确定地址的值
SIB:10 001 000
10表明比例因子为4,001表明索引寄存器为RCX,000表明基址寄存器为RAX
根据指令前缀,将地址相关寄存器大小转为32位:push qword ptr [eax+ecx*4]