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. 与 C++ 相比,Python 和 Java 是相对简单的语言(这一点不适用于 C)。
  6. C++ 模板对质量反编译提出了许多挑战(这一点也不适用于 C)。
  7. C/C++ 预处理器。
  8. 在 Python 中,源文件和字节码文件之间存在一对一的关系。在 Java 中,关系是一个或多个字节码文件的一个来源。在 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++ 代码可以利用此功能。

在某些处理器和操作系统上,可以像执行代码一样执行数据,并像使用数据一样使用代码。这使得很难明确地将两者分开。而且,这也是 C/C++ 程序通常可以轻松完成的事情。

在某些处理器和操作系统上,动态生成代码并执行它很容易,并且可以在运行时修改现有代码。这也导致了反编译代码的歧义。C/C++ 程序通常也可以做到这一点。

编辑:另外,一些 CPU 对同一条指令有多种不同的编码。例如,x86 CPU 有 2 条指令mov reg, reg/memmov reg/mem, reg. 这些使您可以在寄存器和内存位置(在任一方向)以及两个寄存器之间移动数据。这两条指令都可用于在两个寄存器之间传输数据,但它们具有不同的编码。如果程序以某种方式依赖于特定的编码(例如,为了通过校验和验证其完整性),那么从反汇编中mov eax, ebx您将无法分辨mov它最初是两条指令中的哪一条,因此如果您尝试重新组装反汇编,你可能会破坏程序。

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

我猜想各种字节码不那么模棱两可,而且它们的功能更受限制,这就是让反编译更容易的原因。

于 2013-03-25T07:26:33.627 回答