DIYの逆アセンブラー

長年にわたって機械語命令の構造を知ることは必須ではないため、人は自分をプログラマーと呼ぶことができます。 当然、これは常にそうではありませんでした。 最初のアセンブラーが登場する前は、プログラミングはマシンコードで直接実行されていました。 多数のエラーに関連付けられた重労働。 現代のアセンブラでは、コマンドをエンコードする方法である鉄から(合理的な範囲で)抽象化することができます。 高水準言語のコンパイラーについて言えます。 彼らは、実装の複雑さと、プログラマーがソースコードを一連の機械命令に変換できる(そして、十分な範囲で最適に変換できる)シンプルさに驚いています。 プログラマーからは、お気に入りの言語/ IDEの知識のみが必要です。 コンパイラーが元のリストに変換するものを知る必要はありません。

マシン命令のコーディング構造の簡単な説明、実装例、x86アーキテクチャ用の逆アセンブラのソースコードを参照したい方は、歓迎します。





x86アーキテクチャ用の逆アセンブラーの作成は、特に難しいタスクではありませんが、それでもかなり具体的です。 プログラマには特定の種類の知識が必要です。マイクロプロセッサがマシンコード内の一連の「バイト」を認識する方法に関する知識です。 すべての大学が、完全に機能する現代的な逆アセンブラーを作成するのに十分な量でそのような知識を得ることができるわけではありません-あなたは自分でそれを探す必要があります(通常英語) この投稿は、逆アセンブラを作成する問題を完全にカバーするふりをするものではなく、32ビットモードのコマンド実行であるx86アーキテクチャ用に逆アセンブラがどのように記述されたかを簡単に説明するだけです。 また、公式仕様からのいくつかの概念の翻訳において不正確になる可能性に注意したいと思います。



Intel x86のコマンド構造



チームの構造は次のとおりです。

•オプションのプレフィックス(各プレフィックスのサイズは1バイト)

•必須のコマンドオペコード(1または2バイト)

•Mod_R / M-コマンドのオペランドの構造を定義するバイト-オプション。

•コマンドのオペランドが占めるオプションのバイト(SIB [Scale、Index、Base]フィールド、オフセット、および即値の1バイトとして分割される場合があります)。



プレフィックス



次のプレフィックスが存在します。

最初の6つは、メモリセルにアクセスするときにコマンドが使用するセグメントレジスタを変更します。

•0x26-ESセグメント置換プレフィックス

•0x2E-CSセグメント置換プレフィックス

•0x36-SSセグメント置換プレフィックス

•0x3E-DSセグメント置換プレフィックス

•0x64-FSセグメント置換プレフィックス

•0x65-GSセグメント置換プレフィックス



•0x0F-追加コマンドのプレフィックス(実際のプレフィックスとは見なされない場合があります-この場合、コマンドのオペコードは2バイトで構成され、最初のバイトは0x0Fです)



•0x66-オペランドのサイズを再定義するためのプレフィックス(たとえば、eaxレジスタの代わりにaxが使用されます)

•0x67-アドレスサイズの再定義プレフィックス(以下を参照)

•0x9B-プレフィックスの待機(WAIT)

•0xF0-ロックプレフィックス(LOCKはマルチスレッドアプリケーションの同期に使用されます)

•0xF2-REPNZコマンドの繰り返しプレフィックス-バイトシーケンス(文字列)を使用

•0xF3-REPコマンドのプレフィックスの繰り返し-バイトシーケンス(文字列)を使用



これらの接頭辞はそれぞれ、機械語命令のセマンティクスや構造(たとえば、その長さやニーモニックの選択)を変更します。



オペコードチーム。

コマンドのオペコードは1つである場合があります。また、プレフィックスと一緒にコマンドのニーモニック(名前)を一意に定義する場合もあります。 多くのチームがあります。 また、最新のマイクロプロセッサの高度化により、その数は減りません-新しいチームが登場しますが、時代遅れのチームは消えません(下位互換性)。 オペコードとそれらに関連付けられたコマンドのリストは、通常、マイクロプロセッサメーカーの公式Webサイトからダウンロードできます。



Mod_R / Mバイトは、次のフィールドで構成されています。



•Mod-最初の2ビット(0〜3の値)

•R / M-次の3ビット(0〜7の値)

•ModR / Mの値-次の3ビット(0〜7の値)



実装:



逆アセンブラーを作成するには、次のページを使用します: http : //ref.x86asm.net/geek32.html



いくつかのテーブルがあります。 本質的に、逆アセンブラを記述するために必要なのはこれらのテーブルとそのフィールドの説明だけです。 もちろん、論理的な推論と空き時間がさらに必要です。

最初の表には、0x0Fプレフィックスを含まないマシン命令がリストされています。 2番目のリストには、この接頭辞を含む命令が含まれています(これらの命令のほとんどは、MMXファミリ以降のPentiumのマイクロプロセッサに登場しました)。

次の3つの表により、Mod_R / Mバイトを32ビット命令エンコードモードの命令オペランドのシーケンスに変換できます。 さらに、これら3つのテーブルの各後続は、前のテーブルの特殊なケースのMod_R / Mバイトの分析を明確にします。

最後の表では、Mod_R / Mバイトを16ビット命令エンコードモードの命令オペランドのシーケンスに変換できます。 デフォルトでは、コマンドは32ビットモードでエンコードされていると見なされます。 エンコードモードを変更するには、アドレスサイズの再定義プレフィックス(0x67)が使用されます。



最初に行うことは、最初の2つのテーブルを便利なデータ構造に転送することです。 同じサイトで、これらのテーブルのxmlバージョンをダウンロードし、それらを既に美しい構造構造に変換できます。 私は別の行動をとりました-HTMLテーブルをExcelにロードし、VBAで簡単なスクリプトを作成して、ソースコードを受け取りました。これは、手動で修正した後、必要なデータ構造でした。



分解アルゴリズム自体は非常に簡単です。



•現在の機械語命令で使用されるプレフィックスのリストがコンパイルされます

•対応するフィールドは、オペコード、プレフィックス、およびターゲット(必要な)マイクロプロセッサの生成(モデル)に応じて、2つのテーブルのいずれかで検索されます。

•見つかったレコードは、このコマンドのサポートが登場したマイクロプロセッサの世代(モデル)などのフィールドのリスト、またはこのコマンドで変更できるフラグのリストなどによって特徴付けられます。 主に、コマンドのニーモニック(名前)とオペランドのリストにのみ興味があります。 見つかったオペランドのすべてのオペランドとMod_R / Mバイトのフィールドを分析すると、コマンドのテキスト表現と長さがわかります。



オペランドの数の範囲は0〜3です。 ソーステーブルには、100種類を超えるオペランドが含まれています。 一部のオペランドは重複しています。名前は異なりますが、Mod_R / Mバイト(および場合によっては後続のバイト)の処理シーケンスは同じです。



さまざまなオペランドの処理の例と、最も単純な「Hello world」関数の逆アセンブルの例を表示するには、C ++ Builder 6コンパイラー逆アセンブラーソースコードをダウンロードできます。



PS:

この投稿を読む人がそこから引き出す情報を必要とすることは事実ではありません(逆アセンブラーはユニットを作成します) )



All Articles