x86 の手順

このセクションの一覧では、アスタリスク (*) でマークされた命令が特に重要です。 そのようにマークされていない命令は重要ではありません。

x86 プロセッサでは命令のサイズが可変であるため、逆アセンブルはパターン マッチングの演習となります。 アドレスから逆アセンブルするには、実際の目的のポイントよりも後ろのポイントで逆アセンブルを開始し、命令が意味を持ち始めるまで前に進む必要があります。 命令の途中で逆アセンブルを開始した可能性もあるため、最初のいくつかの命令は意味をなさない可能性があります。 残念ながら、逆アセンブリが命令ストリームと同期することはありません。機能する開始ポイントが見つかるまで、別の開始ポイントで逆アセンブルを試す必要があります。

適切にパッキングされた switch ステートメントの場合、コンパイラはコード ストリームに直接データを出力するため、switch ステートメントを使用して逆アセンブルすると、通常は意味のない命令が発生します (実際にはデータであるため)。 データの終わりを見つけ、そこで逆アセンブルを続けます。

命令の表記

命令の一般的な表記では、左側に宛先レジスタを配置し、右側にソースを配置します。 ただし、この規則にはいくつかの例外が存在する可能性があります。

通常、算術命令は、ソース レジスタと宛先レジスタを組み合わせた 2 レジスタです。 結果は、宛先に保存されます。

一部の命令には、16 ビットと 32 ビットの両方のバージョンがありますが、ここに記載されているのは 32 ビット バージョンのみです。 ここに記載されているものは、セグメント化されたモデルでのみ使用される浮動小数点命令、特権命令、および命令を示しています (Microsoft Win32 では使用されません)。

スペースを節約するため、次の例に示すように、命令の多くは結合された形式で表されています。

*

MOV

r1, r/m/#n

r1 = r/m/#n

最初のパラメーターはレジスタでなければなりませんが、2 番目のパラメーターにはレジスタ、メモリ参照、または即時値を指定することができることを意味します。

さらにスペースを節約するため、次に示すように命令を表すこともできます。

*

MOV

r1/m, r/m/#n

r1/m = r/m/#n

最初のパラメーターはレジスタまたはメモリ参照、2 番目のパラメーターはレジスタ、メモリ参照、または即時値にすることができることを意味します。

特に明記されていない限り、この省略形を使用する場合、ソースと宛先の両方のメモリを選択することはできません。

