用调试信息反编译C代码?

时间:2013-03-25 07:06:44

标签: java c++ c reverse-engineering decompiling

Java和Python字节代码比C / C ++编译器生成的编译机器代码相对容易反编译。

我无法找到令人信服的答案,为什么来自-g选项的信息不足以进行反编译,但足以进行调试? Python / Java字节代码中包含的额外内容是什么,这使得反编译变得容易了?

3 个答案:

答案 0 :(得分:8)

以下是一些原因:

  1. Java和Python字节码相对简单且高级,而某些CPU的指令集(想想x86)则非常复杂。
  2. 字节码非常类似于他们设计的语言结构。
  3. 在生成字节码时,Java和Python的执行很少通过优化。这导致字节码与原始源代码的结构紧密对应。优秀的C或C ++编译器能够生成远离原始源代码的程序集。
  4. 很少有Java和Python编译器,以及许多C和C ++编译器。如果您要定位一个已知的编译器(或一小组已知编译器),那么生成高质量的反编译器会更容易。
  5. 与C ++相比,Python和Java是相对简单的语言(这一点并不适用于C)。
  6. C ++模板对质量反编译提出了许多挑战(这一点也不适用于C)。
  7. C / C ++预处理器。
  8. 在Python中,源文件和字节码文件之间存在一对一的关系。在Java中,关系是一个或多个字节码文件的一个源。在C和C ++中,关系是多对多的,在源头上有很多重叠(想想标题)。

答案 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/memmov reg/mem, reg。这些允许您在寄存器和存储器位置(在任一方向上)和两个寄存器之间移动数据。这两条指令都可用于在两个寄存器之间传输数据,但它们具有不同的编码。如果程序以某种方式依赖于特定的编码(例如,为了通过校验和验证其完整性),则从mov eax, ebx这样的反汇编中,您将无法分辨两个mov指令中的哪一个它最初是,所以如果你试图重新组装反汇编,你可能会破坏程序。

您可以使用调试器调试带或不带调试/符号信息的程序。此信息仅使人类更容易导航代码和数据,因为可以使用其名称和类型识别和显示许多(但不一定是所有)例程和变量,而不仅仅是原始地址和原始无类型数据。

我猜测各种字节码不那么模糊,而且它们可以做的事情受到更多限制,而这就更容易对它们进行反编译。