Java反编译工具

本文主要介绍三款Java反编译工具,分别为javap、jad、CFR,三款工具各有优缺点,可以根据需求合理选择。

文章目录

    • 一、理解Java编译与反编译
      • 1.1 宏观上的概念
      • 1.2 Java编译与反编译
    • 二、灵活选取Java反编译工具
      • 2.1 JDK自带的工具--javap
      • 2.2 好用的工具--jad
      • 2.3 功能强大的工具--CFR

一、理解Java编译与反编译

1.1 宏观上的概念

编程语言分为低级语言和高级语言。其中机器语言和汇编语言属于低级语言,直接用计算机指令编写程序,而C、C++、Java、Python等属于高级语言,用语句编写程序,语句是计算机指令的抽象表示。低级语言是计算机认识的语言、高级语言是程序员认识的语言。

所谓的编译,就是将高级语言翻译成计算机能识别的低级语言的过程,而反编译则是将已经编译好的语言还原成未编译高级语言的过程。

1.2 Java编译与反编译

针对于Java语言的编译,是将.java源文件通过javac编译器编译成.class字节码文件。字节码文件并不能由计算机直接识别,需要借助JVM内嵌的解释器解释成机器语言后执行。而Java语言的反编译,指的是将.class字节码文件还原成近似java源代码文件。此处近似是指反编译得到的是常量优化之后的代码。

二、灵活选取Java反编译工具

2.1 JDK自带的工具–javap

javapJDK自带工具,可以反编译代码,也可以查看.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
..........

2.2 好用的工具–jad

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

2.3 功能强大的工具–CFR

官网链接:http://www.benf.org/other/cfr/
相较于jadCFR是复杂的,需要输入多个参数,但是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

如果博文对您有所帮助,欢迎点赞和关注,哈哈。。

你可能感兴趣的:(直面Java)