Java反編譯工具
- 2021 年 8 月 13 日
- 筆記
編譯和反編譯
程式語言分成高級語言和低級語言。低級語言如機器語言、彙編語言。這類語言直接用電腦指令編寫命令,不需要編譯。這些語言機器能看到懂,但是程式設計師讀起來很費勁。而我們平時經常用的語言C、Java、Python屬於高級語言,這些語言程式設計師能看的懂。而機器是看不懂的。
簡單的總結為:高級語言就是程式設計師認識的語言,而低級語言是機器認識的語言。而把高級語言轉成低級語言這個過程就是編譯,而反編譯就是把低級語言轉成高級語言。
有了反編譯,我們就可以看懂Java編譯器生成的位元組碼,比如Synchronized的實現原理(監聽器monitor)、枚舉、語法糖、泛型,這些都需要用到反編譯工具。
javap
javap是jdk自帶的反編譯命令,可以對程式碼進行反編譯,但是反編譯的並不是java文件。
使用格式
javap <options> <classes>
常用: javap -c 類名
-help --help -? 輸出此用法消息
-version 版本資訊
-v -verbose 輸出附加資訊
-l 輸出行號和本地變數表
-public 僅顯示公共類和成員
-protected 顯示受保護的/公共類和成員
-package 顯示程式包/受保護的/公共類和成員 (默認)
-p -private 顯示所有類和成員
-c 對程式碼進行反彙編
-s 輸出內部類型簽名
-sysinfo 顯示正在處理的類的系統資訊 (路徑, 大小, 日期, MD5 散列)
-constants 顯示最終常量
-classpath <path> 指定查找用戶類文件的位置
-cp <path> 指定查找用戶類文件的位置
-bootclasspath <path> 覆蓋引導類文件的位置
下面寫一段synchronized程式碼:
public class SynchronizedTest {
private int count = 0;
public void addOne() {
synchronized (SynchronizedTest.class) {
count++;
}
}
}
執行編譯和反編譯命令
javac SynchronizedTest .java
javap -c SynchronizedTest.class
直接用記事本打開SynchronizedTest.class文件是一堆亂碼文件,用sublime打開是一串數字
cafe babe 0000 0034 0017 0a00 0400 1209
0003 0013 0700 1407 0015 0100 0563 6f75
6e74 0100 0149 0100 063c 696e 6974 3e01
0003 2829 5601 0004 436f 6465 0100 0f4c
696e 654e 756d 6265 7254 6162 6c65 0100
0661 6464 4f6e 6501 000d 5374 6163 6b4d
6170 5461 626c 6507 0014 0700 1507 0016
反編譯後的程式碼:
public class com.SynchronizedTest {
public com.SynchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_0
6: putfield #2 // Field count:I
9: return
public void addOne();
Code:
0: ldc #3 // class com/yyw/oil/web/admin/controller/purchase/SynchronizedTest
2: dup
3: astore_1
4: monitorenter
5: aload_0
6: dup
7: getfield #2 // Field count:I
10: iconst_1
11: iadd
12: putfield #2 // Field count:I
15: aload_1
16: monitorexit
17: goto 25
20: astore_2
21: aload_1
22: monitorexit
23: aload_2
24: athrow
25: return
Exception table:
from to target type
5 17 20 any
20 23 20 any
}
javap並沒有將位元組碼反編譯成成java文件,而是生成一種另一種能看的懂得位元組碼。可以看出被synchronized修飾的程式碼包含 monitorenter 和 monitorexit。synchronized 底層依賴著兩個指令來實現同步, 這裡看起來比較晦澀難懂。
CFR
在官網上下載jar,執行如下命令:
java -jar cfr-0.151.jar SynchronizedTest.class
得到反編譯java文件:
public class SynchronizedTest {
private int count = 0;
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public void addOne() {
Class<SynchronizedTest> clazz = SynchronizedTest.class;
synchronized (SynchronizedTest.class) {
++this.count;
// ** MonitorExit[var1_1] (shouldn't be in output)
return;
}
}
}
CFR還帶有一些參數:
| 參數 | 注釋 |
|---|---|
| –decodeenumswitch (boolean) | 去除switch對枚舉支援的語法糖 |
| –decodelambdas (boolean) | 去除lambda表達式的語法糖 |
| –decodestringswitch (boolean) | 去除switch string支援的語法糖 |
其餘參數可使用如下命令查看:
java -jar cfr-0.151.jar --help
idea
使用idea生成class文件,用idea打開class文件即可。idea是絕大多數Java程式設計師使用的編輯器,使用idea打開文件比較方便、快捷。


