1. 程式人生 > >Intel硬編碼(一):Opcode Map、定長指令與指令字首

Intel硬編碼(一):Opcode Map、定長指令與指令字首

Intel CPU(基於P6微架構)的機器指令(硬編碼)格式如下圖所示:
這裡寫圖片描述

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

1、Opcode Map:

由於彙編指令對應的硬編碼數目很多,為其所有彙編指令設計出專門的硬編碼是不切實際的,所以一種格式的硬編碼,有可能因其具體的某一bit位不同而表示的彙編指令也有所微小差異,此微小差異一般是Opcode引起的,而由ModR/M和SIB的bit位來體現的(關於這點我們放到不定長指令中去討論)。現在研究學習的定長指令(指的是一個Opcode對應的指令長度是一定的),其對應的彙編指令格式是固定的(比如0X40不能加立即數和偏移量就只能表示inc eax

;而B0只能加一個位元組的立即數指令長度為2位元組,B0 XX即為mov al,XX不能能表示其他任何指令),但不管是定長還是不定長的硬編碼,其都可以從下面的Opcode Map表(TableA-2和TableA-3)中找查出來(紅色圈出來的是比較重要的以及我們要分析的定長指令):

這裡寫圖片描述

一個1Byte的定長指令,其16進製為類似於“AB”的形式,而第一個A是Opcode Map表中的行號,第二個的B是其列號,行號和列號就能確定一個具體的指令。比如:第4行第5列索引出來的指令為(綠色圈出來):inc ebp,第5行第0列為**push eax **等等。

但是由於指令的Opcode部分不止有一個位元組的,還有兩個位元組的,那麼兩個位元組的僅僅用該表是無法表示的,設計人員在設計時,留了一個特殊的位置即0F

,0F作為兩位元組指令的第一位元組,而第二位元組再另外一張表中。也就是說所有兩位元組的指令都是以0F開頭的(注意:這裡說的兩位元組都是僅僅指Opcode的長度)。而另外一張表(TableA-4與TableA-5)如下所示:
這裡寫圖片描述

A-4,A-5與A-2、A-3的查詢方式相同,由於2位元組長的Opcode比較複雜,這裡不做重點研究。我們重點來研究1Byte的Opcode,其具體細節我們分類來看。

2、修改通用暫存器的定長指令:

修改通用暫存器的常用定長指令,由於第4行前8列(Left)的inc reg指令和後8列(Right)的dec reg指令,還有第5行的前8列的push reg指令與後8列的pop reg

指令。以及第9行前8列的xchg eax,reg指令、第11行前8列的mov reg8,imm8指令、後8列的mov reg32,imm32指令等等(我們發現沒有mov reg16,imm16,至於為什麼我們在指令字首中便會提到)。

比如第五行:

前八列:
50 <–> push eax
51 <–> push ecx
52 <–> push edx
53 <–> push ebx
54 <–> push esp
55 <–> push ebp
56 <–> push esi
57 <–> push edi
後八列:
58 <–> pop eax
59 <–> pop ecx
5A <–> pop edx
5B <–> pop ebx
5C <–> pop esp
5D <–> pop ebp
5E <–> pop esi
5F <–> pop edi

第九行前八列:

90 xchg eax,eax <==> nop
91 <–> xchg eax,ecx
92 <–> xchg eax,edx
93 <–> xchg eax,ebx
94 <–> xchg eax,esp
95 <–> xchg eax,ebp
96 <–> xchg eax,esi
97 <–> xchg eax,edi

其它指令我們就不在一一列出來,只要稍微熟悉一下Opcode Map就可以爛熟於心了,我們可以發現,這些指令是有規律可偱的,都遵循暫存器編號的順序來為彙編指令設計編碼的,而暫存器變號我們也有必要提一下:
這裡寫圖片描述

3、修改EIP並且與JCC對應的定長指令:

硬編碼中的Opcode後面的立即數並非是跳轉的地址,“跳轉地址=當前指令地址+當前指令長度+imm”

(1)、第七行:近距離JCC跳轉

條件跳轉:Opcode後面跟一個立即數的偏移,因此指令共兩個位元組
立即數是有符號的:最高位為0(7F)向下跳,最高位為1(80)向上跳

