Java和Python字节代码比C / C ++编译器生成的编译机器代码相对容易反编译。
我无法找到令人信服的答案,为什么来自-g选项的信息不足以进行反编译,但足以进行调试? Python / Java字节代码中包含的额外内容是什么,这使得反编译变得容易了?
答案 0 :(得分:8)
以下是一些原因:
答案 1 :(得分:1)
我无法找到令人信服的答案,为什么-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的回答,还要检查this。
答案 2 :(得分:0)
某些处理器(如x86处理器)具有可变长度的指令。如果控制被传递到指令的中间(=第一个字节之后的任何地方),那么它也可以是有效指令(或几个指令)。这使得很难明确地反汇编机器代码。 C / C ++代码可以利用这个功能。
在某些处理器和操作系统上,可以像执行代码一样执行数据,并像使用数据一样使用代码。这使得很难明确地将两者分开。而且,这也是C / C ++程序通常可以轻松完成的事情。
在某些处理器和操作系统上,可以轻松生成代码并执行它,并且可以在运行时修改现有代码。这也会导致反编译代码的模糊性。而且C / C ++程序通常也可以这样做。
编辑:此外,某些CPU对同一指令有多种不同的编码。例如,x86 CPU有2条指令mov reg, reg/mem
和mov reg/mem, reg
。这些允许您在寄存器和存储器位置(在任一方向上)和两个寄存器之间移动数据。这两条指令都可用于在两个寄存器之间传输数据,但它们具有不同的编码。如果程序以某种方式依赖于特定的编码(例如,为了通过校验和验证其完整性),则从mov eax, ebx
这样的反汇编中,您将无法分辨两个mov
指令中的哪一个它最初是,所以如果你试图重新组装反汇编,你可能会破坏程序。
您可以使用调试器调试带或不带调试/符号信息的程序。此信息仅使人类更容易导航代码和数据,因为可以使用其名称和类型识别和显示许多(但不一定是所有)例程和变量,而不仅仅是原始地址和原始无类型数据。
我猜测各种字节码不那么模糊,而且它们可以做的事情受到更多限制,而这就更容易对它们进行反编译。