本文主要介绍三款Java反编译工具,分别为javap、jad、CFR,三款工具各有优缺点,可以根据需求合理选择。
编程语言分为低级语言和高级语言。其中机器语言和汇编语言属于低级语言,直接用计算机指令编写程序,而C、C++、Java、Python等属于高级语言,用语句编写程序,语句是计算机指令的抽象表示。低级语言是计算机认识的语言、高级语言是程序员认识的语言。
所谓的编译,就是将高级语言翻译成计算机能识别的低级语言的过程,而反编译则是将已经编译好的语言还原成未编译高级语言的过程。
针对于Java语言的编译,是将.java源文件通过javac编译器编译成.class字节码文件。字节码文件并不能由计算机直接识别,需要借助JVM内嵌的解释器解释成机器语言后执行。而Java语言的反编译,指的是将.class字节码文件还原成近似java源代码文件。此处近似是指反编译得到的是常量优化之后的代码。
javap
是JDK
自带工具,可以反编译代码,也可以查看.class
字节码文件。javap
的反编译是非常轻量的,反编译出的不是.java
文件,不利于理解。
public class Test {
public static void main(String[] args) {
List<String> stringList = ImmutableList.of("awe","coder");
stringList.forEach(e -> {
System.out.println(e);
});
}
}
$ javac Test.java // 得到Test.class
对上面得到的字节码文件反编译
$ javap -p Test.class
public class com.ac.Test {
public com.ac.Test();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.String);
}
通过javap -v -p
选项打印出的仍然是字节码文件。字节码中展示的内容是最详细的,从其中可以透彻的分析底层原理。分析字节码文件在很多情况下是非常有必要的,例如分析synchronized
底层原理时。下面由于篇幅,仅展示一小部分字节码。
$ javap -v -p Test.class
.......
private static void lambda$main$0(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return
..........
jad
很容易使用,大家也很熟悉。直接对.class
文件执行,就可以反编译出.java文件。
$ jad Test.class
但是jad
长期不更新,对Java
中的新功能支持很差,例如lambda
表达式。反编译时,经常会报下面的错误。
$ jad Test.class
Parsing Test.class...The class file version is 52.0 (only 45.3, 46.0 and 47.0 are supported)
JavaClassFileParseException: Invalid tag value 0x12
JD-GUI-1.6.3
反编译lambda表达式,会直接把字节码文件反编译成lambda
表达式。分析lambda
表达式底层原理,通过JD-GUI
是行不通的。因此引出了另一款反编译工具–CFR
。
官网链接:http://www.benf.org/other/cfr/
相较于jad
,CFR
是复杂的,需要输入多个参数,但是CFR
可以编译Java9,10,12
中的新功能,甚至可以将其他JVM
语言的class
文件反编译成Java
文件。
我们继续反编译文章开头代码的字节码文件,采用CFR
命令
$ java -jar /opt/cfr/cfr-0.146.jar Test.class --decodelambdas false
反编译后结果如下
package com.ac;
import com.google.common.collect.ImmutableList;
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
ImmutableList stringList = ImmutableList.of((Object)"awe", (Object)"coder");
stringList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}
private static /* synthetic */ void lambda$main$0(String e) {
System.out.println(e);
}
}
通过CFR
反编译出的代码,看上去优雅了很多,同时也能更清晰地看到lambda
语法糖的desugar
过程。
正如小标题所言,CFR
功能强大,不仅可以反编译字节码,还可以反编译jar
包。同时CFR
有着诸多选项,可以通过--help
来查看使用。下面列举一小部分选项。
$ java -jar /opt/cfr/cfr-0.146.jar --help
--decodeenumswitch
(boolean) default: true if class file from version 49.0 (Java 5) or greater
--decodefinally
(boolean) default: true
--decodelambdas
(boolean) default: true if class file from version 52.0 (Java 8) or greater
--decodestringswitch
(boolean) default: true if class file from version 51.0 (Java 7) or greater
如果博文对您有所帮助,欢迎点赞和关注,哈哈。。