OPCode 详解

OpCode

操作码(Operation Code, OPCode):描述机器语言指令中,指令要执行某种操作的机器码

OPCode在不同的场合中通常具有不同的含义,例如PHP虚拟机(Zend VM)、java虚拟机(JVM)以及一些软件保护虚拟机中的最小操作单元都可以称之为OPCode。

常用字节

常用单字节OPCode概览A -- 40~4F

         opcode                                 asm                      using
          
          0x40                                  inc eax                 emit(0x40)
          0x41                                  inc ecx                 emit(0x41)
          0x42                                  inc edx                 emit(0x42)
          0x43                                  inc ebx                 emit(0x43)
          0x44                                  inc esp                 emit(0x44)
          0x45                                  inc ebp                 emit(0x45)
          0x46                                  inc esi                 emit(0x46)
          0x47                                  inc edi                 emit(0x47)
          0x48                                  dec eax                 emit(0x48)
          0x49                                  dec ecx                 emit(0x49)
          0x4a                                  dec ebx                 emit(0x4a)
          0x4b                                  dec ebx                 emit(0x4b)
          0x4c                                  dec esp                 emit(0x4c)
          0x4d                                  dec ebp                 emit(0x4d)
          0x4e                                  dec esi                 emit(0x4e)
          0x4f                                  dec edi                 emit(0x4f)

常用单字节OPCode概览B -- 50~5F

           opcode                                asm                        using
           0x50                                  push eax                 emit(0x50)
           0x51                                  push  ecx                emit(0x51)
           0x52                                  push  edx                emit(0x52)
           0x53                                  push  ebx                emit(0x53)
           0x54                                  push  esp                emit(0x54)
           0x55                                  push  ebp                emit(0x55)
           0x56                                  push  esi                emit(0x56)
           0x57                                  push  edi                emit(0x57)
           0x58                                  pop eax                  emit(0x58)
           0x59                                  pop  ecx                 emit(0x59)
           0x5a                                  pop  edx                 emit(0x5a)
           0x5b                                  pop  ebx                 emit(0x5b)
           0x5c                                  pop  esp                 emit(0x5c)
           0x5d                                  pop  ebp                 emit(0x5d)
           0x5e                                  pop  esi                 emit(0x5e)
           0x5f                                  pop  edi                 emit(0x5f)

常用单字节OPCode概览C -- 70~7F

         opcode                            asm                            using
         0x70  0x12                        Jo 0x12          {_emit(0x70)} {_emit(0x12)}
         0x71  ...                         Jno ...                        ... ...
         0x72  ...                         Jb  ...                        ... ...
         0x73  ...                         Jae ...                        ... ...
         0x74  ...                         Je  ...                        ... ...
         0x75  ...                         Jne ...                        ... ...
         0x76  ...                         Jbe ...                        ... ...
         0x77  ...                         Ja  ...                        ... ...
         0x78  ...                         Js  ...                        ... ...
         0x79  ...                         Jns ...                        ... ...
         0x7a  ...                         Jp  ...                        ... ...
         0x7b  ...                         Jnp ...                        ... ...
         0x7c  ...                         Jl  ...                        ... ...
         0x7d  ...                         Jge ...                        ... ...
         0x7e  ...                         Jle ...                        ... ...
         0x7f  ...                         Jg  ...                        ... ...

常用单字节OPCode概览D -- 90~9F

        Opcode                       asm                   Using
         0x90                    Nop/xchg eax,eax       _emit(0x90)
         0x91                    Xchg eax,ecx       
         0x92                    Xchg eax,edx       
         0x93                    Xchg eax,ebx       
         0x94                    Xchg eax,esp       
         0x95                    Xchg eax,ebp      
         0x96                    Xchg eax,esi       
         0x97                    Xchg eax,edi  

OPCode与指令的对应关系

同类型的指令OPCode不一定相同

B8 01000000    mov eax, 1
8B C3          mov eax, ebx
8B C7          mov eax, edi

OPCode相同的情况下指令也不一定相同

90 nop
90 xchg ax, ax
90 xchg eax, eax    

结论: OPCode与汇编指令并非是单纯的对应关系

那么它是如何进行解释的呢?

首先它分为6个主要数据域,其中只有代码是必须存在的,指令长度在1-16个字节

所以指令独此一份,不可能为其他机器码

OPCode 详解_第1张图片

x86与x86-64指令集的指令的格式为:

指令前缀 指令码 ModR/M SIB 偏移 直接数
Instruction Prefixes Opcode Displacement Immediate
可选。 最多4个单字节前缀。 任何顺序均可。 单字节、双字节、三字节 按需。 0-2位:R/M 3-5位:Reg/Opcode 6-7位:Mod 按需。 0-2位:Base 3-5位:Index 6-7位:Scale 0、1、2、4字节长 0、1、2、4字节长

1, 指令前缀 Instruction Prefixes