さらに、ソースまたは宛先にビット サイズのサフィックス (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

ゼロ拡張で移動します。

MOVSXMOVZX は、ソースから宛先への符号拡張またはゼロ拡張を実行する mov 命令の特別なバージョンです。 これは、ソースと宛先を異なるサイズにすることができる唯一の命令です。 (実際には、異なるサイズでなければなりません。

スタック操作

スタックは esp レジスタによってポイントされます。 esp の値はスタックの一番上にあります (直近にプッシュされ、最初にポップする必要がある)。古いスタック要素は、上位のアドレスに存在しています。

プッシュ

r/m/#n

値をスタックにプッシュします。

POP

r/m

スタックから値をポップします。

PUSHFD

スタックにフラグをプッシュします。

POPFD

スタックからフラグをポップします。

PUSHAD

すべての整数レジスタをプッシュします。

POPAD

すべての整数レジスタをポップします。

Enter

#n, #n

スタック フレームを構築します。

*

LEAVE

スタック フレームを破棄

C/C++ コンパイラでは、enter 命令は使用されません。 (enter 命令は、Algol や Pascal などの言語で入れ子になったプロシージャを実装するために使用されます)。

leave 命令は、次のようになります。

mov esp, ebp
pop ebp

データ変換

CBW

byte (al) を word (ax) に変換します。

CWD

word (ax) を dword (dx:ax) に変換します。

CWDE

word (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 + carry

NEG

r1/m

r1/m = -r1/m

INC

r/m

r/m += 1

DEC

r/m

r/m -= 1

CMP

r1/m, r2/m/#n

r1/m - r2/m/#n を計算

cmp 命令は減算を計算し、結果に従ってフラグを設定しますが、結果はスローされます。 通常、減算の結果をテストする条件付き jump 命令が続きます。

MUL

r/m8

ax = al * r/m8

MUL

r/m16

dx:ax = ax * r/m16

MUL

r/m32

edx:eax = eax * r/m32

IMUL

r/m8

ax = al * 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) = (ax % r/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

符号なし除算および符号あり除算。 擬似コードの説明の最初のレジスタは剰余を受け取り、2 番目のレジスタは商を受け取ります。 結果が宛先をオーバーフローすると、除算オーバーフロー例外が生成されます。

除算後のフラグの状態が未定義です。

*

SETcc

r/m8

r/m8 を 0 または 1 に設定

条件 cc が true の場合、8 ビット値は 1 に設定されます。 それ以外の場合、8 ビット値はゼロに設定されます。

バイナリ コード 10 進数

COBOL で記述されたコードをデバッグする場合を除き、これらの命令は表示されません。

DAA

加算後の 10 進数調整。

DAS

減算後の 10 進調整。

これらの命令は、パックされたバイナリ コード 10 進演算を実行した後、al レジスタを調整します。

AAA

追加後の ASCII 調整。

AAS

減算後の ASCII 調整。

これらの命令は、アンパックされたバイナリ コード 10 進演算を実行した後、al レジスタを調整します。

AAM

乗算後の ASCII を調整。

AAD

除算後の ASCII 調整。

これらの命令は、アンパックされたバイナリ コード 10 進演算を実行した後、al および ah レジスタを調整します。

Bits

かつ

r1/m, r2/m/#n

r1/m = r1/m and r2/m/#n

OR

r1/m, r2/m/#n

r1/m = r1/m or r2/m/#n

XOR

r1/m, r2/m/#n

r1/m = r1/m xor r2/m/#n

NOT

r1/m

r1/m = bitwise not r1/m

*

TEST

r1/m, r2/m/#n

r1/m and r2/m/#n を計算

test 命令は論理 AND 演算子を計算し、結果に応じてフラグを設定しますが、結果はスローされます。 通常、論理 AND の結果をテストする条件付き jump 命令が続きます。

SHL

r1/m, cl/#n

r1/m <<= cl/#n

SHR

r1/m, cl/#n

r1/m >>= cl/#n zero-fill

*

SAR

r1/m, cl/#n

r1/m >>= cl/#n sign-fill

シフト アウトした最後のビットは、キャリーに配置されます。

SHLD

r1, r2/m, cl/#n

左に二重シフトします。

r1cl/#n で左にシフトし、r2/m の最上位ビットを入力します。 シフト アウトした最後のビットは、キャリーに配置されます。

SHRD

r1, r2/m, cl/#n

右に二重シフトします。

r1cl/#n で右にシフトし、r2/m の最下位ビットを入力します。 シフト アウトした最後のビットは、キャリーに配置されます。

ROL

r1, cl/#n

r1cl/#n で左に回転します。

ROR

r1, cl/#n

r1cl/#n で右に回転します。

RCL

r1, cl/#n

r1/C を cl/#n で左に回転します。

RCR

r1, cl/#n

r1/C を cl/#n で右に回転します。

回転はシフトに似ていますが、シフト アウトされたビットが受信フィル ビットとして再表示される点が異なります。 回転命令の C 言語バージョンでは、回転にキャリー ビットが組み込まれています。

BT

r1, r2/#n

r1 のビット r2/#n をキャリーにコピーします。

BTS

r1, r2/#n

ビット r2/#n r1 を設定し、前の値をキャリーにコピーします。

BTC

r1, r2/#n

ビット r2/#n r1 をクリアし、前の値をキャリーにコピーします。

制御フロー

Jcc

dest

分岐条件。

JMP

dest

直接ジャンプ。

JMP

r/m

間接ジャンプ。

CALL

dest

直接呼び出し。

*

CALL

r/m

間接呼び出し。

call 命令は、リターン アドレスをスタックにプッシュし、宛先にジャンプします。

*

RET

#n

Return

ret 命令がポップされ、スタック上のリターン アドレスにジャンプします。 RET 命令のゼロ以外の #n は、リターン アドレスをポップした後、#n 値をスタック ポインターに追加する必要があることを示します。

LOOP

結果がゼロ以外の場合、ecx と jump を減らします。

LOOPZ

結果がゼロ以外で、zr が設定されている場合、ecx と jump をデクリメントします。

LOOPNZ

結果がゼロ以外で、zr がクリアされている場合、ecx と jump をデクリメントします。

JECXZ

ecx がゼロの場合はジャンプします。

これらの命令は x86 の CISC 遺産の残りであり、最近のプロセッサでは、実際には長い道のりで書き出された同等の命令よりも遅くなります。

文字列操作

MOVST

Tesi から edi に移動します。

CMPST

esiTedi と比較します。

SCAST

edi から T をスキャンして accT を確認します。

LODST

esi から accTT を読み込みます。

STOST

accT から ediT を格納します。

操作を実行すると、方向フラグ (上または下) の設定に従って、ソース レジスタと宛先レジスタが sizeof(T) の分だけインクリメントまたはデクリメントされます。

命令の前に REP を付けて、ecx レジスタで指定された回数の分だけ操作を繰り返すことができます。

rep mov 命令は、メモリのブロックをコピーするために使用されます。

rep stos 命令は、メモリ ブロックを accT で埋めるために使用されます。

フラグ

LAHF

フラグから ah を読み込みます。

SAHF

フラグに ah を格納します。

STC

キャリーを設定します。

CLC

キャリーをクリアします。

CMC

キャリーを補完します。

STD

方向をに設定します。

CLD

方向をに設定します。

STI

割り込みを有効にします。

CLI

割り込みを無効にします。

インターロックされた命令

XCHG

r1, r/m

r1r/m をスワップします。

XADD

r1, r/m

r1r/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

カーネルにトラップします。

BOUND

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 だけ左にシフトします。