硬编码(Reg/Opcode 和 SIB)
变长指令Reg/Opcode
我们知道,3-5的这一段,描述的是通用寄存器Reg的信息,那当描述的不是Reg时,我们该如何查表
0x80
0x81
0x82
0x83
看下这三个指令对应的Tabel A-2这表主表内容
注:凡是看到有Grp的,均参写 Table A-6
表注释补充:
上标符号及其含义
符号 | 含义 |
---|---|
1A | ModR/M 字节的第 5、4、3 位用作操作码扩展(参考附录 A.4:“单字节和双字节操作码的扩展”)。 |
1B | 当需要故意生成无效操作码异常(#UD)时,使用 0F0B (UD2 指令)、0FB9H (UD1 指令)或 0FFFH (UD0 指令)操作码。 |
1C | 某些指令共享相同的双字节操作码。如果指令存在变体,或操作码对应不同指令,则通过 ModR/M 字节区分。具体解码值参见表 A-6。 |
i64 | 该指令在 64 位模式下无效或不可编码。单字节 40-4F (INC/DEC)在 64 位模式下会被解释为 REX 前缀(需改用 FE/FF 组 4 和 5 编码 INC/DEC)。 |
o64 | 该指令仅在 64 位模式下可用。 |
d64 | 在 64 位模式下,指令默认使用 64 位操作数大小且无法编码 32 位操作数大小。 |
f64 | 在 64 位模式下,操作数大小强制为 64 位(操作数大小前缀对此指令无效)。 |
v | 仅存在 VEX 编码形式(无传统 SSE 形式)。对于整数通用寄存器指令,表示必须使用 VEX 前缀。 |
v1 | 仅存在 VEX128 和 SSE 形式(无 VEX256),且无法从数据大小推断时使用。 |
举例说明:
80 65 08 FF
- 第一个字节为80,查Table A-2表,得到对应结构:Eb,Ib
- 第二个字节为ModR/M字段,所以拆分65:
01(Mod) 100(Reg/Opcode) 101(R/M)
- 先看01(Mod),再看101(R/M)对应的是
[RBP] + disp8
- 再看1A的解释:
ModR/M 字节的第 5、4、3 位用作操作码(Opcode)扩展(参考附录 A.4:“单字节和双字节操作码的扩展”)
- 看TableA-6这张表:看Group这一列,后面跟的什么就看什么,比如我们后面跟着的是1,就确定了我们所在的行
- 看我们Reg/Opcode的值为:100,对应100的位置为AND
- 最终我们的指令就是:
AND [RBP+disp8],Ib
- 我们已经分析过80 65的指令了,但是disp8偏移量和Ib立即数是多少呢,所以80 65 后面还要跟两个字节,用来表示偏移和立即数
最终结果:
定长指令SIB
SIB是由谁来决定的?
SIB是由ModR/M来决定的,同时ModR/M如果确定了,那么同时Displacement和Immediate也确定了
SIB结构
指令示例:
88 84 48
- 通过查表,得知88的指令格式为 MOV Eb,Gb
- 那么第二个值84就为ModR/M,拆分:
10(Mod) 000(reg/Opcode) 011(R/M)
- 查Tabel 2-2表,先看
Mod
部分为:10
,R/M为:[--][--] + disp32
,这种奇怪的格式 - 如果查到为[–][–]这种格式的描述时,就说明,以当前查询方式无法获取到指令格式,还需要额外的一个字节(
SIB
)来进行查询 - 我们接着拆分48为:
01 (Scale)001(Index)000(Base)
- 查Table 2-3 表
- 先看Base(000)为RAX
- 再看Scale(01)对应的在哪一组
- 最后看Index(001)为:[RCX*2]
- 最终我们的指令为:MOV BYTE PTR DS:[RAX + RCX * 2 + disp32],AL