分为4组,每组用1个字节编码。每组在指令中至多指定1个前缀值。4组的顺序可以任意。

  • 第1组锁与重复(Lock and repeat)
    • 锁(LOCK)编码为:F0H。用于互斥访问共享内存的操作。
    • 非零时重复(REPNE/REPNZ)编码为:F2H。用于字符串操作指令。
    • 为零时重复(REP/REPE/REPZ)编码为:F3H。用于字符串操作指令。
  • 第2组
    • 段覆盖(Segment override):CS、SS、DS、ES、FS、GS的段覆盖前缀的编码分别是2EH、36H、3EH、26H、64H、65H.
    • 分支提示(Branch hints),用于条件分支指令Jcc。提示分支不发生编码为2EH;提示分支发生编码为3EH。
  • 第3组操作数长度覆盖(Operand-size override)编码为66H。用于在16位与32位操作数切换。
  • 第4组地址长度覆盖(Address-size override)编码为67H.用于在16位与32位地址切换。
;切换操作数大小
;切换顺序: 从大到小
40             INC EAX
66 40          INC AX

;无效的前缀应用
8AC1          MOV AL, CL
66 BAC1       MOV AL, CL

;重复操作段前缀
F3 66 AD      REP LODSW
F2 AC         REPNE LODSB

;段超越前缀
8B 03          MOV EAX, [DWORD DS:EBX]
658B 03        MOV EAX, [DWORD GS:EBX]

前缀的具体含义可以在Intel手册中查到,比如2E,从手册可以看出是锁定CS段

OPCode 详解_第2张图片

2, 代码 code

长度为1、2或3字节,此外ModR/M中还可能有3位。对于双字节指令码或三字节指令码,其中的第1个字节为0FH,用于与指令前缀区分。

3,构造模式 ModR/M

构造模式(Mode): 主要解析逻辑集中在ModeR/M域,通过查找Intel手册解析该域确定指令的具体格式

分为三个部分

  • 模式 Mode
  • 寄存器 Reg
  • 寄存器 R/M

对照Intel手册中的表来解析OPCode中的ModR/M域来确定指令的具体格式.

89  D8          mov eax, ebx
D8 = 11011000
Mod  Reg  R/M
11   011  000

许多指令的内存操作数需要使用ModR/M字节作为寻址模式说明符。其中的mod与r/m组合,共有32个值,表示8个寄存器与24种寻址模式。reg/opcode表示寄存器号或者额外的3位指令码,其具体含义依赖基本指令码。Mod与R/M的5位表示的第一操作数(源与目的操作数中寻址方式更复杂的那个操作数,指令码中的“方向位”direction bit(d)给出源或目的操作数哪个是第一操作数)的寻址方式如下:

寻址方式 Mod R/M
[EAX] 00 000
[ECX] 001
[EDX] 002
[EBX] 003
[--][--] 004
disp32 005
[ESI] 006
[EDI] 007
[EAX]+disp8 01 000
[ECX]+disp8 001
[EDX]+disp8 002
[EBX]+disp8 003
[--][--]+disp8 004
[EBP]+disp8 005
[ESI]+disp8 006
[EDI]+disp8 007
[EAX]+disp32 10 000
[ECX]+disp32 001
[EDX]+disp32 002
[EBX]+disp32 003
[--][--]+disp32 004
[EBP]+disp32 005
[ESI]+disp32 006
[EDI]+disp32 007
EAX/AX/AL/MM0/XMM0 11 000
ECX/CX/CL/MM/XMM1 001
EDX/DX/DL/MM2/XMM2 002
EBX/BX/BL/MM3/XMM3 003
ESP/SP/AH/MM4/XMM4 004
EBP/BP/CH/MM5/XMM5 005
ESI/SI/DH/MM6/XMM6 006
EDI/DI/BH/MM7/XMM7 007
1.[--][--]表示随后的SIB字节指明寻址方式; 2.Mod为11B时,表示寄存器操作数。对于R/M的每个值,根据指令码与操作数长度属性确定具体的寄存器号。 3.当指令需要第2操作数时,由Reg/Opcode的3位给出。第2操作数只能是寄存器操作数。寄存器的指定方式,与Mod为11B时指定作为第1操作数的寄存器的方式完全相同。

某些ModR/M字节表示的寻找模式,需要SIB字节来补充寻址方式。scale表示比例系数;index表示变址寄存器号;base表示基址寄存器号。使用scale与index的5位定义比例变址寄存器如下:

4,辅助分析 SIB

分为三个部分:

  • 比例 Scale
  • 索引 IndexOf
  • 基数 Base
