硬编码学习笔记(二)—— 经典变长指令
硬編碼學習筆記(二)—— 經典變長指令
- 前言
- 指令結構
- 符號說明
- 尋址符號
- 操作數符號
- 上標符號
- One-Byte Opcode Map
- 變長指令
- ModR/M
- 例:0x88
- 例:0x89
- 例:0x8A
- 例:0x8B
- SIB
- 例:88 84 48 78 56 34 12
- 例:89 84 84 78 56 34 12
- Opcode Extension Tables
- 例:80 65 08 FF
- Instruction Prefixs
- 段前綴
- 操作指令前綴:修改地址默認長度
- 操作指令前綴:修改默認尋址方式
前言
本次學習僅基于intel x86模式
指令結構
描述:對于任何一條指令,都由以下幾部分組成,但不是每部分都必須存在
長度:最短1個字節,最長15個字節
Instruction Prefixes:指令前綴
Opcode: 主操作碼
ModR/M:在內存中引用一個操作數的許多指令都有一個尋址方式的指定符字節(稱為ModR/M字節)跟隨在主操作碼之后
SIB:ModR/M字節的某些編碼需要第二個尋址字節(SIB字節)
Displacement:一些尋址方式包含緊跟ModR/M字節(或者SIB字節,如果有的話)的位移
Immediate:立即數
符號說明
尋址符號
| A | 直接尋址 |
| C | 控制寄存器 |
| D | 調試寄存器 |
| E | 寄存器/內存 |
| F | EFLAGS/RFLAGS寄存器 |
| G | 通用寄存器 |
| I | 立即數 |
| J | 要添加到指令指針寄存器的相對偏移量 |
| M | ModR/M字節可能僅指向內存。 |
| N | MMX 技術寄存器 |
| O | 該指令沒有ModR/M字節。操作數的偏移量在指令中被編碼為一個字或雙字(取決于地址大小屬性)。 |
| P | ModR/M字節的reg字段選擇一個打包的四字MMX技術寄存器 |
| Q | ModR/M字節跟隨操作碼并指定操作數。該操作數要么是MMX技術寄存器,要么是內存地址。 |
| R | ModR/M字節的R/M字段可能僅指一個通用寄存器 |
| S | ModR/M字節的reg字段選擇一個段寄存器 |
| U | ModR/M字節的R/M字段選擇一個128位的XMM寄存器。 |
| V | ModR/M字節的reg字段選擇一個128位的XMM寄存器。 |
| W | ModR/M字節跟隨操作碼并指定操作數。操作數要么是一個128位的XMM寄存器要么是一個內存地址。 |
| X | 由DS:rSI寄存器對尋址的內存 |
| Y | 由ES:rDI寄存器對尋址的內存 |
操作數符號
| a | 內存中的兩個單字操作數或內存中的兩個雙字操作數,具體取決于操作數大小屬性(僅由BOUND指令使用) |
| b | 字節 |
| c | 字節/字 |
| d | 雙字 |
| dq | 四字 |
| p | 32位/48位/80位指針 |
| pd | 128位封裝的雙精度浮點數據 |
| pi | 雙四字,MMX技術寄存器(例如:mm0) |
| ps | 128位封裝的單精度浮點數據 |
| q | 四字 |
| s | 6字節或10字節的偽描述符 |
| ss | 128位打包的單精度浮點數據的標量元素 |
| si | 雙字整數寄存器(例如:eax) |
| v | 字、雙字或四字(取決于當前CPU的模式) |
| w | 字 |
| z | 16位操作數大小的字或32位或64位操作數大小的雙字 |
上標符號
| 1A | ModR/M字節的第5、4和3位用作操作碼擴展 |
| 1B | 使用0F0B操作碼(UD2指令)或0FB9H操作碼時,故意嘗試生成無效的操作碼異常(#UD) |
| 1C | 在Pentium III處理器中添加的一些指令可以使用相同的雙字節操作碼。如果指令有變化,或者操作碼代表不同的指令,則使用ModR/M字節來區分指令。 |
| i64 | 指令在64位模式下無效 |
| o64 | 指令僅在64位模式下有效 |
| d64 | 當處于64位模式時,指令默認為64位操作數大小,并且不能對32位操作數大小進行編碼。 |
| f64 | 在64位模式下,操作數大小被強制為64位操作數大小(在64位模式下,該指令會忽略改變操作數大小的前綴)。 |
One-Byte Opcode Map
變長指令
描述:當操作系統遇到諸如0x88這樣的Opcode時,其指令序列為MOV Eb, Gb,目的是將一個8位的寄存器存儲到一個8位的寄存器或內存中,當表中出現E或G這樣的符號時,即存在ModR/M字段
思考:CPU如何知道要把值放在哪個寄存器中?
答案:使用ModR/M字段
ModR/M
描述:指令在引用一個不確定操作數時使用該字段進行定位
結構:
Reg/Opcode:用來確定寄存器或操作碼是什么
Mod+R/M:這兩個字段拼在一起確定是哪個內存或寄存器
例:0x88
指令格式:MOV Eb, Gb
例:88 00
Mod:00 Reg/Opcode:000 //使用0號寄存器,是EAX還是AL由操作碼決定 R/M:000 //Mod與R/M拼在一起指向[EAX]因此字節碼88 00對應的指令為:MOV [EAX], AL
注意:
例:0x89
指令格式:MOV Ev, Gv
例:89 49 10
Mod:01 Reg/Opcode:001 //使用1號寄存器 R/M:001 //Mod與R/M拼在一起指向[ECX]+disp8
因此字節碼89 49 10對應的指令為:MOV [ECX+0x10], ECX
例:0x8A
指令格式:MOV Gb, Eb
例:8A 92 78 56 34 12
Mod:10 Reg/Opcode:010 //使用2號寄存器 R/M:010 //Mod與R/M拼在一起指向[EDX]+disp32
因此字節碼8A 92 78 56 34 12對應的指令為:MOV DL, [EDX+0x12345678]
例:0x8B
指令格式:MOV Gv, Ev
例:8B DB
Mod:11 Reg/Opcode:011 //使用3號寄存器 R/M:011 //Mod與R/M拼在一起指向EBX
因此字節碼8B DB對應的指令為:MOV EBX, EBX
SIB
描述:
結構:
Scale: 2的幾次方
Index:下標
Base:確定是哪個寄存器
例:DS:[EAX+ECX2+0x12345678]
scale表示21
Index表示ECX
Base表示EAX
0x12345678由ModR/M字段決定
即:SIB=Base+Index2的scale次方
[*]:取決于ModR/M中MOD字段的值
00 [scaled index] + disp32
01 [scaled index] + disp8 + [EBP]
10 [scaled index] + disp32 + [EBP]
例:88 84 48 78 56 34 12
Opcode:88
指令格式:MOV Eb, Gb
ModR/M:84
Mod:10 Reg/Opcode:000 //使用0號寄存器AL R/M:100 //使用SIB字段[--][--]:查詢SIB字段
SIB:48
Scale:01 Index:001 //Scale與Index結合指向[ECX*2] Base:000 //使用0號寄存器EAX因此,字節碼88 84 48對應的指令為MOV BYTE PTR DS:[EAX+ECX*2+0x12345678], AL
例:89 84 84 78 56 34 12
Opcode:89
指令格式:MOV Ev, Gv
ModR/M:84
Mod:10 Reg/Opcode:000 //使用0號寄存器AL R/M:100 //使用SIB字段[--][--]:查詢SIB字段
SIB:84
Scale:10 Index:001 //Scale與Index結合指向[ECX*2] Base:000 //使用0號寄存器EAX
因此,字節碼88 84 48對應的指令為MOV DWORD PTR DS:[ESP+EAX*4+0x12345678], EAX
Opcode Extension Tables
描述:當操作系統遇到諸如0x80這樣的Opcode時,其指令序列為Eb, Ib,并無指明Opcode
思考:CPU如何知道Opcode是什么?
答案:查詢Opcode Extension Tables
例:80 65 08 FF
ModR/M:65
因此,字節碼80 65 08 FF對應的指令為AND BYTE PTR SS:[EBP+0x8], 0xFF
Instruction Prefixs
描述:指令前綴
段前綴
描述:
1)在早期8086CPU尋址范圍較小,Intel采用段寄存器*16+偏移的方式尋址
2)后來80386CPU擴大了尋址范圍,段寄存器便被用作了其它用途,不參與尋址
3)但是類似DS:[]這種格式被保留了下來
4)實際上操作碼已經決定了尋址時使用哪個段寄存器作為基址,不需要其他字節描述
注意:
1)如果沒有特別說明,[]前為DS,即DS:[]
2)若是像PUSH和POP指令,以及其它在[]中使用ESP/EBP的指令,默認前綴為SS
3)在[Base+Index*2Scale+I]中,以Base作為判斷條件,沒有特別說明,默認前綴為DS
4)如果Base為ESP/EBP,默認前綴為SS
5)EIP取指令時默認前綴為CS
6)如果指令加段寄存器前綴,則該條指令一律用這個段; 如果加多個段寄存器前綴,默認只看Opcode前面那個
默認值
CS:2E SS:36 DS:3E ES:26 FS:64 GS:65操作指令前綴:修改地址默認長度
描述:在無指令前綴的Opcode中,B0表示MOV AL, Ib,B8表示MOV EAX, Id,但卻不存在MOV AX, I這樣的Opcode,這是因為intel使用指令前綴完成這件事情
例:0x66
作用:將操作數改為16位模式
操作指令前綴:修改默認尋址方式
例:0x67
作用:將操作數改為16位模式
總結
以上是生活随笔為你收集整理的硬编码学习笔记(二)—— 经典变长指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 硬编码学习笔记(一)—— 经典定长指令
- 下一篇: Windows消息机制学习笔记(三)——