7

我正在处理的遗留项目包括一些二进制 jar 文件形式的外部库。我们决定,为了分析和潜在的修补,我们希望接收这个库的源代码,使用它们来构建新的二进制文件,并在经过足够长的详细回归测试后切换到这些二进制文件。

假设我们已经检索并构建了源(我实际上处于计划阶段)。在实际测试之前,我想执行一些“兼容性检查”以排除源代表与“旧”二进制文件中的内容截然不同的可能性。

使用该javap工具,我能够提取用于编译的 JDK 版本(至少我相信它是 JDK 的版本)。它说,二进制文件是使用主要版本 46 和次要版本 0 构建的。根据这篇文章,它映射到 JDK 1.2。

假设将使用相同的 JDK 进行源代码编译。

问题是:如果这两个二进制文件都是从相同的来源构建的,是否有一种可靠且可能有效的验证方法?我想知道所有方法签名和类定义是否相同,以及大多数或所有方法实现是否相同/相似。

该库非常大,所以我认为对反编译的二进制文件进行详细分析可能不是一种选择。

4

4 回答 4

1

我建议一个多阶段的过程:

应用之前建议的 Jardiff 或类似的,看看是否有任何 API 差异。如果可能的话,选择一个可以报告私有方法等选项的工具。在实践中,Java 中的任何实质性实现更改都可能会更改某些方法和类,即使公共 API 未更改。

如果你有一个 API 匹配,用指定的编译器编译几个随机选择的文件,反编译结果和原始类文件,然后比较结果。如果它们匹配,则将相同的过程应用于越来越大的代码体,直到您发现不匹配或检查了所有内容。

与实际的类文件相比,反编译代码的差异更有可能为您提供有关差异性质的线索,并且更容易过滤非显着差异。

如果你得到一个不匹配的,分析它。这可能是由于您不关心的事情。如果是这样,请尝试构建一个脚本来删除这种形式的差异并恢复编译和比较过程。如果您得到广泛的不匹配,请尝试编译器参数,例如优化。如果对编译器参数的调整消除了差异,则继续进行批量比较。此阶段的目标是找到在样本文件上产生匹配的编译器参数和反编译代码过滤器的组合,并将它们应用于库的批量比较。

如果您无法在反编译代码中获得相当接近的匹配,则您可能没有正确的源代码。即便如此,如果您有一个 API 匹配,那么构建您的系统并使用编译结果运行您的测试可能是值得的。如果您的测试至少与您从源代码构建的版本一样好,请继续使用它。

于 2012-11-12T15:30:19.273 回答
0

有各种各样的 JAR 比较工具。曾经相当不错的是Jardiff。我有一段时间没有使用它,但我确信它仍然可用。在同一空间中还有一些商业产品可以满足您的需求。

于 2012-11-12T15:14:38.570 回答
0

Perception 提到的 Jardiff 是一个好的开始,但是理论上没有办法做到 100%。这是因为可以使用不同的编译器以及不同的编译器配置和优化级别来编译相同的源代码。因此,除了类和方法签名之外,没有办法比较二进制代码(字节码)。

方法的“类似实现”是什么意思?让我们假设一个聪明的编译器放弃了一个else案例,因为它发现条件可能永远都不为真。两者相似吗?是的,没有.. :-)

恕我直言,最好的方法是设置非常好的回归测试用例来检查库的每个关键特性。这可能很可怕,但从长远来看可能比寻找错误更便宜。这一切都取决于你在这个项目中的未来计划。这不是一个简单的决定。

于 2012-11-12T15:24:01.247 回答
0

对于方法签名,请使用 jardiff 之类的工具。

为了实现的相似性,您必须回退到一个疯狂的猜测。在操作码级别比较字节码可能依赖于编译器并导致大量误报。如果是这种情况,您可以回退到使用LineNumberTable比较类的方法。

它为您提供每个方法的行号列表(只要类文件已使用调试标志编译,这在非常旧的或商业库中通常会丢失)。

如果两个类文件是从相同的源代码编译的,那么至少每个方法的行号应该完全匹配。

您可以使用 Apache BCEL 等库来检索 LineNumberTable:

  // import org.apache.bcel.classfile.ClassParser;
  JavaClass fooClazz = new ClassParser( "Foo.class" ).parse();
  for( Method m : fooClazz.getMethods() )
  {
     LineNumberTable lnt = m.getLineNumberTable();
     LineNumber[] tab = lnt.getLineNumberTable();
     for( LineNumber ln : tab )
     {
        System.out.println( ln.getLineNumber() );
     }
  }
于 2012-11-12T16:52:03.123 回答