java class常用反编译操作

1. Jadx

jadx是个反编译神器,集apktool、jd功能于一身,能直接查看apk,dex,jar文件。目前还不支持查看aar文件。
如果要查看aar文件的话,可以先重命名为zip文件,再解压查看。

下面介绍下如何使用jadx将编译后的class文件转换为java源码文件:

  1. 把class文件所属的jar包通过jadx方式打开

     

    java class常用反编译操作_第1张图片

     

  2. 点击菜单中的File -->Save all(或者使用快捷键Ctrl+S)

     

    java class常用反编译操作_第2张图片

     

  3. 输入保存路径,点击Select

     

    java class常用反编译操作_第3张图片

     

4.查看生成的java文件

 

java class常用反编译操作_第4张图片

 

附:jadx下载地址

2. Fernflower

fernflowser也是一个开源的反编译工具项目,Android Studio内置的反编译工具就是它。

 

java class常用反编译操作_第5张图片

fernflower没有图形界面,主要是通过命令行的方式进行操作。

下面介绍下如何使用fernflower将编译后的class文件转换为java源码文件:

  1. 下载fernflower的jar包文件
    http://files.minecraftforge.net/maven/net/minecraftforge/fernflower/

  2. 将需要反编译的jar包文件用压缩工具解压

     

    java class常用反编译操作_第6张图片

     

  3. 通过命令行输出反编译输出java文件

java -jar fernflower.jar -dgs=1 svg2vector-applet-1.0.0 .

java class常用反编译操作_第7张图片

 

fernflower其他具体使用方式可参考: https://github.com/fesh0r/fernflower

3、jad工具

命令: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

- directory for output files
-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 - split strings into pieces of max chars (splitstr)
-lnc - output original line numbers as comments (lnc)
-lradix- display long integers using the specified radix
-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 - prefix for all packages in generated source files
-pc - prefix for classes with numerical names (default: _cls)
-pe - prefix for unused exception names (default: _ex)
-pf - prefix for fields with numerical names (default: _fld)
-pi - pack imports into one line using .* (packimports)
-pl - prefix for locals with numerical names (default: _lcl)
-pm - prefix for methods with numerical names (default: _mth)
-pp - prefix for method parms with numerical names (default:_prm)
-pv - pack fields with the same types into one line (packfields)
-r - restore package directory structure
-radix- display integers using the specified radix (8, 10, or 16)
-s - output file extension (default: .jad)
-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 - use spaces for indentation (default: 4)
-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

4、在线反编译网 http://javare.cn/

5、JDK自带的反编译工具 javap

javap是JDK自带的反汇编器,可以查看java编译器为我们生成的字节码。通过它,我们可以对照源代码和字节码,从而了解很多编译器内部的工作。
语法:

 java class常用反编译操作_第8张图片

-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
}

6、借助idea、eclipse工具

你可能感兴趣的:(逆向工程)