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

Java 用调试信息反编译C代码?,java,c++,c,reverse-engineering,decompiling,Java,C++,C,Reverse Engineering,Decompiling,Java和Python字节码比C/C++编译器生成的已编译机器码更容易反编译 对于-g选项中的信息不足以反编译,但足以进行调试的原因,我无法找到令人信服的答案? Python/Java字节码中包含哪些额外的东西使反编译变得容易?以下是一些原因: Java和Python字节码相对简单且高级,而某些CPU(比如x86)的指令集极其复杂 字节码紧密地模仿了为其设计的语言的结构 在生成字节码时,Java和Python执行的优化很少。这导致字节码与原始源代码的结构紧密对应。一个好的优化C或C++编译器能够

Java和Python字节码比C/C++编译器生成的已编译机器码更容易反编译

对于-g选项中的信息不足以反编译,但足以进行调试的原因,我无法找到令人信服的答案?
Python/Java字节码中包含哪些额外的东西使反编译变得容易?

以下是一些原因:

  • Java和Python字节码相对简单且高级,而某些CPU(比如x86)的指令集极其复杂
  • 字节码紧密地模仿了为其设计的语言的结构
  • 在生成字节码时,Java和Python执行的优化很少。这导致字节码与原始源代码的结构紧密对应。一个好的优化C或C++编译器能够产生与原始源代码有很大差别的汇编。 <> LI>很少有java和python编译器,还有很多C++和C++编译器。如果您的目标是单个已知编译器(或一小部分已知编译器),那么生成高质量的反编译器会更容易
  • Python和java是C++语言中比较简单的语言(这一点不适用于C)。
  • C++模板对高质量的反编译提出了许多挑战(这一点也不适用于C)
  • C/C++预处理器
  • 在Python中,源文件和字节码文件之间存在一对一的关系。在Java中,关系是一个源到一个或多个字节码文件。在C和C++中,关系是多对多的,在源前端有很多重叠(思考头)。
    有些处理器,如x86处理器,具有可变长度的指令。如果将控件传递到指令的中间(=第一个字节后的任何位置),则该指令也可以是有效的指令(或多条指令)。这使得很难明确地反汇编机器代码。C/C++代码可以利用此功能

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

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

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

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

    我猜各种字节码都不那么模棱两可,而且在它们的功能上更受限制,这使得反编译这些字节码更容易

    对于-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的答案检查之外。

    这个答案最适合堆栈交换的area51反向工程networks@Koushik:该网站已经有五天的历史了,似乎正在进行私人测试。@NPE哦,完全忘记了。。希望公共测试从soonHi NPE开始,谢谢你的回答:)这些观点完全有道理。但是,你知道有哪种C/C++反编译器能够令人满意地反编译-O0-g编译代码吗?我要补充的是-g只提供标签名,而没有它,二进制文件只包含绝对地址和偏移量。这有助于阅读反编译的程序集,但不会让您回到C.@C.fogelklou:“反编译”程序集是什么?那不是C吗?我是说一个转换成文本的二进制文件。。。反编译可能不是正确的词,但最终结果是以文本可读格式包含的程序集代码。@Saswattpadhi“decompiled”程序集不必是c。它可以是c++/D/B/fortran等。也就是说,它取决于。Hi Alexey,