それでは今回では残りの主要なニーモニックの説明に入ります。(その1)
ニーモニックのインデックスからサンプルプログラムがダウンロードできます。
書式:PUSH src
POP
dest
一時的にデータを保持するための領域にスタックというものがあります。PUSHはスタックポインタ(ESP)をdecrementし、srcをスタックのトップに転送します。POPは逆で、destにスタックのトップのデータを転送し、スタックポインタをincrementします。srcとdestはレジスタかメモリ参照が入ります。
あれこれ言うよりサンプル見たほうが理解は早いかと思いますw
00401000 >/$ B8
78563412 MOV
EAX,12345678
00401005 |. BA 32547698 MOV
EDX,98765432
0040100A |. 50
PUSH EAX
; EAXをスタックにプッシュ
0040100B |. 52
PUSH EDX
; EDXをスタックにプッシュ
0040100C |. 58
POP EAX
; EAXにスタックをポップ
0040100D |. 5A
POP EDX
; EDXにスタックをポップ
0040100E |. FF35 00304000 PUSH DWORD PTR DS:[403000]
; メモリ403000をスタックにプッシュ
00401014 |. FF35 04304000 PUSH DWORD PTR DS:[403004]
; メモリ403004をスタックにプッシュ
0040101A |. 8F05 00304000 POP DWORD PTR DS:[403000]
; メモリ403000にスタックをポップ
00401020 |. 8F05 04304000 POP DWORD PTR DS:[403004]
; メモリ403004にスタックをポップ
00401026 |. 6A 00
PUSH 0
; /ExitCode = 0
00401028 \. E8 01000000 CALL
<JMP.&kernel32.ExitProcess>
; \ExitProcess
0040100A〜0040100DでEAXとEDXの交換、0040100E〜00401020で[403000]と
[403004]の交換をしています。ちなみに、単に交換するだけならXCHG dest,srcでできますがw
スタックももちろんメモリにあります。ダンプウィンドウでESPの値のアドレスに飛んでみればスタックウィンドと同じものが見えます。実行ファイルもメモリ上にロードされて実行されているので00401000とかに飛んでみれば、B8 78 56 34
12・・・という数値が見えます。
○NOTとANDとORとXOR
書式:NOT src
AND
dest,src
OR
dest,src
XOR
dest,src
NOT srcでsrcのビットを反転させます。
AND,OR,XORもビットレベルの処理で、以下のようになります。
|
AND=論理積 |
OR=論理和 |
XOR=排他的論理和 |
|
0 AND 0 =
0 |
0 AND 0 =
0 |
0 AND 0 =
0 |
|
0 AND 1 =
0 |
0 AND 1 =
1 |
0 AND 1 =
1 |
|
1 AND 0 =
0 |
1 AND 0 =
1 |
1 AND 0 =
1 |
|
1 AND 1 =
1 |
1 AND 1 =
1 |
1 AND 1 =
0 |
|
両ビットが1の時のみ1 |
両ビットが0の時のみ0 |
両ビットが一致する時のみ0 |
上記の処理を対応するビットに対して行い、結果をdestに転送します。例えば、1010 AND 1101 =
1000で、1010 OR 1101 =
1111です。排他的論理和はその特徴として、「dest = srcなら必ず0になる」,「排他的論理和を取った値(論理演算結果)をdest(あるいはsrc)で排他的論理和を取るとsrc(あるいはdest)になる」というものがあります。後者はこの性質を利用して暗号化なんかに利用されているらしいです。
00401000 >/$ B8
02000000 MOV EAX,2
; EAXに0010bを転送(-3になる)
00401005 |. F7D0
NOT EAX
; 1010bのビットを反転
00401007 |. B8 0E000000 MOV EAX,0E
; EAXに1110bを転送
0040100C |. BA 05000000 MOV EDX,5
; EDXに0101bを転送
00401011 |. 23C2
AND EAX,EDX
; 1110bと0101bで論理積を取る
00401013 |. 0BC2
OR EAX,EDX
; 0100bと0101bで論理和を取る
00401015 |. BA 0F000000 MOV EDX,0F
; EDXに1111bを転送
0040101A |. 33C2
XOR EAX,EDX
; 0101bと1111bで排他的論理和を取る
0040101C |. 33C2
XOR EAX,EDX
; 1010bと1111bで排他的論理和を取る
0040101E |. 33C0
XOR EAX,EAX
; EAXに0をセット
00401020 |. 6A 00
PUSH 0
; /ExitCode = 0
00401022 \. E8 01000000 CALL
<JMP.&kernel32.ExitProcess>
; \ExitProcess
0040101CでEAXが0101b(5)に戻っています。0040101Eでは同じ値で排他的論理和を取っているので0になります。MOV EAX,0だと5BYTEも必要になるのに対して、これだと2BYTEでEAXを初期化できますね。
○CMPとTEST
書式:CMP src1,src2
TEST
src1,src2
いっしょに並べたら怒られそうな気もしますが・・・、どちらも条件分岐などに利用されることが多いのでw
CMP src1,src2はsrc1とsrc2を比較し、結果にしたがってフラグをセットします。やってることは計算結果を破棄するSUB命令です(SUBでもフラグのセットは行われている)。これで、src1,src2の比較の結果(どちらが大きいか、同じかなど)がフラグレジスタに一時的に保存されます。
TEST
src1,src2は結果を破棄するAND命令です。src1とsrc2で論理積を取り、0になれば0フラグをセットします。
セットしたフラグに応じて条件付きの処理が行えます。
フラグはレジスタウィンドウのCPAZSTDOと縦に書かれた所(フラグレジスタ)で確認できます。
|
<よく使うフラグ> |
|
|
C(キャリーフラグ) |
算術演算で繰り上がり(加算)や桁借り(減算)した時に「1」、しなかった場合は「0」になる。 |
|
Z(ゼロフラグ) |
演算結果が0になった場合に「1」、それ以外なら「0」になる。 一番重要! |
|
S(サインフラグ) |
演算結果がマイナスになった場合に「1」になる。このフラグは演算結果の最上位ビットがそのまま反映されている。 |
00401000 >/$ B8
02000000 MOV
EAX,2
00401005 |. BA 03000000 MOV
EDX,3
0040100A |. 3BC2
CMP EAX,EDX
; 2と3を比較→CSを1,Zを0に
0040100C |. BA 01000000 MOV
EDX,1
00401011 |. 3BC2
CMP EAX,EDX
; 2と1を比較→CZSを0に
00401013 |. BA 02000000 MOV
EDX,2
00401018 |. 3BC2
CMP EAX,EDX
; 2と2を比較→CSを0,Zを1に
0040101A |. B8 0F000000 MOV
EAX,0F
0040101F |. BA 05000000 MOV
EDX,5
00401024 |. 85C2
TEST EDX,EAX
; 0101bと1111bで論理積→Zを0に
00401026 |. B8 0A000000 MOV EAX,0A
; EAXに1010bを転送
0040102B |. 85C2
TEST EDX,EAX
; 0101bと1010bで論理積→Zを1に
0040102D |. 6A 00
PUSH 0
; /ExitCode = 0
0040102F \. E8 00000000 CALL
<JMP.&kernel32.ExitProcess>
; \ExitProcess
Z以外は「あ〜なんか大小比較してるぞぉ〜」って程度の認知でもいいですw
ちなみに、フラグレジスタをダブルクリックでフラグのON,OFFが切り替えられます。
○JX
書式:JX dest
条件分岐命令です。JXなんてニーモニックはありません。Xにはフラグを指定する文字が入ります。色々ありますが、雰囲気から察してやって下さいw destにはアドレスが入り、条件を満たしている時はそのアドレスにジャンプします。
主要なものを下に挙げます。
|
命令 |
元 |
意味 |
|
JE |
Jmup if equal |
Zフラグが立っていればジャンプ |
|
JZ |
Jmup if zero |
Zフラグが立っていればジャンプ |
|
JNE |
Jmup if not equal |
Zフラグが立っていなければジャンプ |
|
JNZ |
Jmup if not zero |
Zフラグが立っていなければジャンプ |
|
JA |
Jmup if avobe |
比較の結果、上なら(符号なし)ジャンプ |
|
JG |
Jmup if great |
比較の結果、大きいなら(符号付き)ジャンプ |
|
JNA |
Jmup if not avobe |
比較の結果、上でなければ(符号なし)ジャンプ |
|
JNG |
Jmup if not great |
比較の結果、大きくなければ(符号付き)ジャンプ |
|
JB |
Jmup if below |
比較の結果、下なら(符号なし)ジャンプ |
|
JL |
Jmup if less |
比較の結果、小さいなら(符号付き)ジャンプ |
|
JNB |
Jmup if not below |
比較の結果、下でなければ(符号なし)ジャンプ |
|
JNL |
Jmup if not less |
比較の結果、小さくなければ(符号付き)ジャンプ |
JAE(Jmup if avobe or equal)のように末尾にEを加えれば等号付きの条件(要するにJAE=JNB)になります。
また、JMP destで無条件分岐になります。(もちろん重要)
00401000 >/$ B8
01000000 MOV
EAX,1
00401005 |. BA FFFFFFFF MOV
EDX,-1
0040100A |. 3BD0
CMP EDX,EAX
; -1と1を比較
0040100C |. 7C 05
JL SHORT jx.00401013
; -1<1なのでジャンプ
0040100E |. BA 00000000 MOV EDX,0
; 実行されない
00401013 |> 3BD0
CMP EDX,EAX
; FFFFFFFFと1を比較
00401015 |. 72 05
JB SHORT jx.0040101C
; FFFFFFFF<1で無いのでジャンプしない
00401017 |. B9 04000000 MOV ECX,4
; ループカウンタをセット
0040101C |> 49
/DEC ECX
; ループカウンタをデクリメント
0040101D |.^75 FD
\JNZ SHORT jx.0040101C
; 4回ループ
0040101F |. 6A 00
PUSH 0
;
/ExitCode = 0
00401021 \. E8 00000000 CALL
<JMP.&kernel32.ExitProcess> ;
\ExitProcess
DECなど演算処理は普通フラグの変化を伴います。上記の例ではECXをデクリメントし続けて、0になったら分岐しなくなると言うことですね。
サンプルプログラムの著作権とかは面倒なので放棄してます。好きに使ってもらって構いません。