opcode
操作码就是机器码。最多6部分,最长15字节。php虚拟机(zend vm)、jvm中的最小操作单元都可以成为opcode。
opcode由以下域组成:
prefixes - opcode - ModR/M - SIB - displacement - immediate
opcode是必须的。
0x40 inc eax _emit(0x40)
与指令并非简单的一一对应关系:
90 nop
90 xchg eax, eax
SIB:scale(比例因子), index, base。它们加上立即数(immediate)可以组成最复杂的相对基址变址寻址。
opcode表
一字节:高四位作为行索引,低四位作为列索引。
0F二字节指令:使用第二个字节的高四位和低四位作为行列索引。
一字节表OF表格为2-byte escape (Table A-3)
0F38、0F3A三字节指令:使用第三个字节的高四位和低四位。
观察一字节表,可以发现,这些指令都遵循寄存器编号的顺序来为汇编指令设计编码的,而寄存器编号如下:
hex | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
---|---|---|---|---|---|---|---|---|
r8 | al | cl | dl | bl | ah | ch | dh | bh |
r16 | ax | cx | dx | bx | sp | bp | si | di |
r32 | eax | ecx | edx | ebx | esp | ebp | esi | edi |
Segments | DS | ES | FS | GS | SS | CS | IP |
操作数格式为Zz,
- 大写Z为寻址方式,比如,G是由ModR/M的reg指定寄存器,E是由ModR/M的R/M指定操作数;
- 小写z为操作数类型,比如,v表示word/dword/qword
常用寻址方式:
- A,直接寻址,没有ModR/M字节
- E, ModR/M的R/M域指定寄存器或内存地址。
- F,EFLSGS/RFLAGS
- G, ModR/M的Reg域指定寄存器.
- I, 立即数
- S,ModR/M的Reg域指定段寄存器.
常用操作数类型:
- b, 1 byte
- w, word
- d, dword, 4 bytes
- dq, dqword, 16 bytes
- q, qword, 8 bytes
- qq, qqword, 32 bytes
前缀
前缀(Prefixes)的大小为1Byte,用于描述指令的前缀情况,他们可以被划分为5个集合:
- 66:切换操作数大小
- 67:切换地址大小
- F2/F3:重复操作前缀
- 2E/36/3E/26/64/65:修改默认段(段超越)
- F0:锁定前缀
段超越:
- 2E - CS
- 36 - SS
- 3E - DS
- 26 - ES
- 64 - FS
- 65 - GS
一个OpCode可能会有几个Prefixes,顺序可能打乱。
40 INC EAX
66 40 INC AX
F3 66 AD REP LODSW
F2 AC REPNE LODSB
8B 03 MOV EAX, [DWORD DS:EBX]
658B 03 MOV EAX, [DWORD GS:EBX]
如果Prefixes不能对随它之后的OpCode起作用,那么它就会被忽略。
8AC1 MOV AL, CL
66 BAC1 MOV AL, CL
ModR/M
OPCode的主要解析逻辑都集中在ModR/M域,通过对照Intel的opcode解析OPCode中的ModR/M域就能确定指令的具体格式。
练习
02 1B
02:add Gb, Eb
1B: 00 011 011
mod:00 ebx, ebx
add bl, [ebx]
2E:8925 00112233 mov dword ptr cs:[33221100], esp
查一下0F
指令:2-byte escape
需要记住
- E8 : call 立即数
- E9 : jmp
- FF15 : CALL 地址
FF15需要查询Table A-6, opcode extensions…
0040103B E8 00000000 call 00401040
00401040 FF15 00112233 call dword ptr [33221100]
00401046 - E9 00112233 jmp 3362214B