とにかくこれを行うことができますか?私は objdump を使用しましたが、私が知っているアセンブラで受け入れられるアセンブリ出力を生成しません。実行可能ファイル内の命令を変更して、後でテストできるようにしたいと考えています。
8 に答える
これを行うための信頼できる方法はないと思います。マシン コード形式は非常に複雑で、アセンブリ ファイルよりも複雑です。コンパイルされたバイナリ (たとえば、ELF 形式) を取得して、同じ (または十分に類似した) バイナリにコンパイルされるソース アセンブリ プログラムを生成することは実際には不可能です。違いを理解するには、GCC をアセンブラーに直接コンパイルした場合の出力 ( gcc -S
) と、実行可能ファイルの objdump の出力 ( ) を比較してくださいobjdump -D
。
私が考えることができる2つの主要な合併症があります。まず、マシン コード自体は、ポインターのオフセットなどのために、アセンブリ コードと 1 対 1 で対応していません。
たとえば、Hello world への C コードを考えてみましょう。
int main()
{
printf("Hello, world!\n");
return 0;
}
これは x86 アセンブリ コードにコンパイルされます。
.LC0:
.string "hello"
.text
<snip>
movl $.LC0, %eax
movl %eax, (%esp)
call printf
.LCO は名前付き定数で、printf は共有ライブラリ シンボル テーブル内のシンボルです。objdump の出力と比較します。
80483cd: b8 b0 84 04 08 mov $0x80484b0,%eax
80483d2: 89 04 24 mov %eax,(%esp)
80483d5: e8 1a ff ff ff call 80482f4 <printf@plt>
まず、定数 .LC0 は現在、メモリ内のどこかのランダム オフセットです。アセンブラとリンカはこれらの定数の場所を自由に選択できるため、この定数を正しい場所に含むアセンブリ ソース ファイルを作成することは困難です。
第二に、これについては完全にはわかりませんが (位置に依存しないコードなどに依存します)、printf への参照は実際にはそのコードのポインター アドレスでまったくエンコードされていないと思いますが、ELF ヘッダーには実行時にアドレスを動的に置き換えるルックアップ テーブル。したがって、逆アセンブルされたコードは、ソース アセンブリ コードと完全には一致しません。
要約すると、ソース アセンブリにはシンボルが含まれていますが、コンパイルされたマシン コードには反転が困難なアドレスがあります。
2 つ目の大きな問題は、動的にリンクするライブラリや、元のコンパイラによってそこに配置されたその他のメタデータなど、元の ELF ファイル ヘッダーに存在していたすべての情報をアセンブリ ソース ファイルに含めることができないことです。これを再構築するのは難しいでしょう。
前述したように、特別なツールでこのすべての情報を操作できる可能性はありますが、再アセンブルして実行可能ファイルに戻すことができるアセンブリ コードを簡単に生成できるとは考えにくいです。
実行可能ファイルのほんの一部を変更することに関心がある場合は、アプリケーション全体を再コンパイルするよりもはるかに巧妙な方法をお勧めします。objdump を使用して、関心のある関数のアセンブリ コードを取得します。手動で「ソース アセンブリ構文」に変換します (ここでは、入力と同じ構文で逆アセンブリを実際に生成するツールがあればいいのにと思います)。 、必要に応じて変更します。完了したら、それらの関数だけを再コンパイルし、objdump を使用して、変更したプログラムのマシン コードを見つけます。次に、16 進エディタを使用して、元のプログラムの対応する部分の上に新しいマシン コードを手動で貼り付けます。新しいコードが古いコードと正確に同じバイト数になるように注意してください (そうしないと、すべてのオフセットが間違っている可能性があります)。 )。新しいコードが短い場合、NOP 命令を使用してパディングできます。それよりも長い場合、問題が発生する可能性があり、代わりに新しい関数を作成して呼び出す必要がある場合があります。
@mgiuca は、技術的な観点からこの回答に正しく対処しました。実際、実行可能プログラムを再コンパイルしやすいアセンブリ ソースに逆アセンブルすることは、簡単な作業ではありません。
議論に少し追加すると、技術的に複雑ではありますが、探索するのに興味深いテクニック/ツールがいくつかあります。
- 静的/動的計測。この手法には、実行可能形式の分析、特定の目的のための特定のアセンブリ命令の挿入/削除/置換、実行可能ファイル内の変数/関数へのすべての参照の修正、および変更された新しい実行可能ファイルの発行が含まれます。私が知っているいくつかのツールは、 PIN、Hijacker、PEBIL、DynamoRIOです。そのようなツールを設計された目的とは異なる目的に合わせて構成するのは難しい可能性があり、実行形式と命令セットの両方を理解する必要があることを考慮してください。
- 完全な実行可能逆コンパイル。この手法では、実行可能ファイルから完全なアセンブリ ソースを再構築しようとします。仕事をしようとするオンライン逆アセンブラーを一目見たいと思うかもしれません。とにかく、さまざまなソースモジュールと、おそらく関数/変数名に関する情報が失われます。
- リターゲット可能な逆コンパイル。この手法は、コンパイラーのフィンガープリント(つまり、既知のコンパイラーによって生成されたコードのパターン) やその他の決定論的な要素を調べて、実行可能ファイルからより多くの情報を抽出しようとします。主な目標は、C ソースなどの高レベルのソース コードを実行可能ファイルから再構築することです。これにより、関数/変数名に関する情報を取り戻すことができる場合があります。でソースをコンパイルすると、
-g
多くの場合、より良い結果が得られることを考慮してください。Retargetable Decompilerを試してみることをお勧めします。
このほとんどは、脆弱性評価および実行分析の研究分野からのものです。これらは複雑な手法であり、多くの場合、ツールを箱から出してすぐに使用することはできません. それでも、一部のソフトウェアをリバース エンジニアリングしようとする場合、これらは非常に役立ちます。
バイナリ アセンブリ内のコードを変更するには、通常、3 つの方法があります。
- 定数のような些細なことであれば、16進エディタで場所を変更するだけです。最初にそれを見つけることができると仮定します。
- コードを変更する必要がある場合は、LD_PRELOAD を使用してプログラム内の関数を上書きします。ただし、関数が関数テーブルにない場合は機能しません。
- 修正したい関数のコードをハックして、LD_PRELOAD を介してロードした関数に直接ジャンプし、同じ場所にジャンプして戻します (これは上記 2 つの組み合わせです)。
もちろん、アセンブリが何らかの自己整合性チェックを行う場合、2 番目のものだけが機能します。
編集:それが明らかでない場合、バイナリアセンブリをいじるのは非常に高レベルの開発者の仕事であり、本当に具体的なことでない限り、ここでそれについて尋ねるのは難しいでしょう。
あなたが興味を持っているかもしれないもう一つのこと:
- バイナリ インストルメンテーション - 既存のコードの変更
興味がある場合は、Pin、Valgrind (またはこれを行うプロジェクト: NaCl - Google のネイティブ クライアント、おそらく QEmu) を確認してください。
ptrace (つまり、gdb のようなデバッガー) の監視下で実行可能ファイルを実行すると、実際のファイルを変更することなく、実行を制御できます。もちろん、影響を与えたい特定の命令が実行可能ファイルのどこにあるかを見つけるなど、通常の編集スキルが必要です。