1. 程式人生 > >IA-32指令解析詳解

IA-32指令解析詳解

IA-32指令解析詳解

0x00 前言

 這段時間忙於考試,資訊理論和最優化,還有演算法分析,有點讓人頭大。期間花了幾天看SEH機制,能明白個大概,但是對於VC++對於SHE的包裝似乎還是不是很明白,發現逆向工程核心原理對於這段寫的太簡單,至於加密與解密則是模稜兩可,軟體加密技術內幕倒是詳解了,可是太老了,程式碼又是彙編寫的總是編譯不通過。真是讓人難受!本來想寫一篇SEH詳解,但是基於以上原因暫時擱置。這兩天看了逆向核心原理關於IA-32指令解析的內容,發現還是挺有意思,遂記錄下來。

0x01 何謂IA-32的指令解析

  其實很簡單,就是把彙編的機器碼解析成組合語言的過程。下圖顯示了機器碼和彙編程式碼的關係。

 

0x02 解析所需知識點

  1. 彙編指令的格式

彙編指令有六部分構成,如下圖:

 

Prefix----指令字首(可選項)

Opand----操作指令(必選項)

Mod R/M---運算元輔助說明(可選項)

SIB----Mod R/M輔助說明(可選項,但是出現Mod R/M 這個必須有)

Displacement---運算元作為記憶體地址時用來表示位移(可選項)

Immediate ----表示運算元為立即數(可選項)

  2.重要欄位簡單說明

1)指令字首prefix大小為一個位元組,用來輔助說明指令的具體功能,可選項

例如:66:81FE 4746 CMP SI,474紅色字型就是指令字首,後面跟一個:。

2)操作指令opand,這個沒啥好說的,必選項,大小為1到3位元組,通常為一個位元組,多位元組後面會有說明.例如:66:81FE 4746 CMP SI,474

3)Mod R/M 大小為一個位元組,由三部分組成,分別為 Mod(位元組前兩位),Reg(位元組中間三位),R/M(位元組後三位)。Mod R/M的主要功能就是說明運算元的定址方式,包括暫存器選擇,記憶體運算元的偏移等等。例如:66:81FE 4746 CMP SI,474

4)SIB,大小為一個位元組,也是用來輔助運算元定址的,一般用於輔助Mod R/M,當出現基址加變址定址或者基址定址時要用到。898424  50020000 Mov [ESP+250],EAX.

 3. 為了能夠順利解析IA-32的機器碼,我們應該下載Intel的開發手冊,然後打印出有關指令解析的圖表。下載網址為:http://www.intel.com/products/processor/manuals

選擇第一個最完整的下載,如下圖:

 

下面是我總結的應該列印的頁碼數:

table 2-2 32-bit Addressing forms with the ModR/M Byte, page 510

table 2-3 32-bit Addressing forms with the SIB Bytes, page 511

APPENDIX A ,page 487

A.2.1 Codes for Addressing Method

A.2.2 Code for Operand Type

Table A-2 .one –byte Opcode Maps

Table A-3.two-bytes Opcode Maps

pages :2519-2530

Table A-6 Opcode Extension for one-and Two Opcodes by Group Number

pages:2535-2537

0x03 指令解析步驟

1) 操作碼對映

首先我們解析一個長度為一的操作碼,對應的表是Table A-2 .one –byte Opcode Maps

指令:41,將指令拆成4和1,4對應表的行向量,1對應表列向量,如下圖:

 

 

 

 

由上圖可知41對應的指令為INC ECX,我們可以使用od來印證正確性。如下圖:

 

至於為什麼用暫存器ECX,是因為IA-32預設使用32位暫存器。

2) 運算元的使用

指令68 A0B44000,我們先拆分68為6和8還是使用表Table A-2 .one –byte Opcode Maps

來查詢操作指令。如下圖:

 

上圖可知是push指令,運算元的定址方式有Iz規定,我們接下來來使用code for Addressing Method和表code for opcode type 來檢視具體資訊,第一個大寫字元I規定了定址方式,在code for Addressing Method查詢對應含義,如下圖:

 

表示這是一個立即數。第二個小寫字元z在表code for opcode type 中查詢,如下圖:

z表示使用的字元大小為字或者雙字,由於是在32位系統中預設使用雙字(使用單字的情況是使用字首來說明),故這裡是個雙字,Iz一起就是表明使用雙字立即數,表明指令68 A0B44000中A0B44000就是使用的雙字立即數。完整的指令翻譯過來就是:

Push 0004B4A0(注意彙編中的資料儲存),我們也來使用od驗證一下。如下圖:

 

3) 帶有Mod R/M的指令

上面兩個指令都很簡單,除了操作碼就是運算元本身,下面來解析帶有Mod R/M的指令,現在來看指令89C1。先解析89如下圖:

 

上圖可知指令是OR指令,兩個運算元的說明分別是Ev和Gv,這兩個使用表A.2.1 Codes for Addressing Method和表A.2.2 Code for Operand Type來檢視。Ev的查詢結果如下圖:

 

 

