【问题标题】:Is there any Java Decompiler that can correctly decompile calls to overloaded methods? [closed]是否有任何 Java 反编译器可以正确反编译对重载方法的调用? [关闭]
【发布时间】:2011-02-19 21:02:21
【问题描述】:

考虑这个(恕我直言简单)示例:

public class DecompilerTest {
    public static void main(String[] args) {
        Object s1 = "The", s2 = "answer";
        doPrint((Object) "You should know:");
        for (int i = 0; i < 2; i++) {
            doPrint(s1);
            doPrint(s2);
            s1 = "is";
            s2 = new Integer(42);
        }
        System.out.println();
    }

    private static void doPrint(String s1) {
        System.out.print("Wrong!");
    }

    private static void doPrint(Object s1) {
        System.out.print(s1 + " ");
    }
}

在没有调试信息的情况下使用源/目标级别 1.1 编译它(即不应该存在局部变量信息)并尝试反编译它。我尝试了 Jad、JD-GUI 和 Fernflower,它们都至少有一个调用错误(即程序至少打印了一次“错误!”)

真的没有 java 反编译器可以推断出正确的强制转换,从而不会调用错误的重载吗?

编辑: 目标级别 1.1,因此不存在 Java6 特定的快速验证信息。这可能会给反编译器一个线索,即 s1 已被声明为 Object 而不是 String。即使没有这些信息,反编译器也应该能够反编译代码(不一定获得原始变量类型,但表现出相同的行为),特别是因为许多混淆器也会剥离它。

哪些反编译器出错了:

  • 他们在第一次通话中错过了(Object) 的演员阵容。
  • 他们推断 s1 的类型为 String,但忘记在对 doPrint 的调用中添加强制转换(因此调用的是字符串版本而不是对象版本)。
  • 一个蹩脚的(我什至没有列出)甚至将s2的类型推断为字符串,导致代码无法编译。

无论如何,这段代码从不调用String 重载,但反编译的代码却调用了。

【问题讨论】:

  • “他们接错电话”是什么意思?反编译后生成什么源码?

标签: java decompiler jad


【解决方案1】:

你好,

抱歉回复晚了。我正在从http://www.reversed-java.com/fernflower/forum?threadfolder=2_DE复制我的答案

您的问题实际上是一个众所周知的问题。让我们看看:

1) 纯字节码不包含任何关于对象变量类型的信息,因此在第一遍中,s1 和 s2 被声明为 Object。

2) 反编译器正在努力为每个变量分配可能的最佳类型(= Fernflower 中实现的“最窄类型原则”)。所以 s1 和 s2 被正确识别为 String 的实例。

3) 调用 doPrint 为我们提供了指向正确方法的直接链接
私有静态无效doPrint(对象s1)

4) 到目前为止一切正常,对吧?现在我们将一个字符串变量 s1 传递给一个函数,该函数需要一个对象。我们需要铸造它吗?你会认为不是这样,因为 Object 是 String 的超类型。然而我们这样做了——因为在同一个类中有另一个函数具有相同的名称和不同的参数签名。所以我们需要分析整个类来找出是否需要演员表。

5) 一般来说,这意味着我们需要分析所有库中的所有引用类,包括java运行时。巨大的工作量!事实上,这个特性在 Fernflower 的一些 alpha 版本中实现了,但由于性能和内存损失,还没有在生产中实现。其他提到的反编译器在设计上缺乏这种能力。

希望我已经澄清了一点:)

【讨论】:

  • 是的,我知道一般问题(这就是为什么我不难将更长的课程减少到上面的示例)。但是,据我了解,如果您没有关于目标类的类信息,您总是可以将(多余的)强制转换添加到参数的确切类型,不是吗?以后您仍然可以使用 Eclipse 的快速修复来删除所有多余的强制转换。 (即反编译代码时我更喜欢正确性而不是美观)
  • 你说得对,可以做到。唯一的缺点是我们使用所有这些不必要的强制转换生成了一些非常丑陋的代码。我将为此行为实施一个选项,谢谢您的建议。
  • "纯字节码不包含任何关于对象变量类型的信息"
  • “纯字节码不包含有关对象变量类型的任何信息” - 错误。类型派生自类型化数据源(参数、字段)以及操作和方法调用(所有这些都是类型化的)。堆栈映射属性也提供给方法代码。此外,字节码验证器可以防止加载带有错误类型局部变量的类文件。
【解决方案2】:

Krakatau 正确处理所有重载方法,甚至是在原始类型上重载的方法,大多数反编译器都会出错。它总是将参数转换为被调用方法的确切类型,因此代码可能比必要的更混乱,但至少它是正确的。

披露:我是 Krakatau 的作者。

【讨论】:

    【解决方案3】:

    用于反编译的 JadClipse Eclipse 插件还提供了 JODE 反编译器,您可能想尝试一下。 Jad 放弃时我会使用它。

    Dava 反编译器还使用 Soot,我上次查看时,它在重构原始 Java 代码方面非常雄心勃勃。我没有尝试过你的例子,但你可能想看看。 http://www.sable.mcgill.ca/dava/

    【讨论】:

    • Dava 和其他人一样失败了。 Jode 工作,但由于 Jode 几乎在所有通过任何混淆器的程序上都会崩溃,这对我没有帮助:(猜我问错了问题。;)
    • 否决接受答案?那是新的:)
    • 如果您不喜欢被否决和接受,则更改了我接受的答案:)(我首先对您投了反对票,因为您的建议也不起作用,但后来决定从所有被否决的答案中您是最好的) - 顺便说一句,你从哪里看出是我投了反对票和接受了?
    【解决方案4】:

    Procyon 应该正确处理重载的方法调用。与 Krakatau 一样,Procyon 最初为每个与目标方法不完全匹配的方法参数插入强制转换。但是,其中大部分将在识别和消除冗余强制转换的反编译的后期阶段被删除。如果 Procyon 可以验证这样做不会导致调用绑定到不同的方法,则 Procyon 只会删除调用参数上的强制转换。例如,如果声明方法的.class 无法解析,则它根本不会尝试删除强制转换,因为它无法知道哪些重载可能会发生冲突。

    【讨论】:

      【解决方案5】:

      添加到以前的答案: 以下是截至 2015 年 3 月的现代反编译器列表:

      • 南河谷
      • CFR
      • 法学博士
      • 蕨类植物

      它们都支持重载方法。

      您可以在线测试上述反编译器,无需安装,并做出自己的明智选择。 云中的 Java 反编译器:http://www.javadecompilers.com/

      【讨论】:

      • 不确定 JD 和 JDCore 是否相同,但记录一下:来自该网站的 JDCore 无法反编译来自该问题的示例
      • JD 和 JDCore 是一样的。很糟糕,它失败了。希望其他反编译器成功吗?
      猜你喜欢
      • 2014-08-28
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多