天天看點

X86架構指令模拟

前言:

要進行指令模拟,我們先需要了解X86架構下的指令是長什麼樣子的。根據intel的程式設計手冊我們找到了如下資訊。

Intel CPU的機器指令格式如下圖所示:

X86架構指令模拟

e.g.:圖檔位于intel開發手冊第二卷第二章的2.1

根據開發手冊,一條指令由 指令字首(Instruction Prefixes) + 操作碼(Opcode) + ModR/M + SIB + 偏移(displacement) + 立即數(Immediate data)幾部分組成,一條指令至少需要有Opcode,其它幾部分,在不同指令中可能存在可能不存在。今天我們主要來看Opcode、Instruction Prefixes、Immediate data三部分在不同指令中的使用。

1.  指令字首

指令字首分為四組,每組都有一組允許的字首代碼。對于每條指令,它僅在包含來自四個組(組 1、2、3、4)中的每一組的最多一個字首代碼時有用。第 1 組至第 4 組可以以任何相對于彼此的順序放置。
第一組:封鎖和重複執行字首
      

    F0H: LOCK字首,封鎖總線。在有數的指令(如ADD,ADC)前方時,使指令變為原子操作,并與被修飾的指令一起提供記憶體屏障效果。

    F2H:REPNE/REPNZ字首(隻位于字元串指令前)

    F3H:REP字首(隻位于字元串指令前)

    F3H:(與REP字首同碼)

第二組:段字首      

    在32位彙編中,有8個段寄存器:ES、CS、SS、DS、FS、GS、LDTR、TR(順序固定),不再用段寄存器尋址而隻做權限控制。字首和寄存器對應如下:

2E - CS
36 - SS
3E - DS
26 - ES
64 - FS
65 - GS      

    使用字首修飾後,指令(opcode)的預設段寄存器會被修改,如預設的MOV操作從DS段拿資料,加上36字首後會基于SS段位址取資料。

第三組:修改操作數預設長度      

     66H,用來“反轉”預設的16位或32位操作數寬度。例如,當預設的操作數寬度是32位時,可以用這個字首選擇16位寬度的操作數,或者反之。如下指令碼和彙編對照:

50:  PUSH EAX
6650: PUSH AX      
第四組:修改預設位址長度      

    67H,用來“反轉”預設的16位或32位位址寬度。例如,當預設的位址寬度是32位時,可以用這個字首選擇16位寬度的位址,或者反之。如下指令碼和彙編對照:

8801:   MOV DS:[ECX],AL
678801: MOV DS:[BX+DI], AL      

2.  指令碼(opcode)

    主操作碼的長度可以是 1、2 或 3 個位元組。如果主操作碼不能确定指令則會有額外的字段編輯倒ModR/M中。如果主操作碼是0x0f開頭則需要取第二位元組,如果主操作碼開頭是0x0f38,0x0f3a開頭則再取第三位元組。

    從intel程式設計手冊中截取一位元組指令碼格式如下圖:

X86架構指令模拟