3

JavaおよびPythonバイトコードは、C /C++コンパイラによって生成されたコンパイル済みマシンコードよりも逆コンパイルが比較的簡単です。

-gオプションからの情報がコンパイル解除には不十分であるが、デバッグには十分である理由について、説得力のある答えを見つけることができません。逆コンパイルを容易にするPython/Javaバイトコードに含まれる余分なものは何ですか?

4

3 に答える 3

9

これにはいくつかの理由があります。

  1. JavaとPythonのバイトコードは比較的単純で高レベルですが、一部のCPU(x86など)の命令セットは非常に複雑です。
  2. バイトコードは、それらが設計された言語の構造を厳密に模倣しています。
  3. バイトコードを生成する場合、JavaとPythonのパフォーマンスは、最適化によってほとんど実行されません。これにより、元のソースコードの構造に厳密に対応するバイトコードが生成されます。優れた最適化CまたはC++コンパイラは、元のソースコードから遠く離れたアセンブリを生成できます。
  4. JavaとPythonのコンパイラはほとんどなく、CとC++のコンパイラはたくさんあります。単一の既知のコンパイラー(または既知のコンパイラーの小さなセット)をターゲットにしている場合は、高品質の逆コンパイラーを作成する方が簡単です。
  5. PythonとJavaは、C ++と比較して比較的単純な言語です(この点はCには当てはまりません)。
  6. C ++テンプレートには、品質の逆コンパイルに多くの課題があります(この点はCにも当てはまりません)。
  7. C /C++プリプロセッサ。
  8. Pythonでは、ソースファイルとバイトコードファイルの間に1対1の関係があります。Javaでは、関係は1つ以上のバイトコードファイルへの1つのソースです。CおよびC++では、関係は多対多であり、ソースの前面で多くの重複があります(ヘッダーを考えてください)。
于 2013-03-25T07:10:37.213 に答える
2

-gオプションからの情報がコンパイル解除には不十分であるが、デバッグには十分である理由について、説得力のある答えを見つけることができません。

デバッグ情報には、基本的に、生成されたコードのアドレスとソースファイルの行番号の間のマッピングのみが含まれます。デバッガーはコードを逆コンパイルする必要はありません。元のソースを表示するだけです。ソースファイルが欠落している場合、デバッガーはそれらを魔法のように表示しません。

とはいえ、デバッグ情報が存在すると、逆コンパイルが容易になります。デバッグ情報に使用されているタイプと関数プロトタイプのレイアウトが含まれている場合、デコンパイラーはそれを使用して、はるかに正確な逆コンパイルを提供できます。ただし、多くの場合、元のソースとは異なる可能性があります。

たとえば、デバッグ情報を使用せずにHex-Raysデコンパイラーで逆コンパイルされた関数は次のとおりです。

int __stdcall sub_4050A0(int a1)
{
  int result; // eax@1

  result = a1;
  if ( *(_BYTE *)(a1 + 12) )
  {
    result = sub_404600(*(_DWORD *)a1);
    *(_BYTE *)(a1 + 12) = 0;
  }
  return result;
}

のタイプがわからないため、a1そのフィールドへのアクセスは追加とキャストとして表されます。

シンボルファイルがロードされた後の同じ関数は次のとおりです。

void __thiscall mytree::write_page(mytree *this, PAGE *src)
{
  if ( src->isChanged )
  {
    cache::set_changed(this->cache, src->baseAddr);
    src->isChanged = 0;
  }
}

かなり改善されていることがわかります。

バイトコードの逆コンパイルが通常簡単である理由については、NPEの回答に加えて、これも確認してください。

于 2013-03-25T14:05:17.190 に答える
0

x86プロセッサなどの一部のプロセッサには、可変長の命令があります。制御が命令の中央(=最初のバイトの後の任意の場所)に渡される場合、それも有効な命令(または複数の命令)である可能性があります。これにより、マシンコードを明確に分解することが困難になります。C /C++コードはこの機能を利用できます。

一部のプロセッサとOSでは、データをコードであるかのように実行し、コードをデータであるかのように使用することができます。これにより、2つを明確に分離することが困難になります。そして、繰り返しになりますが、これはC /C++プログラムが簡単に実行できることです。

一部のプロセッサとOSでは、コードをその場で生成して実行するのは簡単で、実行時に既存のコードを変更することができます。これも、コードの逆コンパイルのあいまいさの原因になります。また、C /C++プログラムでもこれを実行できることがよくあります。

編集:また、一部のCPUには、同じ命令に対して複数の異なるエンコーディングがあります。たとえば、x86CPUには2つの命令mov reg, reg/memとがありmov reg/mem, regます。これらを使用すると、レジスタとメモリ位置の間(いずれかの方向)および2つのレジスタ間でデータを移動できます。これらの命令は両方とも、2つのレジスタ間でデータを転送するために使用できますが、エンコーディングが異なります。プログラムが何らかの形で特定のエンコーディングに依存している場合(たとえば、チェックサムを介してその整合性を検証する目的で)、逆アセンブルから、元々mov eax, ebx2つの命令のどちらであったかがわからないため、再アセンブルしようとするとmov分解すると、プログラムが壊れることがあります。

デバッガーを使用して、デバッグ/シンボル情報の有無にかかわらずプログラムをデバッグできます。この情報は、生のアドレスや生のタイプレスデータだけでなく、名前とタイプを使用して多くの(必ずしもすべてではない)ルーチンと変数を識別および表示できるため、人間がコードとデータをナビゲートしやすくするだけです。

さまざまなバイトコードは、あいまいさが少なく、実行できることがより制限されていると思います。これにより、それらの逆コンパイルが容易になります。

于 2013-03-25T07:26:33.627 に答える