9

私はPEファイルパーサーを作成しているところですが、PEファイル内の実際のコードを解析して解釈したいところに到達しました。これはx86オペコードとして保存されていると想定しています。

例として、DLL内の各エクスポートは、関数がメモリ内に格納されるRVA(相対仮想オフセット)を指し、これらのRVAを物理ファイルオフセットに変換する関数を作成しました。

問題は、これらは本当にオペコードなのか、それとも他の何かなのかということです。

関数がファイル内にどのように格納されるかはコンパイラ/リンカに依存しますか、それとも1バイトまたは2バイトのX86オペコードですか。

例として、Windows 7 DLL'BWContextHandler.dll'には、メモリにロードされ、システム内で使用できるようにする4つの関数が含まれています。最初にエクスポートされる関数は「DllCanUnloadNow」で、ファイル内のオフセット0x245Dにあります。このデータの最初の4バイトは次のとおりです。0xA10x5C0xF10xF2

それで、これらの1バイトまたは2バイトのオペコードですか、それともまったく別のものですか?

誰かがこれらを調べる方法についての情報を提供することができれば、それはありがたいです。

ありがとう!

さらに少し読んで、IDAのデモバージョンでファイルを実行した後、最初のバイト0xA1は1バイトのオペコード(mov eaxを意味する)であると言ったのは正しいと思います。私はここからそれを得ました:http://ref.x86asm.net/geek32.html#xA1そして私はそれが当分の間正しいと思います。

ただし、後続のバイトが残りの命令をどのように構成するかについては、少し混乱しています。私が知っているx86アセンブラーから、移動命令には宛先とソースの2つのパラメーターが必要なので、命令は(何かを)eaxレジスターに移動することであり、何かが次のバイトにあると想定しています。しかし、私はまだその情報を読む方法を知りません:)

4

2 に答える 2

6

x86エンコーディングは複雑なマルチバイトエンコーディングであり、RISC(MIPS / SPARC / DLX)の場合のように、命令テーブルで1行を見つけてデコードすることはできません。1つの命令の16バイトエンコーディングも可能です:1〜3バイトのオペコード+いくつかのプレフィックス(マルチバイトVEXを含む)+イミディエートまたはメモリアドレス、オフセット、スケーリング(imm、ModR / M、SIB; moffs)をエンコードするためのいくつかのフィールド。また、単一のニーモニックには数十のオペコードが存在する場合があります。さらに、いくつかのケースでは、同じasmラインの2つのエンコードが可能です( "inc eax"=0x40および=0xff0xc0)。

1バイトのオペコード。moveaxを意味します。私はここからそれを得ました:http://ref.x86asm.net/geek32.html#xA1そして私はそれが当分の間正しいと思います。

テーブルを見てみましょう。

po; flds; ニーモニック; op1; op2; grp1; grp2; 説明

A1; W; MOV; eAX; Ov; gen; datamov; 動く ;

(ヒント:geek32テーブルを使用せず、http://ref.x86asm.net/coder32.html#xA1に切り替えてください-「A1MOV eAX moffs16 / 32 Move」のように、より多くのデコードでフィールドが少なくなります)

オペランド用の列op1とop2、http: //ref.x86asm.net/#column_opがあります。A1オペコードの最初のものは常にeAXであり、2番目(op2)はOvです。表http://ref.x86asm.net/#Instruction-Operand-Codesによると:

O /moffsOriginal命令にはModR/Mバイトがありません。オペランドのオフセットは、命令内でワード、ダブルワード、またはクワッドワード(アドレスサイズ属性に応じて)としてコード化されます。ベースレジスタ、インデックスレジスタ、またはスケーリング係数は適用できません(MOV(A0、A1、A2、A3)のみ)。

したがって、A1オペコードの後、メモリオフセットがエンコードされます。x86(32ビットモード)には32ビットのオフセットがあると思います。

PS:タスクがPEの解析であり、逆アセンブラーを発明しない場合は、libdisasmやlibudis86などのx86逆アセンブルライブラリを使用してください。

PPS:元の質問の場合:

問題は、これらは本当にオペコードなのか、それとも他の何かなのかということです。

はい、「A1 5C F1 F2 05 B9 5C F1 F2 05 FF 50 0C F7 D8 1B C0 F7 D8 C3 CC CCCCCCCC」はx86マシンコードです。

于 2012-12-07T16:47:10.253 に答える
5

特にVisualStudioコンパイラによって生成されたコード、特にx86プログラムの場合、逆アセンブルは困難です。いくつかの問題があります:

  1. 命令は可変長であり、任意のオフセットで開始できます。一部のアーキテクチャでは、命令の調整が必要です。x86ではありません。アドレス0で読み取りを開始すると、オフセット1で読み取りを開始した場合とは異なる結果が得られます。有効な「開始位置」(関数のエントリポイント)が何であるかを知る必要があります。

  2. 実行可能ファイルのテキストセクションのすべてのアドレスがコードであるとは限りません。一部はデータです。Visual Studioは、「ジャンプテーブル」(switchステートメントの実装に使用される配列)を、それらを読み取るプロシージャの下のテキストセクションに配置します。データをコードとして誤って解釈すると、誤った分解が発生する可能性があります。

  3. 考えられるすべてのプログラムで機能する完全な分解を行うことはできません。プログラムは自分自身を変更できます。そのような場合、プログラムを実行してそれが何をするのかを知る必要があり、それが「停止問題」につながることになります。あなたが望むことができる最高のものは、「ほとんどの」プログラムで機能する分解です。

これらの問題に対処するために通常使用されるアルゴリズムは、「再帰下降」分解と呼ばれます。再帰下降パーサーと同様に機能し、既知の「エントリポイント」(exeの「メイン」メソッドまたはdllのすべてのエクスポート)で開始し、逆アセンブルを開始します。他のエントリポイントは、分解中に発見されます。たとえば、「呼び出し」命令が与えられると、ターゲットはエントリポイントであると見なされます。逆アセンブラは、検出されたエントリポイントを、検出されなくなるまで繰り返し逆アセンブルします。

ただし、その手法にはいくつかの問題があります。間接参照によってのみ実行されるコードは見つかりません。Windowsでの良い例は、SEH例外のハンドラーです。それらにディスパッチするコードは実際にはオペレーティングシステム内にあるため、再帰下降下降アセンブリはそれらを検出せず、それらを逆アセンブルしません。ただし、多くの場合、パターン認識(ヒューリスティックマッチング)で再帰下降を拡張することで検出できます。

機械学習を使用してパターンを自動的に識別することができますが、多くの逆アセンブラー(IDA proなど)は手書きのパターンを使用しており、かなりの成功を収めています。

いずれにせよ、x86コードを分解したい場合は、Intelのマニュアルを読む必要があります。サポートする必要のあるシナリオはたくさんあります。命令内の同じビットパターンは、修飾子、プレフィックス、プロセッサの暗黙的な状態などに応じて、さまざまな方法で解釈できます。これはすべてマニュアルで説明されています。ボリュームIの最初のいくつかのセクションを読むことから始めます。それは、基本的な実行環境をウォークスルーします。必要な残りのもののほとんどはボリュームIIにあります。

于 2012-12-08T20:04:34.637 に答える