[Mac] 將 Android APK 檔案反編譯成 Java 原始碼

[Mac] 將 Android APK 檔案反編譯成 Java 原始碼

最近在研究如何反編譯 (decompile) Android APK~

其實已經有人把一次到位的工具寫出來,

例如 bytecode-viewerAndroidDecompiler 等等。

不過實際用過的心得,是這些專案通常是組合了其他的工具,

因此專案沒有更新的話,附帶的工具也是舊的…

 

決定還是自己從頭到尾做一遍,

一方面可以確保各個工具都是最新的狀態,

另一方面可以視情況隨時抽換不同的工具,

獲得更好的反編譯效果~

 

1. 取出 APK 中的 classes.dex 檔案

以下是 Android 原始碼編譯的簡易過程:

  • 原始碼 .java 編譯成 .class 檔案
  • 所有的 .class 檔案合併壓縮成 .jar 檔
  • .jar 檔編碼成 classes.dex 檔案
  • classes.dex 被壓縮放到 .apk 檔案裡面 (APK 其實就是一種 zip 檔)

 

因此我們想取得原始碼的話,需要將這過程反過來。

先從 APK 中,把 classes.dex 解出來:

unzip test.apk classes.dex

 

2. 將 classes.dex 檔案解碼成 .jar 檔

我們可以利用 dex2jar 這個工具,

將 classes.dex 檔案解碼回原本的 .jar 檔~

 

在 Mac 上,用 Homebrew 直接安裝 dex2jar:

brew install dex2jar

 

dex2jar 裡附了許多 d2j- 開頭的工具,

我們可以用 d2j-dex2jar 將 .dex 檔解碼~

輸入是 classes.dex 時,預設輸出是 classes-dex2jar.jar:

d2j-dex2jar classes.dex

 

3. 使用 JD-GUI 檢視 .jar/.class 中的原始碼

JAR 檔裡包的是 .class 檔案,

要將 .class 檔案反編譯回 .java 原始碼,

有許多種工具可以做到,例如 JD-GUI

 

在 Mac 上用 Homebrew Cask 安裝 JD-GUI:

brew cask install jd-gui

 

安裝好後,用 JD-GUI 程式開啟 classes-dex2jar.jar 檔案,

就可以直接看到 .class 對應的 Java 原始碼了:

 

如果想要將所有的原始碼都儲存下來,

方便用別的方式檢視的話 (如 Sublime Text、或是 grep),

可以用 JD-GUI > File > Save All Sources,

就會將原始碼儲存壓縮成 classes-dex2jar.jar.src.zip,十分方便~

 

4. 使用 Procyon 反編譯 .jar/.class

依網友所言,JD-GUI 目前似乎還不支援新版 Java 的一些功能 (如 lamda),

一些後起之秀如 Procyon 值得一試~

 

在 Mac 上,用 Homebrew 安裝 procyon-decompiler:

brew install procyon-decompiler

 

接著就可以用它來反編譯 .jar 檔了:

procyon-decompiler -o src classes-dex2jar.jar

 

解完的原始碼就會放在 -o 指定的目錄下。

實際測試結果,procyon 反編譯後的原始碼,可讀性是比 JD-GUI 要好的,

但有時候 JD-GUI 看的到的原始碼,procyon 反而解不開,

這時會在原始碼裡面看到如下的錯誤訊息:

// 
// This method could not be decompiled.
// 
// java.lang.IllegalStateException: Expression is linked from several locations: Label_0074:
//     at com.strobel.decompiler.ast.Error.expressionLinkedFromMultipleLocations(Error.java:27)
//     at com.strobel.decompiler.ast.AstOptimizer.mergeDisparateObjectInitializations(AstOptimizer.java:2592)
//     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:235)
//     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
//     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:214)
//     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:99)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:757)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:655)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:532)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:499)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:141)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:130)
//     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:105)
//     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
//     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
//     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:317)
//     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:238)
//     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:138)
// 
throw new IllegalStateException("An error occurred while decompiling this method.");

 

因此建議各種反編譯工具都可以嘗試看看,可能會有不同效果喔~

 

參考資料:

A Quick Look at Java Decompilers

How to decompile an APK or DEX file on Android platform?

How to decompile a whole Jar file?

(本頁面已被瀏覽過 513 次)

發表迴響

你的電子郵件位址並不會被公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料