Hex- RaysDecompilerの作者であるIlfakGuilfanovが、あるコンで彼の逆コンパイラーの内部動作についてスピーチをしました。ここにホワイトペーパーとプレゼンテーションがあります。これは、逆コンパイラーを構築する際のすべての困難と、それをすべて機能させる方法の概要を説明しています。
それとは別に、クリスティーナ・シフエンテスの古典的な博士論文など、かなり古い論文がいくつかあります。
複雑さに関しては、すべての「逆コンパイル」は、バイナリの言語と実行時間に依存します。たとえば、.NETとJavaの逆コンパイルは、成功率が非常に高い(元のソースを生成する)無料の逆コンパイラが利用可能であるため、「完了」と見なされます。ただし、これは、これらのランタイムが使用する仮想マシンの非常に特殊な性質が原因です。
C、C ++、Obj-C、Delphi、Pascalなどの真にコンパイルされた言語に関しては、タスクははるかに複雑になります。詳細については、上記の論文をお読みください。
逆アセンブラと逆コンパイラの違いは何ですか?
バイナリプログラム(実行可能ファイル、DLLライブラリなど)がある場合、それはプロセッサ命令で構成されます。これらの命令の言語は、アセンブリ(またはアセンブラ)と呼ばれます。バイナリでは、これらの命令はバイナリエンコードされているため、プロセッサは直接実行できます。逆アセンブラはこのバイナリコードを取得し、テキスト表現に変換します。この翻訳は通常1対1です。つまり、1つの命令が1行のテキストとして表示されます。このタスクは複雑ですが、簡単です。プログラムは、すべての異なる命令と、それらがバイナリでどのように表されるかを知る必要があります。
一方、逆コンパイラーははるかに難しいタスクを実行します。バイナリコードまたは逆アセンブラ出力(1対1であるため、基本的に同じ)のいずれかを受け取り、高レベルのコードを生成します。例を示しましょう。このC関数があるとしましょう:
int twotimes(int a) {
return a * 2;
}
コンパイルすると、コンパイラは最初にその関数のアセンブリファイルを生成します。これは次のようになります。
_twotimes:
SHL EAX, 1
RET
(最初の行は単なるラベルであり、実際の命令ではありませんSHL
。左シフト操作を実行します。これは、2をすばやく乗算RET
することで、関数が完了したことを意味します)。結果のバイナリでは、次のようになります。
08 6A CF 45 37 1A
(私はそれを作りました、本当のバイナリ命令ではありません)。これで、逆アセンブラがバイナリ形式からアセンブリ形式に移動することがわかりました。逆コンパイラーは、アセンブリーフォームからCコード(または他の高級言語)に移動します。