Java 反编译
对 Java 的反编译工具做出介绍,包含:javap、cfr。
1. javap
javap 是 JDK 自带的反编译工具。
PS F:\> javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
其中常用的选项有:-p 和 -c
下面使用 -p 参数查看枚举类的所有类和成员:
PS F:\> javap -p .\TestEnum.class
Compiled from "TestEnum.java"
public final class test.TestEnum extends java.lang.Enum<test.TestEnum> {
public static final test.TestEnum A;
public static final test.TestEnum B;
private static final test.TestEnum[] $VALUES;
public static test.TestEnum[] values();
public static test.TestEnum valueOf(java.lang.String);
private test.TestEnum();
static {};
}
可以出 TestEnum 类中的所有变量与方法,但是想要查看其实现细节,-c 参数是一个选择。
PS F:\> javap -c .\TestEnum.class
Compiled from "TestEnum.java"
public final class test.TestEnum extends java.lang.Enum<test.TestEnum> {
public static final test.TestEnum A;
public static final test.TestEnum B;
public static test.TestEnum[] values();
Code:
0: getstatic #1 // Field $VALUES:[Ltest/TestEnum;
3: invokevirtual #2 // Method "[Ltest/TestEnum;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Ltest/TestEnum;"
9: areturn
public static test.TestEnum valueOf(java.lang.String);
Code:
0: ldc #4 // class test/TestEnum
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class test/TestEnum
9: areturn
static {};
Code:
0: new #4 // class test/TestEnum
3: dup
4: ldc #7 // String A
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field A:Ltest/TestEnum;
13: new #4 // class test/TestEnum
16: dup
17: ldc #10 // String B
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field B:Ltest/TestEnum;
26: iconst_2
27: anewarray #4 // class test/TestEnum
30: dup
31: iconst_0
32: getstatic #9 // Field A:Ltest/TestEnum;
35: aastore
36: dup
37: iconst_1
38: getstatic #11 // Field B:Ltest/TestEnum;
41: aastore
42: putstatic #1 // Field $VALUES:[Ltest/TestEnum;
45: return
}
-c 参数正如描述中所说,对 TestEnum.class 代码进行反汇编,生成了我们能看懂的字节码。
javap 使用的机会不多,一般只有在真的需要看字节码的时候才会用到。但是字节码中间暴露的东西是最全的。比如在分析synchronized的原理的时候就有是用到javap。通过javap生成的字节码,我发现synchronized底层依赖了ACC_SYNCHRONIZED标记和monitorenter、monitorexit两个指令来实现同步。
2. crf
CFR 将反编译现代 Java 特性,支持 Java 9、12 和 14 的大部分。它完全是用 Java 6 编写的,所以在任何地方都可以工作!
使用 --help 参数查看可使用的参数,及其相关注释
PS F:\PrivateData\Notes\on_java_8\out\production\on_java_8\test> java -jar .\cfr-0.151.jar --help
CFR 0.151
--aexagg (boolean)
--aexagg2 (boolean)
--aggressivedocopy (int >= 0) default: 0
--aggressivedoextension (boolean)
--aggressiveduff (boolean)
--aggressivesizethreshold (int >= 0) default: 15000
--allowcorrecting (boolean) default: true
--allowmalformedswitch (boolean)
--analyseas (One of [DETECT, JAR, WAR, CLASS])
--antiobf (boolean) default: false
--arrayiter (boolean) default: true if class file from version 49.0 (Java 5) or greater
--caseinsensitivefs (boolean) default: true
--clobber (boolean)
--collectioniter (boolean) default: true if class file from version 49.0 (Java 5) or greater
--commentmonitors (boolean) default: false
--comments (boolean) default: true
--constobf (boolean) default: Value of option 'antiobf'
--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
--dumpclasspath (boolean) default: false
--eclipse (boolean) default: true
--elidescala (boolean) default: false
--extraclasspath (string)
--forbidanonymousclasses (boolean) default: false
--forbidmethodscopedclasses (boolean) default: false
--forceclassfilever (string, specifying either java version as 'j6', 'j1.0', or classfile as '56', '56.65535')
--forcecondpropagate (boolean)
--forceexceptionprune (boolean)
--forcereturningifs (boolean)
--forcetopsort (boolean)
--forcetopsortaggress (boolean)
--forcetopsortnopull (boolean)
--forloopaggcapture (boolean)
--hidebridgemethods (boolean) default: Value of option 'obfattr'
--hidelangimports (boolean) default: true
--hidelongstrings (boolean) default: false
--hideutf (boolean) default: true
--ignoreexceptions (boolean) default: false
--ignoreexceptionsalways (boolean) default: false
--importfilter (string)
--innerclasses (boolean) default: true
--instanceofpattern (boolean) default: true if class file from version 58.0 (Java 14) or greater, or experimental in 58.0 (Java 14)
--j14classobj (boolean) default: false if class file from version 49.0 (Java 5) or greater
--jarfilter (string)
--labelledblocks (boolean) default: true
--lenient (boolean) default: false
--liftconstructorinit (boolean) default: true
--lomem (boolean) default: false
--methodname (string)
--obfattr (boolean) default: Value of option 'antiobf'
--obfcontrol (boolean) default: Value of option 'antiobf'
--obfuscationpath (string)
--outputdir (string)
--outputpath (string)
--override (boolean) default: true if class file from version 50.0 (Java 6) or greater
--previewfeatures (boolean) default: true
--pullcodecase (boolean) default: false
--recordtypes (boolean) default: true if class file from version 58.0 (Java 14) or greater, or experimental in 58.0 (Java 14)
--recover (boolean) default: true
--recovertypeclash (boolean)
--recovertypehints (boolean)
--reducecondscope (boolean)
--relinkconststring (boolean) default: true
--removebadgenerics (boolean) default: true
--removeboilerplate (boolean) default: true
--removedeadconditionals (boolean)
--removedeadmethods (boolean) default: true
--removeinnerclasssynthetics (boolean) default: true
--rename (boolean) default: false
--renamedupmembers (boolean) default: Value of option 'rename'
--renameenumidents (boolean) default: Value of option 'rename'
--renameillegalidents (boolean) default: Value of option 'rename'
--renamesmallmembers (int >= 0) default: 0
--showinferrable (boolean) default: false if class file from version 51.0 (Java 7) or greater
--showversion (boolean) default: true
--silent (boolean) default: false
--skipbatchinnerclasses (boolean) default: true
--staticinitreturn (boolean) default: true
--stringbuffer (boolean) default: false if class file from version 49.0 (Java 5) or greater
--stringbuilder (boolean) default: true if class file from version 49.0 (Java 5) or greater
--stringconcat (boolean) default: true if class file from version 53.0 (Java 9) or greater
--sugarasserts (boolean) default: true
--sugarboxing (boolean) default: true
--sugarenums (boolean) default: true if class file from version 49.0 (Java 5) or greater
--switchexpression (boolean) default: true if class file from version 57.0 (Java 13) or greater, or experimental in 56.0 (Java 12)
--tidymonitors (boolean) default: true
--trackbytecodeloc (boolean) default: false
--tryresources (boolean) default: true if class file from version 51.0 (Java 7) or greater
--usenametable (boolean) default: true
--usesignatures (boolean) default: true
--help (string)
Please specify '--help optionname' for specifics, eg
--help pullcodecase
常用的参数有:
1)--decodeenumswitch:对于 switch 支持 String 的细节进行解码。
2)--decodestringswitch:对于 switch 支持 enum 的细节进行解码。
3)--decodelambdas:对 Lambda 表达式细节进行反编译。
4)--sugarenums:对 enum 语法糖细节进行节码。
比如:可以使用 --sugarenums 参数查看 Enum 的语法糖,对 TestEnum.class 的细节进行查看。
PS F:\PrivateData\Notes\on_java_8\out\production\on_java_8\test> java -jar .\cfr-0.151.jar .\TestEnum.class --sugarenums false
/*
* Decompiled with CFR 0.151.
*/
package test;
public final class TestEnum
extends Enum<TestEnum> {
public static final /* enum */ TestEnum A = new TestEnum("A", 0);
public static final /* enum */ TestEnum B = new TestEnum("B", 1);
private static final /* synthetic */ TestEnum[] $VALUES;
public static TestEnum[] values() {
return (TestEnum[])$VALUES.clone();
}
public static TestEnum valueOf(String name) {
return Enum.valueOf(TestEnum.class, name);
}
private TestEnum(String string, int n) {
super(string, n);
}
static {
$VALUES = new TestEnum[]{A, B};
}
}
参考资料
- CFR 参考地址:www.benf.org/other/cfr/
- HollisChuang's Blog:www.hollischuang.com/archives/58