jadx是个反编译神器,集apktool、jd功能于一身,能直接查看apk,dex,jar文件。目前还不支持查看aar文件。
如果要查看aar文件的话,可以先重命名为zip文件,再解压查看。
下面介绍下如何使用jadx将编译后的class文件转换为java源码文件:
4.查看生成的java文件
附:jadx下载地址
fernflowser也是一个开源的反编译工具项目,Android Studio内置的反编译工具就是它。
fernflower没有图形界面,主要是通过命令行的方式进行操作。
下面介绍下如何使用fernflower将编译后的class文件转换为java源码文件:
下载fernflower的jar包文件
http://files.minecraftforge.net/maven/net/minecraftforge/fernflower/
将需要反编译的jar包文件用压缩工具解压
通过命令行输出反编译输出java文件
java -jar fernflower.jar -dgs=1 svg2vector-applet-1.0.0 .
fernflower其他具体使用方式可参考: https://github.com/fesh0r/fernflower
命令:jad -o -r -s java -d src classes/**/*.class
就能在根目录下发现是src的文件夹,里面都是反编译后的java源文件。
jad命令的参数含义如下:
-o:覆盖旧文件,而且不用提示确认。
-r:重新加载生成包结构。
-s (java):定义输出文件的扩展名。jad为默认扩展名,我们反编译后当然是要.java源文件了。
-d:输出文件的目录。src表示反编译后的所有文件都放在src目录下。
classes/**/*.class:classes是需要反编译的文件夹的名字,整个表示classes目录下的所有class文件。你也可以写成这样**/*.class,这表示当前目录及其子目录下所有的class文件(包含所有的子目录)。
.jad -o -8 -r -dXXXXX -sjava XXXXX
-o: 覆盖写,如果文件已经存在,则覆盖
-r: 建立和java包一致的文件夹路径
-dXXXX: 反编译后保存路径,如 D:/output/src
-sjava: 反编译后的文件后缀名,我们希望是.java文件
-8: 避免反编译时将中文编译为unicode,必不可少。
注意:路径中不要有中文,否则会出现错误,JavaClassFileReadException: can't open input file on `D:\XXXXXX'
注意:会丢失注解 ,比如@XmlAttribute,@Autowired等等,全部缺失。表示很无赖
补充:
使用方法:
[1] 反编译一个class文件:jad example.class,会生成example.jad,用文本编辑器打开就是java源代码
[2] 指定生成源代码的后缀名:jad -sjava example.class,生成example.java
[3] 改变生成的源代码的名称,可以先使用-p将反编译后的源代码输出到控制台窗口,然后使用重定向,输出到文件:jad -p example.class > myexample.java
[4] 把源代码文件输出到指定的目录:jad -dnewdir -sjava example.class,在newdir目录下生成example.java
[5] 把packages目录下的class文件全部反编译:jad -sjava packages/*.class
[6] 把packages目录以及子目录下的文件全部反编译:jad -sjava packages/**/*.class,不过你仍然会发现所有的源代码文件被放到了同一个文件中,没有按照class文件的包路径建立起路径
[7] 把packages目录以及子目录下的文件全部反编译并建立和java包一致的文件夹路径,可以使用-r命令:jad -r -sjava packages/**/*.class
[8] 当重复使用命令反编译时,Jad会提示“whether you want to overwrite it or not”,使用-o可以强制覆盖旧文件
最权威的还是help:
bin>jad
Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov ([email protected]).
Usage: jad [option(s)]
Options: -a - generate JVM instructions as comments (annotate)
-af - output fully qualified names when annotating
-b - generate redundant braces (braces)
-clear - clear all prefixes, including the default ones
-d
-dead - try to decompile dead parts of code (if there are any)
-dis - disassembler only (disassembler)
-f - generate fully qualified names (fullnames)
-ff - output fields before methods (fieldsfirst)
-i - print default initializers for fields (definits)
-l
-lnc - output original line numbers as comments (lnc)
-lradix
-nl - split strings on newline characters (splitstr)
-noconv - don't convert Java identifiers into valid ones (noconv)
-nocast - don't generate auxiliary casts
-noclass - don't convert .class operators
-nocode - don't generate the source code for methods
-noctor - suppress the empty constructors
-nodos - turn off check for class files written in DOS mode
-nofd - don't disambiguate fields with the same names (nofldis)
-noinner - turn off the support of inner classes
-nolvt - ignore Local Variable Table entries (nolvt)
-nonlb - don't insert a newline before opening brace (nonlb)
-o - overwrite output files without confirmation
-p - send all output to STDOUT (for piping)
-pa
-pc
-pe
-pf
-pi
-pl
-pm
-pp
-pv
-r - restore package directory structure
-radix
-s
-safe - generate additional casts to disambiguate methods/fields
-space - output space between keyword (if, while, etc) and expression
-stat - show the total number of processed classes/methods/fields
-t
-t - use tabs instead of spaces for indentation
-v - show method names while decompiling
-8 - convert Unicode strings into ANSI strings (ansi)
-& - redirect STDERR to STDOUT
javap是JDK自带的反汇编器,可以查看java编译器为我们生成的字节码。通过它,我们可以对照源代码和字节码,从而了解很多编译器内部的工作。
语法:
-help --help -? 输出此用法消息
-version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类 和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置
下面举一个小例子,java源代码如下:
public class JavapTest2 {
private String username;
public void say(String username) {
System.out.println("hi,"+username);
}
}
将其编译后,使用 javap来查询 JavapTest2的字节码
javac JavapTest2.java
javap -p -v JavapTest2
生成的字节码如下:
Classfile ../JavapTest2.class
Last modified 2018-8-31; size 608 bytes
MD5 checksum 25f04ad8674616cb2f0e7fe9d35e6ab1
Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#21 // java/lang/Object."":()V
#2 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Class #24 // java/lang/StringBuilder
#4 = Methodref #3.#21 // java/lang/StringBuilder."":()V
#5 = String #25 // hi,
#6 = Methodref #3.#26 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/String
Builder;
#7 = Methodref #3.#27 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#9 = Class #30 // com/pjmike/JVM/JavapTest2
#10 = Class #31 // java/lang/Object
#11 = Utf8 username
#12 = Utf8 Ljava/lang/String;
#13 = Utf8
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 say
#18 = Utf8 (Ljava/lang/String;)V
#19 = Utf8 SourceFile
#20 = Utf8 JavapTest2.java
#21 = NameAndType #13:#14 // "":()V
#22 = Class #32 // java/lang/System
#23 = NameAndType #33:#34 // out:Ljava/io/PrintStream;
#24 = Utf8 java/lang/StringBuilder
#25 = Utf8 hi,
#26 = NameAndType #35:#36 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#27 = NameAndType #37:#38 // toString:()Ljava/lang/String;
#28 = Class #39 // java/io/PrintStream
#29 = NameAndType #40:#18 // println:(Ljava/lang/String;)V
#30 = Utf8 com/pjmike/JVM/JavapTest2
#31 = Utf8 java/lang/Object
#32 = Utf8 java/lang/System
#33 = Utf8 out
#34 = Utf8 Ljava/io/PrintStream;
#35 = Utf8 append
#36 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#37 = Utf8 toString
#38 = Utf8 ()Ljava/lang/String;
#39 = Utf8 java/io/PrintStream
#40 = Utf8 println
{
private java.lang.String username;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE
public com.pjmike.JVM.JavapTest2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 7: 0
public void say(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."":()V
10: ldc #5 // String hi,
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/
lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/
lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
LineNumberTable:
line 11: 0
line 12: 25
}
SourceFile: "JavapTest2.java"
默认情况下 javap 会打印所有非私有的字段和方法,如下:
javap JavapTest2
Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2 {
public com.pjmike.JVM.JavapTest2();
public void say(java.lang.String);
}
javap -v -p JavapTest2
。
加了 -p 选项后,还会打印私有的字段和方法,加上 -v 选项后,它会尽可能地打印出所有信息,如果只需要查询相关方法对应的字节码,可以使用 -c 代替 -v,代码如下:
Compiled from "JavapTest2.java"
public class com.pjmike.JVM.JavapTest2 {
private java.lang.String username;
public com.pjmike.JVM.JavapTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public void say(java.lang.String);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."":()V
10: ldc #5 // String hi,
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la
ng/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}