intel opcode 解析
1. 保护模式 & 实模式 &8086架构
1.1. 1 指令格式描述
1.1.1. 指令前缀 Instruction Prefixes
指令前缀可以划分为4种类型,每种都有一组对应的前缀码,对于每条指令可以从每种指令里面选取一条(最多可以选取4条指令,无序)
Group1
- 锁定和重复前缀(Lock and repeat prefixes) :
- F0H - LOCK prefix
- F2H - REPNE/REPNZ prefix。(Repeat-Not-Zero)仅用于串操作和I/O指令,也可被用作某些指令的强制性前缀。
- F3H - REP or REPE/REPZ。 仅用于串操作和I/O指令,也可被用作POPCNT、LZCNT、ADOX指令的强制性前缀。
- F2H: BND prefix,如果满足以下条件:
- CPUID.(EAX = 07H, ECX = 0):EBX.MPX[bit 14] is set
- BNDCFGU.EN and/or IA32_BNDCFGS.EN is set.
- When the F2 prefix precedes a near CALL, a near RET, a near JMP, a short Jcc, or a near Jcc instruction
Group2
- 段重载前缀(Segment override prefixes):
- 2EH—CS segment override (保留任何分支指令时使用).
- 36H—SS segment override prefix (保留任何分支指令时使用).
- 3EH—DS segment override prefix (保留任何分支指令时使用).
- 26H—ES segment override prefix (保留任何分支指令时使用).
- 64H—FS segment override prefix (保留任何分支指令时使用).
- 65H—GS segment override prefix (保留任何分支指令时使用).
- (分支预测)Branch hints (已不用)
- 2EH—分支不被接受 (used only with Jcc instructions).
- 3EH—分支被接受 (used only with Jcc instructions).
Group 3
- 66H - 操作数大小重载前缀(也用作某些指令的强制性前缀)。
Group 4
F0H(LOCK 前缀)在多处理器环境下强制执行独占共享内存操作。
F2H,F3H(重复前缀)会重复字符串的每一个操作,只有 MOVS,CMPS,SCAS,LODS,STOS,INS,OUTS
等字符串操作或者IO指令可以使用这些前缀。
2EH,3EH(分支预测)允许程序给处理器一个最有可能的执行分支提示。
66H(操作数大小重载前缀)允许程序在16位和32位操作数大小间切换。加前缀代表非默认值(默认值可以随意设定为16位或者32位)
67H(地址大小重载前缀)允许程序在16位和32位地址间切换。
1.1.2. Opcode
主操作码可以是 1, 2, 3个字节大小。可能在 ModR/M中还额外存在3位操作码,它们用来定义较小的字段,如:操作方向,位移大小,寄存器编码,条件码,符号扩展等。
用于通用和SIMD指令的双字节的操作码格式一般如下所示:
- 转义操作码 0FH 被作为主操作码再跟上一个单字节操作码。
- 强制前缀(66H,F2H,F3H)、转义操作码加上一个单字节操作码。eg:
CVTDQ2PD -> F3 0F E6
F3 为强制前缀
用于通用和SIMD指令的三字节的操作码格式一般如下所示:
- 转义操作码 0FH 被作为主操作码再跟上一个双字节操作码。
- 强制前缀(66H,F2H,F3H)、转义操作码加上一个双字节操作码。eg:
PHADDW -> 66 0F 38 01
66 为强制前缀
1.1.3. ModR/M & SIB
许多引用内存中操作数的指令在Opcode 之后都跟有一个寻址模式说明字节,称之为(ModR/M字节)。在ModR/M字节中包含三个字段的信息:
- mod 字段 & R/M 字段:结合之后形成$4* 8 = 32$种可能的值: 8个寄存器和24种寻址模式。
- reg/opcode字段:确定寄存器号或者附加的3位操作码的信息,用途在主Opcode中确定。
- r/m 字段:确定一个寄存器为操作数或者和mod域一起编码寻址模式。
某些ModR/M字节编码需要第二寻址字节(SIB).基址+索引或者比例+索引形式的32位寻址需要SIB字节.SIB字节包括下列字段:
- scale 指定比例因子
- index 指定索引寄存器号
- base 指定基址寄存器号
1.1.4. 偏移和立即数
一些寻址模式会在ModR/M(或者SIB)字节之后补充对应的偏移,如果存在,它是1,2,4个字节。
指令可能指定一个直接操作数,操作数通常在偏移之后。如果存在,它是1,2,4个字节。
1.2. 实例
1.2.1. 16位寻址模式 ModR/M 字节表
1.2.2. 32位寻址模式 ModR/M 字节表
1.2.3. 32位寻址模式 SIB 字节表
1.2.4. 实际规则描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | .text: 00002D0C 55 push ebp
.text: 00002D0D 89 E5 mov ebp, esp
.text: 00002D0F 53 push ebx
.text: 00002D10 83 EC 14 sub esp, 14h
.text: 00002D13 E8 4F E7 FF FF call sub_1467
.text: 00002D18 81 C3 F4 88 02 00 add ebx, (offset off_2B60C - $)
.text: 00002D1E 8B 83 D8 FF FF FF mov eax, ds:(stderr_ptr - 2B60Ch )[ebx]
.text: 00002D24 8B 00 mov eax, [eax]
.text: 00002D26 89 44 24 0C mov [esp + 0Ch ], eax ; s
.text: 00002D2A C7 44 24 08 ED 01 00 00 mov dword ptr [esp + 8 ], 1EDh ; n
.text: 00002D32 C7 44 24 04 01 00 00 00 mov dword ptr [esp + 4 ], 1 ; size
.text: 00002D3A 8D 83 74 A1 FF FF lea eax, (aUsageSetInterp - 2B60Ch )[ebx] ; "Usage: \n [--set-interpreter FILENAME]" ...
.text: 00002D40 89 04 24 mov [esp], eax ; ptr
.text: 00002D43 E8 80 E4 FF FF call _fwrite
.text: 00002D48 83 C4 14 add esp, 14h
.text: 00002D4B 5B pop ebx
.text: 00002D4C 5D pop ebp
.text: 00002D4D C3 retn
|
push ebp
: 查表得 符合 PUSH r64
规则,Opcode对应为50+rd
其中 +rd
表示低三位用来编码 dword 寄存器操作数, 查表知ebp对应为5 所以 push ebp
对应 opcode 为 55。
mov ebp, esp
: 符合规则 MOV r/m32,r32
规则,opcode 对应为 89 /r
,其中 /r
表示有一个 ModR/M 结构的opcode , 该方式为寄存器直接寻址,所以MOD 标识为 11,目的地址为寄存器 ebp,所以R/M为 101,REG/opcode 端看第二个操作数,此处为 esp 所以reg/opcode 为100,对应整体opcode为11 100 101
即 E5。所以整体opcode 为 89 E5
push ebx
类似 1. ebx对应为3 ,所以对应opcode 为 53
sub esp, 14h
: 符合SUB r/m32, imm8
opcode为83 /5 ib
其中/5
表示ModR/M 端存在,且仅使用 r/m操作数,寻址方式为寄存器立即寻址。reg字段包含一个数字5
提供指令操作码的扩展,目的寄存器为 esp R/M 为 100. 对应opcode 为 11 101 100
。ib
表示立即数 14
。整体opcode 为 83 EC 14
call sub_1467
符合CALL rel32
,对应opcode 为 E8 cd
其中 cd
表示 4个btye字节在call后面,这4个byte用于指示相对偏移,此处为 $1467 - (2d13 + 5(指令长度)) = ff ff 17 4f$,即 4F 17 FF FF,所以opcode为 E8 4F E7 FF FF
其后类似,不做展开。
2. IA-32E MODE
分为两种:
- Compatibility Mode(兼容模式) : 允许64位操作系
- 统运行legacy保护模式软件。
- 64位模式: 允许64位操作系统运行为访问64位地址空间而编写的应用程序。
2.1. 前缀(Prefixes)
访问64位专用的前缀 REX Prefixes。以及为了兼容x86所保留的原来的前缀 Legacy Prefixes。
Legacy prefix和REX prefix修饰符是为了改变缺省的寄存器,比如说在32位下mov ax,bx。那么就要有Legacy prefix 0x66修饰,前者还有个作用就是改变当前段寄存器,不多这在目前已经显得不怎么重要了。到了x64的时候,由于通用寄存器的扩展(主要原因),原本的8个通用寄存器只要3个位来标识,但是现在多了r8~r15,16个通用寄存器,很明显加上一位就能完全标识了。那么就是REX prefix的作用。
REX prefix格式如上:总共占1byte ,前4位固定为0100
后四位用来扩展指令,其中:
- W : 标识操作数的大小,一般指令默认为32为操作数,所以需要通过修改 W来改变默认操作数大小,此时 opcode 为
0x48
,对应为 REX.W
- R : 用于扩展ModR/W,原始8个通用寄存器使用三位即可标识,但是x64新增了 r8~r15,一共具有16个通用寄存器,因此新政一位用来扩展 ModR/M.reg域,即原始范围为 $000 ~ 111$,扩展之后为 $0 000 ~ 1 111$
- X : 用来扩展 SIB.index 域
- B : 用来扩展 SIB.base, ModRM.r/m 以及 Opcode.reg
其他基本与x86相同不再赘述。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-5-11 15:35
被安和桥南编辑
,原因: 忘记加原创tag了