70 <–> JO(O標誌位為1跳轉)
71 <–> JNO
72 <–> JB/JNAE/JC
73 <–> JNB/JAE/JNC
74 <–> JZ/JE
75 <–> JNZ/JNE
76 <–> JBE/JNA
77 <–> JNBE/JA
78 <–> JS
79 <–> JNS
7A <–> JP/JPE
7B <–> JNP/JPO
7C <–> JL/JNGE
7D <–> JNL/JGE
7E <–> JLE/JNG
7F <–> JNLE/JG

測試如下:
這裡寫圖片描述

80符號位為1為負數:
跳轉地址 = 當前指令地址 + 當前指令長度 + imm = 004193BE + 2 + 80 = 419340 < 004193BE ,即向上(之前的低地址)跳轉了。

7F符號位為0為正數:
跳轉地址 = 當前指令地址 + 當前指令長度 + imm = 004193C0 + 2 + 7F = 419441 > 004193C0,即向下(之後的高地址)跳轉了。

(2)、0F80~0F8F遠距離JCC跳轉:
後面跟一個四位元組的立即數,指令長共6位元組。立即數正負以7FFFFFFF、80000000為界。0F800F8F和707F對應列的指令一樣只不過立即數字節數不同。這裡也不再列出(注意0F80~0F8F是雙位元組Opcode應該在A-4與A-5中查詢:第八行)。
4、其它修改EIP的定長指令:

同JCC指令的硬編碼一樣,其硬編碼中Opcode後面的立即數也不是要跳轉的地址,計算方式同JCC相同:“跳轉地址=當前指令地址+當前指令長度+imm”

(1)、與ECX相關的跳轉指令(迴圈指令):

E0 <–> loopne/loopnz Ib(dec ecx) (ZF=0 && ECX != 0)
E1 <–> loope/loopz Ib(dec ecx) (ZF=1 && ECX != 0)
E2 <–> loop Ib(dec ecx) (滿足ECX != 0就跳轉)
E3 <–> jecxz/jrcxz Ib (滿足ECX=0跳轉)
注意: Ib即為byte型別立即數(Immediate data),Iw則是Immediate data word,Id即為Immediate data dword,Ap即六位元組長度的直接地址

測試如下所示:
這裡寫圖片描述

(2)、直接call與間接call指令:
所謂直接call即編譯時確定地址,間接call即地址存在記憶體中,並且在記憶體中的地址也是執行時才確定。

E8 <–> call Id
E9 <–> jmp Id
EA <–> jmp Ap,jmp CS:Id (**前四個位元組為跳轉地址,後兩個位元組為段選擇子.**即高兩位元組賦給CS,低四位元組賦給EIP)
EB <–> jmp Ib
FF <–> call dword ptr [edx]

E8call為直接call,call後面的地址即為要跳轉的地址,FFcall為間接call,後面跟的記憶體那隻能夠存放著即將要跳轉的地址。比如用物件指標訪問一個普通成員函式和一個虛擬函式,其call的硬編碼都不同:
這裡寫圖片描述

(3)、ret與reft:

C3 <–>ret (pop eip)
C2 <–>ret Iw (pop eip後,棧頂esp = esp + Iw)
CB <–>retf (出棧8位元組,低四位元組賦給EIP,高四位元組的低兩字賦給CS)
CA <–>retf Iw (在CB的基礎上再做一步esp = esp + Iw)

5、指令字首:

我們在上面提過,修改暫存器的指令中不存在16位暫存器修改的硬編碼。而這些與16位暫存器相關的編碼是通過加上**指令字首(Instruction Prefixes)**的方式來實現的,有指令字首則原本的32位暫存器操作指令,就會變為16位暫存器操作指令來用,不僅是定長指令如此,不定長指令也是如此。但指令字首不僅能進行16位32位暫存器操作硬編碼轉換,我們一一來看幾種常用的指令字首:

1、段字首:
首先在32位彙編中,8個段暫存器:ES、CS、SS、DS、FS、GS、LDTR、TR(順序固定),不再用段暫存器定址而只做許可權控制。段暫存器其實是個結構體,共96位,用匯編指令只能訪問其中16位。

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

