基本结构

image-20250331153157959

image-20250331155123077

该图说明:主操作码一定有,由于其它内容不一定在一条指令中,所以指令长度不固定

符号说明

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 位,这一部分的内容需要先看后面的几个部分才能看懂

占一字节,可以没有

image-20250331160405462

将对应域置 1 就可以使目标扩展

其中经常使用的就是 48,让操作数变为 64 位大小

操作码

一般来说,主操作码长度为 1 到 3 字节,ModR/M 可能编码附加的 3 位操作码

双字节:0F+第二个操作码字节 或 一个前缀(66H, F2H, 或 F3H)+0F+第二个操作码字节
三字节:0F+后两个操作码字节 或 一个前缀(66H, F2H, 或 F3H)+0F+后两个操作码字节

部分内容需要先看后面的

下面为 1 到 3 字节指令的查表

image-20250331161314311

image-20250331161329868

image-20250331161339920

image-20250331161350193

image-20250331161359418

image-20250331161408747

image-20250331161419001

image-20250331161429738

image-20250331161439214

image-20250331161448469

以上截取自 IA32 手册 2024-12 版的 2929 页至 2938 页

除了上面的查表方式,x86-64 指令还有另一种方式:通过组来查询,这种方式使用了一张新表,叫做单双字节操作码的操作码扩展

先附表

image-20250331163515951

image-20250331163526702

image-20250331165953065

image-20250331170007852

image-20250331170031403

image-20250331170044132

image-20250331170056390

image-20250331170105301

image-20250331170116327

image-20250331170126623

image-20250331170135362

image-20250331170145112

image-20250331170154414

image-20250331170205007

image-20250331170213809

image-20250331170226577

image-20250331170241767

image-20250331170254243

这里是 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各涉及三个域:
image-20250331171513102

image-20250331172923245

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各涉及三个域:
image-20250331171513102

image-20250331190129120

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]