Jar 包反编译工具: JD-GUI 与 fernflower

标签: Java 发布于:2024-04-06 23:03:06 编辑于:2024-04-06 23:03:06 浏览量:45

Jar 包反编译工具: JD-GUI 与 fernflower

当需要处理无源代码的久远 jar 包,获取其 java 源码时需要反编译工具的支持。

本文介绍两个工具:JD-GUI 和 IDEA 自带的 java-decompiler.

以反编译 logback-core-1.1.11.jar 为例,jar 包位于 ~/jd 目录。

JD-GUI

打开 jar 包后 Save All Sources 即可,会生成一个 src.zip 结尾的压缩包。

解压 logback-core-1.1.11.jar.src.zip 可得到所有源码。以 ConsoleAppender.java 部分代码为例,生成的源码格式比较乱,左侧有一排注释,最后会生成反编译说明。

fernflower

该组件是 IDEA 采用的反编译工具,在 IDEA 打开 class 文件时,就是通过该组件的反编译能力。

java-decompiler 是 IDEA 中的插件名称,实际上来源于 fernflower 工具。

在 Mac 下,java-decompiler.jar 位于 /Applications/idea.app/Contents/plugins/java-decompiler/lib,执行以下命令:

  1. cd /Applications/idea.app/Contents/plugins/java-decompiler/lib;

  2. java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler ~/jd/logback-core-1.1.11.jar ~/jd/src/

命令格式为:java -jar java-decompiler.jar [-<option>=<value>]* [<source>]+ <destination>

  • source 表示 jar 包所在目录,可以填写单个 jar 包,也可以填写一个目录(将解压目录下所有 jar 包)
  • destination 表示反编译的 java 源码生成目录

执行命令后,将在 ~/jd/src 下生成 logback-core-1.1.11.jar 文件,这个 jar 包就是源文件,解压该 jar 包即可。

  1. unzip ~/jd/src/logback-core-1.1.11.jar;

反编译后效果如下,格式非常友好。

小结

日常查看 jar 包可用 JD-GUI,反编译 jar 包时推荐使用 fernflower,格式友好、正确率高,而 JD-GUI 格式混乱,同时报错特别多。

报错

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

这个错误告诉我们,我们的类是在比我们试图运行它的版本更高的 Java 版本下编译的。更具体地说,在本例中,我们使用 Java11 编译了类,并试图使用 Java8 运行它。

让我们看看主要版本号是如何映射到 Java 版本的:

  • 45 = Java 1.1
  • 46 = Java 1.2
  • 47 = Java 1.3
  • 48 = Java 1.4
  • 49 = Java 5
  • 50 = Java 6
  • 51 = Java 7
  • 52 = Java 8
  • 53 = Java 9
  • 54 = Java 10
  • 55 = Java 11
  • 56 = Java 12
  • 57 = Java 13
  • 58 = Java 14
  • 59 = Java 15
  • 60 = Java 16
  • 61 = Java 17
  • 62 = Java 18
  • 63 = Java 19
  • 64 = Java 20
  • 65 = Java 21
  • 66 = Java 22

现在让我们讨论如何在从命令行运行 Java 时解决这个错误。

根据我们的情况,有两种方法可以解决这个错误:

  1. 为较早版本的 Java 编译代码
  2. 在较新的 Java 版本上运行代码

最后的决定取决于我们的情况。如果我们需要使用已经在更高级别编译的第三方库,我们最好的选择可能是使用较新的 Java 版本运行应用程序。如果我们打包一个应用程序以便分发,那么最好将其编译成较旧的版本。

使用 docker 镜像解决

# docker run -ti openjdk:11 bash

root@9c69ff87ae38:/tmp# ls -l
total 96208
-rw-r--r-- 1 root root 97045625 Sep 22  2023 admin-0.1-SNAPSHOT.jar
drwxr-xr-x 2 root root        6 Aug  2  2022 hsperfdata_root
-rw-r--r-- 1 root root  1469568 Apr  5 02:44 java-decompiler.jar

root@9c69ff87ae38:/tmp# java -version
openjdk version "11.0.16" 2022-07-19
OpenJDK Runtime Environment 18.9 (build 11.0.16+8)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.16+8, mixed mode, sharing)

root@9c69ff87ae38:/tmp# mkdir src

root@9c69ff87ae38:/tmp# java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler ./admin-0.1-SNAPSHOT.jar  ./src/
INFO:  Decompiling class org/springframework/boot/loader/ClassPathIndexFile
INFO:  ... done