如下所示:
這裡寫圖片描述
其中8925是Opcode,而不同的指令字首代表了不同的段暫存器。
注意:如果沒有特殊說明即沒有人為指定段字首,且:

①[]裡不存在ebp/esp/edi則預設為DS:[]
②[]裡存在ebp/esp則預設為SS:[]
③[]裡存在edi預設是ES:[],esi預設是DS:[]

2、操作指令字首:修改預設長度
這個即所謂指令字首解決無16位暫存器操作指令的問題:0X66字首修飾Opcode,則修正32位長度為16位:
如下所示(無論定長指令50還是不定長指令89均相同):
這裡寫圖片描述

3、操作指令字首:修改預設定址方式
0X67作為字首修改運算元寬度(將硬編碼預設對應的運算元寬度改為16位)
如下所示(操作指令字首將定址方式按16位彙編的定址方式進行定址):
這裡寫圖片描述
同一Opcode因為有無指令字首而長度不同,因此加上指令字首前後相當於Opcode的指令長度也是不定的,但是這些“不定長”可預見的。有字首則指令長度加一。而真正的不定長指令卻不是如此的。

**注意:**指令字首也是屬於第一張Opcode Map(包括A-2/A-3)之中的,一個字首佔用一個“位置”,就像雙位元組的Opcode其首位元組也是在第一張Opcode Map的0F位置的。否則是無法區分一條指令到底是字首還是單位元組Opcode還是雙位元組Opcode等等。