這裡應該主義的是,E中已經說明了要使用通用暫存器或者記憶體運算元,並且要使用Mod R/M來進行輔助說明,所以在操作碼89後的C1就是ModR/M,Ev統一起來就是可以使用雙子暫存器運算元或者雙子記憶體運算元,具體的要使用ModR/M欄位來輔助說明。我們把ModR/M欄位的內容c1(1100,0001)拆成三部分:Mod:11,Reg:000,R/M:001,下面根據這個資訊來查詢表table 2-2 32-bit Addressing forms with the ModR/M Byte,如下圖:

 

使用三個欄位分別找到Ev對應的暫存器是ECX,Gv使用的暫存器是EAX,因此完整指令應該為89C1 OR ECX,EAX,用od驗證如下圖:

 

4) 帶有Group指令解析

Group指令與ModR/M結合使用。Group給指令帶來了更為豐富的變化,也是的指令解析更為複雜。例如:83C3 12,先解析83,如下圖:

 

 

上圖可以看出該指令帶group指令。對於group指令我們還需查詢表Table A-6 Opcode Extension for one-and Two Opcodes by Group Number。我們使用ModR/M(C3=1100,0011)的第二個欄位Reg(000)來查詢,如下圖:

 

對應的指令為ADD,結合來看指令大致為ADD Ev,Ib,再根據ModR/M欄位值C3得出指令為ADD EBX,12。

5) 指令字首的使用

指令字首主要來輔助說明操作碼的,比如字首66就會規定指令為16位形式。字首的形式一般都是:字首:操作碼(這裡是顯示形式,其實在od中編寫不用寫符號:但是顯示會出現符號:)。

接下來來示範解析帶有字首的指令 66:81FE 3412。字首66的資訊查詢也是在表

Table A-2 .one –byte Opcode Maps(單位元組操作碼查詢表)中如下圖:

 

上圖表示操作碼的size位16位,接下來繼續解析81,如下圖:

 

說明還要使用ModR/M(FE=1111,1110)的欄位Reg(111)表Table A-6 Opcode Extension for one-and Two Opcodes by Group Number來查詢Group資訊。如下圖:

因此指令的形式是CMP Ev,Ib,由於字首規定使用16位運算元,所以指令解析為:

66:81FE 3412 CMP SI,1234(指令為16位的)。

6) 雙位元組操作碼的解析

雙位元組操作碼很好分辨,指令的第一個位元組為0F,指令對映參考表Table A-3.two-bytes Opcode Maps。接下來我們來解析指令0F85 FA1F0000。先解析0F,查詢表Table A-2 .one –byte Opcode Maps可得下圖:

 

可知這是一個雙位元組運算元,其實雙運算元的第一個位元組都是0F,再來查詢85,我們使用表Table A-3.two-bytes Opcode Maps。如下圖:

 

由上圖可知這是一個條件跳轉指令,而且是長跳轉,JCC +NE/NZ=JNE指令。Long型表明跳轉地址為四個位元組。這裡還需特別注意的是:機器碼0F85 FA1F0000的四個位元組是相對地址,要正確解析出指令還需算出實際的跳轉地址。公式為:實際地址=當前地址+指令大小+相對地址。我們用OD實際的運算一下:選定當前的地址是010073ee,則解析後的實際跳轉地址=010073ee+6(指令大小為五個位元組)+00001FFA= 01008EEE,所以完整的指令解析為:

JNZ 01008EEE。如下圖:

 

7) 同時含有位移值和立即數的情況

例如指令:C705 00CF4000 01000100

首先解析C7,如下圖:

 

這是一個含有group的指令,ModR/M為05(0000,0101),其中Reg值為:000,根據Reg值查詢group表,如下圖:

 

由此可見指令的形式為MOV  Ev,Iz,Ev表示使用ModR/M規定的暫存器雙字或者記憶體雙字。Iz表示立即數雙字。下面使用ModR/M來確定具體的定址方式,查詢ModR/M表,如下圖:

 

因此第一個運算元使用的32位記憶體運算元,預設的段暫存器是DS,第二個運算元是立即數。完整解析指令為:Mov DWORD DS:[40CF00],10001.

8) 使用SIB的情況

SIB也是用來輔助定址的,主要針對基址定址和基址加變址定址。

那如何來判斷指令是否使用了SIB呢?

且看指令8B0C01 ,先解析8B,如下圖:

 

指令形式為MOV Gv,Ev。Gv表示ModR/M規定的雙字暫存器數,Ev表示使用ModR/M規定的雙子暫存器數或者雙字記憶體數。接下倆解析ModR/M,欄位值為0C(00001100),查詢ModR/M表可得下圖:

 

由上圖可得指令的基本結構為MOV ECX,[Reg.A+Reg.B]。對於這種結構的定址方式就要用到SIB輔助定址,指令中的SIB=01,拆成三個欄位scale=00,Index=000,base=001,查詢表table 2-3 32-bit Addressing forms with the SIB Bytes如下圖:

 

因此完整的解析指令為:8B0C01 MOV ECX,DWORD PTR DS:[ECX,EAX],使用OD驗證一下,如下圖:

 

以上就是解析IA-32機器碼時會遇到的所有情況,掌握了這些解析機器碼不成問題。

0x04 感想

  IA-32指令解析本身不是很難,只要多多練習就能提高。還有就是解析是碰到記憶體運算元最好還是使用PTR說明符,雖然有些情況下可以不適用,但是OD預設的情況是碰到記憶體運算元就是用PTR。這點需要注意。