ハンド (逆) アセンブルのための x86 ニーモニックの覚え方
バイナリ列を見て x86 のコードかな〜とニヨニヨできる人に、x86 のコードであること、だけじゃなく実際のコード列も読めるようになってほしい!そんな願いから、今回は hex dump のバイト列を見つめてハンド逆アセンブルできるようになるための、効率良い覚え方を紹介します。
今回は、32-bit x86 について解説するよ。
まとめて覚えておきたい 8 つの命令、add, sub, adc, sbb, and, or, xor, cmp (00h〜3Dh)
これらの命令は近い所に配置されていて、しかも命令のルールがほとんど同じです。
つまり、ほとんど同じように覚えることができるのです。opcode map 上では次の領域が相当します。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
0 | ■ | ■ | ■ | ■ | ■ | ■ | ←add | ■ | ■ | ■ | ■ | ■ | ■ | ←or | ||
1 | ■ | ■ | ■ | ■ | ■ | ■ | ←adc | ■ | ■ | ■ | ■ | ■ | ■ | ←sbb | ||
2 | ■ | ■ | ■ | ■ | ■ | ■ | ←and | ■ | ■ | ■ | ■ | ■ | ■ | ←sub | ||
3 | ■ | ■ | ■ | ■ | ■ | ■ | ←xor | ■ | ■ | ■ | ■ | ■ | ■ | ←cmp | ||
4 |
並びの覚え方ですが… xor と cmp に関してはこのまま覚えましょう。その他については…
- 00h〜 と 08h〜: ビットや値を (足す) 感じの操作 (add と or)
- 10h〜 と 18h〜: キャリーを使った加減算 (adc と sbb)
- 20h〜 と 28h〜: ビットや値を (引く) 感じの操作 (and と sub)
- ここでは sub が後に来ていることに注意。
さて、ここでは add 命令 (00h〜05h) について詳しく見てみましょう。これが分かれば、他の 7 つの命令もおのずと理解できるはずです。
00h, 02h: レジスタやメモリに対する操作 (バイト)
命令フォーマット: [00h|02h] [Mod R/M] ([SIB]) ([Address Offset])
00h と 02h の違いはデータがどちらに流れるかというものです。
00h: add reg/mem ← reg 02h: add reg/mem → reg
x86 の基本的な (Mod R/M を使う) 命令では、メモリオペランドは片方につけることしかできません。そのため、メモリをソースとして扱うかデスティネーションとして扱うかでオペコードが違うということになってきます。
ちなみに、レジスタ同士の場合 00h, 02h のどちらを使っても良いので、次の 2 つのバイト列は同じ操作を意味します。
00 c8: add al ← cl (add al, cl) 02 c1: add cl → al (add al, cl)
Mod R/M バイトは全部暗記しておくと読むのが楽になるけど、シェルコードの多くを読むだけであれば、Mod R/M バイトが C0h〜FFh ならレジスタ同士の操作 (SIB バイトやオフセットはつかない)、とだけ覚えておくと扱いやすいです。
01h, 03h: レジスタやメモリに対する操作 (ワード)
00h, 02h と基本は似ています。ただし、基本的にはワード (32-bit x86 の通常モードでは 32-bit) を扱います。ただし、プレフィックス 66h がオペコードの前についている場合、16-bit 操作になります。(16-bit モードの場合には、66h プレフィックスで 32-bit 操作になる)
命令フォーマット: [01h|03h] [Mod R/M] ([SIB]) ([Address Offset])
もちろん、メモリ操作の方向も似ています。
01h: add reg/mem ← reg 03h: add reg/mem → reg
というわけで、レジスタ同士の演算では 01h, 03h で同じ操作の別バイト列が作れます。
01 c8: add eax ← ecx (add eax, ecx) 03 c1: add ecx → eax (add eax, ecx)
もっと大きな命令グループ (レジスタ)
x86 のレジスタは 3 ビットの数値で表現されます。その数値が命令の 1 バイト目に含まれる命令グループを紹介しましょう。
AL | eAX | 0 |
CL | eCX | 1 |
DL | eDX | 2 |
BL | eBX | 3 |
AH | eSP | 4 |
CH | eBP | 5 |
DH | eSI | 6 |
BH | eDI | 7 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
4 | ■ | ■ | ■ | ■ | ■ | ■ | ■ | ■ | □ | □ | □ | □ | □ | □ | □ | □ |
5 | □ | □ | □ | □ | □ | □ | □ | □ | ■ | ■ | ■ | ■ | ■ | ■ | ■ | ■ |
9 | ■ | ■ | ■ | ■ | ■ | ■ | ■ | ■ | ||||||||
B | □ | □ | □ | □ | □ | □ | □ | □ | ■ | ■ | ■ | ■ | ■ | ■ | ■ | ■ |
条件分岐 (70h-7Fh)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
7 | ■ | □ | ■ | □ | ■ | □ | ■ | □ | ■ | □ | ■ | □ | ■ | □ | ■ | □ |
これには簡単な覚え方は…なさそうです。ただひとつだけ覚えておきたいのは、ある条件とその否定はかならず隣り合っていることです。つまり、short 条件分岐の 1 バイト目、最下位ビットを反転すると、条件が反対になるということです。
72 xx: JE short XX ↑ ↓ 73 xx: JNE short XX
…記事の書き方が雑になってないか?
気のせいではないです、今日中に公開することを急いだ結果がこれだよ! そのうちちゃんと書き足すです。
ハンド (逆) アセンブルを補助するための PDF リスト!
今回はこれを簡単に実現するために、手軽に印刷して参照できる PDF を作ってみました。
http://dl.dropbox.com/u/2476414/TechResources/x86_opcodemap_1_a4.pdf
http://dl.dropbox.com/u/2476414/TechResources/x86_opcodemap_1_b4.pdf
余白の大きい B4 版をオススメします。手元の紙に手軽に印刷できるよう一応 A4 版も作りましたが…ぶっちゃけ見づらいです。
続きは何書く?
Mod R/M バイトについてほとんど何も解説せずに記事を書き終えてしまった (しかもオペコード表だけだと分からない) ので、次回このシリーズを書くとしたら、 Mod R/M バイトと SIB バイトについて、あと Mod R/M を覚えないと書けない単項 (Unary) 命令についても解説してみようと思います。