比例变址 基数 Index
[EAX] 00 000
[ECX] 001
[EDX] 002
[EBX] 003
004
[EBP] 005
[ESI] 006
[EDI] 007
[EAX*2] 01 000
[ECX*2] 001
[EDX*2] 002
[EBX*2] 003
004
[EBP*2] 005
[ESI*2] 006
[EDI*2] 007
[EAX*4] 10 000
[ECX*4] 001
[EDX*4] 002
[EBX*4] 003
004
[EBP*4] 005
[ESI*4] 006
[EDI*4] 007
[EAX*8] 11 000
[ECX*8] 001
[EDX*8] 002
[EBX*8] 003
004
[EBP*8] 005
[ESI*8] 006
[EDI*8] 007

3位base表示的基址寄存器号,定义如下:

EAX ECX EDX EBX ESP [*] ESI EDI
000 001 002 003 004 005 006 007
[*]有两种含义:1.如果Mod为00B,则[scaled index] + disp32,即没有基址寄存器。 2.如果Mod为01B或10B,表示基址寄存器为EBP。

在汇编程序设计中,一般把第1操作数的寻址方式总结为如下8种:

寻址方式 英文术语 举例
立即(数)寻址 immediate addressing mov EAX, 01F2H
寄存器寻址 register addressing mov EAX, ESI
直接寻址 direct addressing mov EAX, DWORD PTR [1FFA00H]
寄存器间接寻址 register indirect addressing mov EAX, DWORD PTR [EBX]
基址加变址寻址 base-plus-index addressing mov EAX, DWORD PTR [EBX+ESI]
寄存器相对寻址 或基址相对寻址 register relative addressing mov EAX, DWORD PTR [EDI+01F4H]
基址相对加变址寻址 base relative-plus-index addressing mov EAX, DWORD PTR 01F4H[EDI+EBX]
比例变址寻址 scaled-index addressing mov EAX, DWORD PTR 01F4H[EDI*8+EBX]

综合指令格式中的ModR/M与SIB两个字节的语义规定,指令的第1操作数的寻址方式可总结为4种物理实现:

  • 立即数:表示在指令的“立即数”部分。包括了直接寻址,即立即数作为内存的地址。
  • 寄存器操作数:Mod为11B,根据R/B部分的值、指令码、操作数长度属性,确定具体的寄存器号。
  • 基址相对寻址:即[Reg+disp8或disp32]。包括了寄存器间接寻址。这种情况计算第1操作数地址时使用了1个寄存器。
  • 基址加比例变址的相对寻址:即[BaseReg+IndexReg*scale+disp8或disp32]。这种情况计算第1操作数地址时使用了2个寄存器。

5,位移 Displacement

6,立即数 Immediate

手工在Intel手册中查找OPCode的汇编代码:

​ F0: 26: C78491 AA000000 11000000

    F0:    26:     C7      84       91    AA000000  11000000
  锁定前缀  段超越    OPCode ModeR/M   SIB      偏移       立即数
查找前缀

F0 - Prefixes:锁定前缀,即 Lock

26 - Prefixes:修改默认段,即段超越前缀,查"Opcode Map"如图,段超越前缀为 ES

OPCode 详解_第3张图片

查找Code

C7 - Code:查"Opcode Map"如图,可知:Grp 11 MOV Ev,Iz

OPCode 详解_第4张图片

查找ModeR/M

84 - ModR/M:转为二进制 10 000 100
模式(Mod)段 :2位 10
寄存器(Reg)段 :3位 000
寄存器(R/M)段 :3位 100
查"ModR/M"表如图,得到 [..][..]+disp32

OPCode 详解_第5张图片

查找SIB

91 - SIB:转为二进制 10 010 001
比例(Scale)段 :2位 10
索引(Index)段 :3位 010
基数(Base)段 :3位 001
查"SIB"表如图,可知 [EDX*4]+ECX

OPCode 详解_第6张图片

AA000000 - Displacement:此为小端模式,即为 0xAA
11000000 - Immediate:小端模式,即为 0x11
综上,得到:
LOCK MOV ES:[EDX*4+ECX+0xAA],0x11
即为:
LOCK MOV DWORD PTR ES:[EDX*4+ECX+0x0AA],0x11

3、内联汇编

 ;内联汇编可以有两种形式,一种是行内联汇编,一种是块内联汇编,二者可交叉使用;
 ;行内联汇编:
 __asm mov eax,a
 __asm add  eax,b
 __asm mov c,eax
 ; 块内联汇编:
 __asm{
          mov eax,a
          add  eax,b
          mov c,eax
 }

4、裸函数

// ;定义:没有任何可执行代码的空函数,在内存中仅仅是一条地址信息.
// ; 使用关键字“__declspec(naked)”定义;
// ; 例:
void __declspec(naked) TestFun( ){
    __asm  ret     
}

总结:

ModR/M信息与确认是否有SIB字节

当Mod != 11b并且R/M的值为100b的时候,表示指令后续有SIB字节,并且该内存操作对象由SIB编码。

MODR/M里有三种情况会有SIB字节

参考链接

  1. https://blog.csdn.net/brunomarss/article/details/50589556
  2. https://blog.csdn.net/sqzxwq/article/details/47786345

你可能感兴趣的:(OPCode 详解)