最近在写一个辣鸡 x86 反汇编器,做点笔记。
概述
x86 指令有 6 部分,并不是每条指令都要有全部部分:
- 指令前缀(0 - 4 字节)
- 指令码(opcode,1 - 2 字节)
- ModR/M(0 - 1 字节)
- SIB(0 - 1 字节)
- 偏移(0/1/2/4 字节)
- 立即数(0/1/2/4 字节)
指令前缀
有 4 组前缀,一条指令在每组中最多只能取其中 1 个前缀,4 组就是 4 个前缀,各来自不同组。
- 第一组:锁与重复
- 0xf0:
lock
- 0xf2:
repne
- 0xf3:
rep
- 0xf0:
- 第二组:段跨越(segment override)
- 0x26:
es:
- 0x2e:
cs:
- 0x36:
ss:
- 0x3e:
ds:
- 0x64:
fs:
- 0x65:
gs:
- 0x26:
- 第三组:操作数长度覆盖(operand-size override)
- 0x66: 改变操作数长度,16 位机默认长度为 16 位,32 位机默认长度为 32 位,改变后互换。
- 第四组:地址长度覆盖(address-size override)
- 0x67: 改变地址长度,16 位机默认长度为 16 位,32 位机默认长度为 32 位,改变后互换。
指令码
The Intel Manual and the x86 Opcode and Instruction Reference are your friends.
ModR/M
ModR/M 指出操作数的寻址方式(addressing mode),其高 2 位为 Mod,中间 3 位为 Reg/Opcode,低 3 位为 R/M。
若 Mod == 11(二进制),则第一操作数为寄存器寻址,否则为存储器寻址。
Reg/Opcode 有两种意义:
- 有的指令需要第二操作数,由这个字段给出。
- 有的指令用这个字段作为扩展指令码(opcode extension),即指令码部分和 Reg/Opcode 字段共同决定该指令是什么指令。
R/M 和 Mod 一起决定第一操作数更具体的寻址方式,见1。
若为存储器寻址,且 R/M == 100(二进制),则后面还有 SIB 字节指明具体寻址方式。
SIB
SIB 是 scale-index-base(比例-变址-基址)的意思,其高 2 位为 scale,中间 3 位为 index,低 3 位为 base。
SIB 寻址的汇编形如 [base+index*scale]
。
scale 为 00、01、10、11(二进制)分别表示比例因子为 1、2、4、8,乘在变址寄存器上。
index 为 0 - 7 分别代表(32 位情况)eax
、ecx
、edx
、ebx
、无变址寄存器、ebp
、esi
、edi
。
base 为 0 - 7 分别代表(32 位情况)eax
、ecx
、edx
、ebx
、esp
、[*]、esi
、edi
。
其中 [*] 表示:
- 若 Mod == 00(二进制),则无基址寄存器。
- 若 Mod == 01 || Mod == 10,则为
ebp
。
位移和立即数
有些指令或寻址方式要求给出偏移或立即数,请查手册。