JavaおよびPythonバイトコードは、C /C++コンパイラによって生成されたコンパイル済みマシンコードよりも逆コンパイルが比較的簡単です。
-gオプションからの情報がコンパイル解除には不十分であるが、デバッグには十分である理由について、説得力のある答えを見つけることができません。逆コンパイルを容易にするPython/Javaバイトコードに含まれる余分なものは何ですか?
JavaおよびPythonバイトコードは、C /C++コンパイラによって生成されたコンパイル済みマシンコードよりも逆コンパイルが比較的簡単です。
-gオプションからの情報がコンパイル解除には不十分であるが、デバッグには十分である理由について、説得力のある答えを見つけることができません。逆コンパイルを容易にするPython/Javaバイトコードに含まれる余分なものは何ですか?
これにはいくつかの理由があります。
-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の回答に加えて、これも確認してください。
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, ebx
2つの命令のどちらであったかがわからないため、再アセンブルしようとするとmov
分解すると、プログラムが壊れることがあります。
デバッガーを使用して、デバッグ/シンボル情報の有無にかかわらずプログラムをデバッグできます。この情報は、生のアドレスや生のタイプレスデータだけでなく、名前とタイプを使用して多くの(必ずしもすべてではない)ルーチンと変数を識別および表示できるため、人間がコードとデータをナビゲートしやすくするだけです。
さまざまなバイトコードは、あいまいさが少なく、実行できることがより制限されていると思います。これにより、それらの逆コンパイルが容易になります。