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 |
ゼロ拡張で移動します。 |
MOVSX と MOVZX は、ソースから宛先への符号拡張またはゼロ拡張を実行する 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 |
左に二重シフトします。 |
r1 を cl/#n で左にシフトし、r2/m の最上位ビットを入力します。 シフト アウトした最後のビットは、キャリーに配置されます。
SHRD |
r1, r2/m, cl/#n |
右に二重シフトします。 |
r1 を cl/#n で右にシフトし、r2/m の最下位ビットを入力します。 シフト アウトした最後のビットは、キャリーに配置されます。
ROL |
r1, cl/#n |
r1 を cl/#n で左に回転します。 |
ROR |
r1, cl/#n |
r1 を cl/#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 |
T を esi から edi に移動します。 |
|
CMPST |
esi の T を edi と比較します。 |
|
SCAST |
edi から T をスキャンして accT を確認します。 |
|
LODST |
esi から accT に T を読み込みます。 |
|
STOST |
accT から edi に T を格納します。 |
操作を実行すると、方向フラグ (上または下) の設定に従って、ソース レジスタと宛先レジスタが sizeof(T) の分だけインクリメントまたはデクリメントされます。
命令の前に REP を付けて、ecx レジスタで指定された回数の分だけ操作を繰り返すことができます。
rep mov 命令は、メモリのブロックをコピーするために使用されます。
rep stos 命令は、メモリ ブロックを accT で埋めるために使用されます。
フラグ
LAHF |
フラグから ah を読み込みます。 |
SAHF |
フラグに ah を格納します。 |
STC |
キャリーを設定します。 |
CLC |
キャリーをクリアします。 |
CMC |
キャリーを補完します。 |
STD |
方向を下に設定します。 |
CLD |
方向を上に設定します。 |
STI |
割り込みを有効にします。 |
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 |
カーネルにトラップします。 |
|
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 だけ左にシフトします。 |