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個字節
所以指令獨此一份,不可能為其他機器碼
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段
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
查找Code
C7 - Code:查"Opcode Map"如圖,可知:Grp 11 MOV Ev,Iz
查找ModeR/M
84 - ModR/M:轉為二進制 10 000 100
模式(Mod)段 :2位 10
寄存器(Reg)段 :3位 000
寄存器(R/M)段 :3位 100
查"ModR/M"表如圖,得到 [..][..]+disp32
查找SIB
91 - SIB:轉為二進制 10 010 001
比例(Scale)段 :2位 10
索引(Index)段 :3位 010
基數(Base)段 :3位 001
查"SIB"表如圖,可知 [EDX*4]+ECX
AA000000 - Displacement:此為小端模式,即為 0xAA
11000000 - Immediate:小端模式,即為 0x11
綜上,得到:
LOCK MOV ES:[EDX4+ECX+0xAA],0x11
即為:
LOCK MOV DWORD PTR ES:[EDX4+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字節