x86 指示
在本節清單中,以星號標示的指示 (*) 特別重要。 未標示的指示並不重要。
在 x86 處理器上,指令是可變大小的,因此反向反組譯是模式比對的練習。 若要從位址向後反組譯,您應該從某個點開始反組譯,比您真正想要更進一步的反組譯,然後向前查看直到指示開始有意義為止。 前幾個指示可能沒有任何意義,因為您可能已在指令中間開始反組譯。 可惜的是,反組解碼永遠不會與指令資料流程同步處理,而且您必須嘗試在不同的起點反組譯,直到您找到可運作的起點為止。
對於妥善封裝的 switch 語句,編譯器會將資料直接發出至程式碼資料流程,因此透過 switch 語句進行反組譯通常會輪轉到沒有意義 (的指示,因為它們實際上是資料) 。 尋找資料結尾並繼續反組譯。
指令標記法
指示的一般標記法是將目的地暫存器放在左邊,並將來源放在右邊。 不過,此規則可能有一些例外狀況。
算術指令通常是與來源和目的地暫存器結合的雙暫存器。 結果會儲存至目的地。
部分指示同時具有 16 位和 32 位版本,但此處只會列出 32 位版本。 此處未列出浮點指令、特殊許可權指示,以及僅用於分段模型的指示, (Microsoft Win32 不會使用) 。
為了節省空間,許多指令會以合併形式表示,如下列範例所示。
* |
MOV |
r1、 r/m/#n |
r1 = r/m/#n |
表示第一個參數必須是暫存器,但第二個參數可以是暫存器、記憶體參考或立即值。
為了節省更多空間,您也可以如下列所示來表示指示。
* |
MOV |
r1/m、 r/m/#n |
r1/m = r/m/#n |
這表示第一個參數可以是暫存器或記憶體參考,而第二個參數可以是暫存器、記憶體參考或立即值。
除非另有說明,否則使用這個縮寫時,您無法同時選擇來源和目的地的記憶體。
此外,位大小的尾碼 (8、16、32) 可以附加至來源或目的地,以指出參數必須是該大小。 例如,r8 表示 8 位暫存器。
記憶體、資料傳輸和資料轉換
記憶體和資料傳輸指令不會影響旗標。
有效位址
* |
Lea |
r, m |
載入有效位址。 (r = m) 位址 |
例如,LEA eax,[esi+4]表示eax = esi + 4。 這個指令通常用來執行算術。
資料傳輸
MOV |
r1/m、 r2/m/#n |
r1/m = r/m/#n |
|
MOVSX |
r1、 r/m |
使用符號延伸模組移動。 |
|
* |
MOVZX |
r1、 r/m |
以零副檔名移動。 |
MOVSX 和 MOVZX 是從來源到目的地執行符號延伸或零延伸模組的特殊 mov 指令版本。 這是唯一允許來源和目的地大小不同的指令。 (事實上,它們的大小必須不同。
堆疊操作
堆疊是由 esp暫存器所指向。 esp的值是最近推送的堆疊頂端 (,首先要彈出) ;較舊的堆疊元素位於較高的位址。
PUSH |
r/m/#n |
將值推送至堆疊。 |
|
POP |
r/m |
堆疊的 Pop 值。 |
|
PUSHFD |
將旗標推送至堆疊。 |
||
POPFD |
堆疊中的快顯旗標。 |
||
PUSHAD |
推送所有整數暫存器。 |
||
POPAD |
快顯所有整數暫存器。 |
||
ENTER |
#n,#n |
建置堆疊框架。 |
|
* |
離開 |
卸載堆疊框架 |
C/C++ 編譯器不使用 enter 指令。 (輸入 指示是用來實作 Algol 或 Pascal.) 等語言的巢狀程式
保留指示相當於:
mov esp, ebp
pop ebp
資料轉換
CBW |
將位元組 (al) 轉換成 (ax) 。 |
CWD |
將單字 (ax) 轉換為 dword (dx:ax) 。 |
CWDE |
將單字 (ax) 轉換為 dword (eax) 。 |
CDQ |
將 dword (eax) 轉換為 qword (edx:eax) 。 |
所有轉換都會執行符號延伸模組。
算術和位操作
所有算術和位操作指令都會修改旗標。
演算法
ADD |
r1/m、 r2/m/#n |
r1/m += r2/m/#n |
|
ADC |
r1/m、 r2/m/#n |
r1/m += r2/m/#n + carry |
|
SUB |
r1/m、 r2/m/#n |
r1/m -= r2/m/#n |
|
SBB |
r1/m、 r2/m/#n |
r1/m -= r2/m/#n + 攜帶 |
|
NEG |
r1/m |
r1/m = -r1/m |
|
公司 |
r/m |
r/m += 1 |
|
DEC |
r/m |
r/m -= 1 |
|
Cmp |
r1/m、 r2/m/#n |
計算 r1/m - r2/m/#n |
cmp指令會計算減法,並根據結果設定旗標,但會擲回結果。 其後面通常會接著條件 式跳躍 指示,以測試減法的結果。
MUL |
r/m8 |
斧頭 = 鋁 * r/m8 |
|
MUL |
r/m16 |
dx:ax ax = * r/m16 |
|
MUL |
r/m32 |
edx:eax eax * = r/m32 |
|
IMUL |
r/m8 |
斧頭 = 鋁 * r/m8 |
|
IMUL |
r/m16 |
dx:ax ax = * r/m16 |
|
IMUL |
r/m32 |
edx:eax eax * = r/m32 |
|
IMUL |
r1、 r2/m |
r1 *= r2/m |
|
IMUL |
r1、 r2/m、#n |
r1 = r2/m * #n |
不帶正負號和帶正負號的乘法。 乘法之後的旗標狀態為未定義。
Div |
r/m8 |
(ah, al) = (axr/m8, ax / % r/m8) |
|
Div |
r/m16 |
(dx,ax) = dx:ax / r/m16 |
|
Div |
r/m32 |
(edx,eax) = edx:eax / r/m32 |
|
IDIV |
r/m8 |
(ah, al) = ax / r/m8 |
|
IDIV |
r/m16 |
(dx,ax) = dx:ax / r/m16 |
|
IDIV |
r/m32 |
(edx,eax) = edx:eax / r/m32 |
不帶正負號和帶正負號的除法。 虛擬程式碼說明中的第一個暫存器會接收餘數,而第二個則會收到商數。 如果結果溢位目的地,就會產生除法溢位例外狀況。
未定義除法之後的旗標狀態。
* |
SETcc |
r/m8 |
將 r/m8 設定為 0 或 1 |
如果條件 cc 為 true,則 8 位值會設定為 1。 否則,8 位值會設定為零。
二進位編碼十進位
除非您正在偵錯以 COBOL 撰寫的程式碼,否則不會看到這些指示。
DAA |
加法後的小數調整。 |
|
Das |
減法後的小數調整。 |
這些指示會在執行已封裝的二進位編碼小數運算之後調整 al 暫存器。
Aaa |
ASCII 會在加法後調整。 |
Aas |
ASCII 會在減法之後調整。 |
這些指示會在執行解壓縮的二進位編碼小數運算之後調整 al 暫存器。
Aam |
ASCII 會在乘法之後調整。 |
AAD |
ASCII 會在除法後調整。 |
這些指示會在執行解壓縮的二進位編碼小數運算之後調整 al 和 ah 暫存器。
位
AND |
r1/m、 r2/m/#n |
r1/m = r1/m 和 r2/m/#n |
|
OR |
r1/m、 r2/m/#n |
r1/m = r1/m 或 r2/m/#n |
|
XOR |
r1/m、 r2/m/#n |
r1/m = r1/m xor r2/m/#n |
|
NOT |
r1/m |
r1/m = 位而非 r1/m |
|
* |
TEST |
r1/m、 r2/m/#n |
計算 r1/m 和 r2/m/#n |
測試指令會計算邏輯 AND 運算子,並根據結果設定旗標,但會擲回結果。 它通常會接著條件式跳躍指示,以測試邏輯 AND 的結果。
SHL |
r1/m、 cl/#n |
r1/m << = cl/#n |
|
Shr |
r1/m、 cl/#n |
r1/m >> = cl/#n零填滿 |
|
* |
SAR 里亞爾 |
r1/m、 cl/#n |
r1/m >> = cl/#n sign-fill |
最後一個位移出的位置會放在攜帶中。
SHLD |
r1、 r2/m、 cl/#n |
向左移雙精度浮點數。 |
將 r1 左移為 cl/#n,填滿 r2/m 的頂端位。 最後一個位移出的位置會放在攜帶中。
SHRD |
r1、 r2/m、 cl/#n |
向右移雙精度浮點數。 |
以cl/#n 向右移r1,填滿r2/m 的底部位。 最後一個位移出的位置會放在攜帶中。
ROL |
r1、 cl/#n |
依cl/#n向左旋轉r1。 |
Ror |
r1、 cl/#n |
以cl/#n 向右旋轉r1。 |
RCL |
r1、 cl/#n |
以cl/#n左旋轉r1/C。 |
RCR |
r1、 cl/#n |
以cl/#n 旋轉r1/C。 |
旋轉就像移動,不同之處在于移出的位會隨著傳入填滿位重新出現。 旋轉指令的 C 語言版本會將攜帶位併入旋轉中。
BT |
r1、 r2/#n |
將r1的位r2/#n複製到攜帶中。 |
BTS |
r1、 r2/#n |
設定r1的位r2/#n,將先前的值複製到 carry 中。 |
Btc |
r1、 r2/#n |
清除r1的位r2/#n,將先前的值複製到 carry。 |
控制流程
Jcc |
dest |
分支條件式。 |
|
JMP |
dest |
直接跳躍。 |
|
JMP |
r/m |
間接跳躍。 |
|
CALL |
dest |
直接呼叫 。 |
|
* |
CALL |
r/m |
呼叫間接。 |
呼叫指令會將傳回位址推送至堆疊,然後跳至目的地。
* |
Ret |
#n |
傳回 |
重試指令會快顯並跳至堆疊上的傳回位址。 RET指令中的非零#n表示在快顯傳回位址之後,應該將值#n新增至堆疊指標。
環 |
如果結果為非零,請遞減 ecx 並跳躍。 |
LOOPZ |
如果結果為非零且已設定zr,則遞減ecx並跳躍。 |
LOOPNZ |
如果結果為非零且zr清楚,請遞減ecx並跳躍。 |
JECXZ |
如果 ecx 為零,則跳躍。 |
這些指令是 x86 的 CISC 傳統,而且在最近的處理器中,實際上比寫出長路的對等指令慢。
字串操作
MOVST |
將 T 從 esi 移至 edi。 |
|
CMPST |
比較esi與edi的T。 |
|
SCAST |
從edi掃描T以取得 accT。 |
|
LODST |
將 T 從 esi 載入至 accT。 |
|
STOST |
從 acc T 儲存T到edi。 |
執行作業之後,來源和目的地暫存器會根據 (向上或向下) 方向旗標的設定,依 sizeof (T) 遞增或遞減。
指令前面可以加上 REP ,以重複 ecx 暫存器所指定的作業次數。
rep mov指令是用來複製記憶體區塊。
rep stos指令可用來使用 accT填滿記憶體區塊。
標誌
LAHF |
從旗標載入 ah 。 |
SAHF |
將 ah 儲存為旗標。 |
Stc |
設定攜帶。 |
Clc |
清除攜帶。 |
CMC |
補數攜帶。 |
性病 |
將方向設定為 向下。 |
CLD |
將方向設定為 向上。 |
性病 |
啟用中斷。 |
CLI |
停用中斷。 |
內嵌的指示
XCHG |
r1、 r/m |
交換 r1 和 r/m。 |
XADD |
r1、 r/m |
將 r1 新增至 r/m,並將原始值放在 r1 中。 |
CMPXCHG |
r1、 r/m |
比較和交換條件式。 |
cmpxchg指令是下列不可部分完成的版本:
cmp accT, r/m
jz match
mov accT, r/m
jmp done
match:
mov r/m, r1
done:
雜項
INT |
#n |
設陷到核心。 |
|
綁定 |
r, m |
如果 r 不在範圍內,則設陷。 |
|
* |
NOP |
無作業。 |
|
XLATB |
al = [ebx + al] |
||
BSWAP |
r |
在暫存器中交換位元組順序。 |
以下是 int 指令的特殊案例。
INT |
3 |
偵錯工具中斷點設陷。 |
INT 3的 opcode 0xCC。 NOP的 opcode 是0x90。
偵錯程式碼時,您可能需要修補一些程式碼。 您可以將違規的位元組取代為 0x90 來執行此動作。
習語
XOR |
r、 r |
r = 0 |
|
TEST |
r、 r |
檢查 r = 0。 |
|
* |
ADD |
r、 r |
將 r 向左移 1。 |