參考資料: Intel Architecture Software Developer’s Manual Volume 2: Instruction Set Reference(Intel白皮書卷2:指令集參考

相關推薦

Intel編碼Opcode Map指令指令字首

Intel CPU(基於P6微架構)的機器指令(硬編碼)格式如下圖所示: 一條指令由:**指令字首(Instruction Prefixes) + 操作碼(Opcode) + ModR/M + SIB + 偏移(displacement) + 立即數(Imme

Intel編碼不定指令ModR/MSIB詳解基於P6微架構

Intel硬編碼(一):Opcode Map、定長指令與指令字首 我們在Opcode Map中提到定長指令的索引方式,也分析了比較常見的一些定長指令,接著我們就要進行不定長指令的分析了。所謂不定長指得是SIB部分、Displcement、Immediate三部

[linux][MongoDB] mongodb學習MongoDB安裝管理工具

ole ont mon mkdir man 管理工具 tar end 認證 參考原文:http://www.cnblogs.com/kaituorensheng/p/5118226.html linux安裝完美實現! 1. mongoDB安裝、啟動、關閉   1.1

SQL夯實基礎inner joinouter join和cross join的區別

創建 color varchar mage bubuko where 是你 cross http 一、數據構建 先建表,再說話 create database Test use Test create table A ( AID int identity(1

JRtplib開發筆記JRtplib簡介JThread庫編譯

原博主部落格地址:https://blog.csdn.net/qq21497936 本文章部落格地址:https://blog.csdn.net/qq21497936/article/details/84785284          

文字編輯器啟用系列Sublime 安裝啟用漢化教程

前言 推薦幾款文字編輯器: Sublime:內嵌python直譯器、大量外掛 EditPlus:語法著色、內嵌瀏覽器 Notepad++:所見即所得功能 UltraEdit:程式設計師的最愛 印象筆記:免啟用 雲同步 Sublime的灰色

oracle中的函式介紹nvl函式decode函式case when函式sum函式

最近做專案接觸到的oracle資料庫比較多,經常用到裡面的一些函式,以前的部落格中也介紹過行轉列和列轉行,這次再簡單給大家介紹幾個: nvl() NVL(a,b)就是判斷a是否是NULL,如果不

深度學習—加快梯度下降收斂速度mini-batchStochastic gradient descent

在深層神經網路那篇部落格中講了,深層神經網路的區域性最優解問題,深層神經網路中存在區域性極小點的可能性比較小,大部分是鞍點。因為鞍面上的梯度接近於0,在鞍面上行走是非常緩慢的。因此,必須想辦法加速收斂速度,使其更快找到全域性最優解。本文將介紹mini-batch

《機器學習》學習筆記線性迴歸邏輯迴歸

    本筆記主要記錄學習《機器學習》的總結體會。如有理解不到位的地方,歡迎大家指出,我會努力改正。     在學習《機器學習》時,我主要是通過Andrew Ng教授在mooc上提供的《Machine Learning》課程,不得不說Andrew Ng老師在講授這門課程時,

Jenkins學習Jenkins安裝啟動外掛安裝

Jenkins安裝與基本配置 一、Jenkins安裝 在最簡單的情況下,Jenkins 只需要兩個步驟: 可以直接wget +下載地址下載 或者網站直接下載到windows機器上,然後通過ssh工具連線伺服器通過rz命令上傳本地下載的wa

c++並發編程基礎並發並行域多線程

競爭 安全 開發 引用 詳解 輕量 事情 地址空間 var 正文 C++11標準在標準庫中為多線程提供了組件,這意味著使用C++編寫與平臺無關的多線程程序成為可能,而C++程序的可移植性也得到了有力的保證。另外,並發編程可提高應用的性能,這對對性能錙銖必較的C++程序員來

Retrofit2+RxJava學習小計單檔案多檔案上傳之填平的坑

從Eclipse轉戰AndroidStudio已經有兩個月了。先誇誇Google親兒子的強大吧,各種方便就不一一道來了。主要是現在的Android陣營已經不想前兩年了。各種開源框架開源庫。也正是如此,AndroidStudio匯入開源的專案非常方便。自從Goog

網路基礎知識網路分層UDP協議

TCP/IP四層: 應用層、運輸層、網路層、鏈路層 應用層是使用者程序,而其他三層是核心工作 應用層協議(TCP): FTP(21) 檔案傳輸協議 Telent(23) 遠端登陸

shell學習筆記for迴圈if語句萬用字元命令代換引號

一、for迴圈for 變數in列表docommand1command2......done例1:vim test.sh#! /bin/bashfor str in name.listdoecho $strdone例2:vim tesh.sh#! /bin/bash for((

凸優化筆記仿射集,凸集

一.直線和線段 設為空間中的兩個點。 直線: 線段: 二.仿射集(Affine Set)凸集(Convex Set)和錐(Cones) 仿射集 仿射集:通過集合中

Python學習筆記基礎語法變數型別運算子快速入門篇

Head First Python、Python基礎教程 下劃線的特殊意義 以下劃線開頭的識別符號是有特殊意義的。 以單下劃線開頭(_foo)的代表不能直接訪問的類屬性,需通過類提供的介面進行訪問,不能用”from xxx import *”而匯入

微信小程式微信小程式申請註冊開發流程

本文主要用於介紹微信小程式開發過程中的注意點,查閱過程請結合微信的開發者文件一同觀看。 本文基於微信小程式公測版,IDE:微信開發者工具 0.10.102800 本文件用於幫助公司內部初學者,如有感覺拖沓請見諒 AppID申請 A

javase複習整理基礎要點重點易錯點多執行緒梳理總結

最近抽出時間從新回頭複習了一下javase基礎,把自己以前理解的不透徹和易錯的知識點重新梳理了一下,便於以後查閱。那麼接下來就開始複習總結! 一、java語言基礎 1、在java中,邏輯運算子“&

Python3+OpenCV學習筆記影象載入顯示和儲存

img = cv2.imread('Rachel.jpg')載入影象後,當然需要顯示出來,我們才能看到,所以,接下來用到第二個函式cv2.imshow(windowname, filename)“windowname”輸入顯示視窗的名稱,引數型別str,需要開啟多個視窗時,只需要“windowname”不同

磁碟管理磁碟結構命名分割槽

1、磁碟的物理結構: 一塊磁碟(機械硬碟)由多片碟片、一個機械手臂、多個磁頭和一個主軸馬達組成的。有N個碟片的磁碟則存在2*N個盤面(因為一個碟片正反兩面均寫有資料),而一個盤面分為可以分為M個磁軌,磁軌與磁軌之間存在間隙,一個磁軌分為L個扇區(Sector)