MASTG-TECH-0017: 反编译 Java 代码
在逆向工程一个 Android 应用程序时,请务必识别所有与应用程序逻辑相关的感兴趣的位置,并使用正确的技术进行逆向工程。在本技术中,我们将重点关注 DEX 字节码,它存储在 APK 主目录中的一个或多个 classes<x>.dex
文件中。但是,Android 应用程序也可能包含其他文件中的代码,这些代码将由系统组件(例如 WebView
)或打包的本地库解释。
将 DEX 字节码反编译为 Java 通常是一个简单的过程,并且存在许多反编译器可以提供与原始源代码几乎相同的代码。但是,某些信息在编译过程中始终会丢失,因此检索到的代码永远不会完全匹配。但是,如果代码已被故意混淆(或应用了一些破坏工具的反编译技巧),则逆向工程过程可能非常耗时且无效率。这也适用于包含本地代码的应用程序。它们仍然可以被逆向工程,但该过程不能被简单地自动化,并且需要了解底层细节。
虽然 DEX 字节码和 Java 字节码不相同,但它们可以相互转换。因此,可以通过首先将 .dex
文件转换为 .jar
文件,然后反编译 .jar
文件来使用 Java 反编译器反编译 Android 应用程序。由于一个应用程序可以有多个 .dex
,这是一个乏味的过程,幸运的是,许多反编译器都原生支持 .apk
文件,从而为您处理此过程。一些反编译器也直接在 DEX 字节码上工作,而不是首先将其转换为 Java 字节码。
由于 Java 应用程序的悠久历史,许多反编译器可用于反编译 Android 应用程序。虽然存在一些非常强大的商业 Android 反编译器,但也有免费的反编译器在许多方面可以与商业反编译器相媲美。最受欢迎的免费反编译器是 jadx,它正在积极开发中。如果 jadx 未能反编译部分代码,最简单的替代方法是 Bytecode Viewer,它在一个应用程序中结合了六个不同的反编译器 (JD-GUI, Procyon, CFR, Fernflower, Krakatau 和 JADX-Core)。这两个反编译器都原生支持 .apk
文件,虽然 bytecodeviewer 看起来是更好的选择,但 jadx 具有更多的 UI 功能,可以提供比 bytecodeviewer 更加愉悦的用户体验。
警告
反编译总是会失败,要么是由于对 .dex
文件的故意操纵,要么仅仅是由于反编译器中的错误。如果所有反编译器都失败,您可以始终回退到 将代码反汇编为 Smali。
让我们来看看 Android UnCrackable L1 在几个不同反编译器中的反编译版本
使用 jadx-gui¶
您可以通过启动 jadx-gui
并使用 GUI,或者通过在启动 jadx-gui 时直接指定 APK 来打开 APK 文件
jadx-gui Uncrackable-Level1.apk
Jadx-gui 在反编译 Android 应用程序时通常做得很好。如果反编译不成功,它还支持多种回退模式,可以在窗格底部切换(SMALI,Simple,Fallback)
使用 jadx¶
除了使用 jadx-gui 打开二进制文件外,还可以使用 jadx 的核心将代码反编译到文件系统。之后,您可以使用您最喜欢的代码编辑器来检查代码
jadx -d decompiled UnCrackable-Level1.apk
可以指定要反编译的类,而不是反编译整个应用程序。这可以通过 --single-class
参数完成
jadx --single-class sg.vantagepoint.uncrackable1.MainActivity UnCrackable-Level1.apk
INFO - loading ...
INFO - Saving class 'sg.vantagepoint.uncrackable1.MainActivity' to file '/home/owasp/UnCrackable-Level1/sources/sg/vantagepoint/uncrackable1/MainActivity.java'
INFO - done
Jadx 还具有一项功能,允许您从反编译的代码创建一个 gradle 项目。然后可以使用 Android Studio 打开此 gradle 项目。请注意,由于在原始编译期间信息丢失,您将无法实际编译应用程序,但您仍然可以使用 Android Studio 强大的 IDE 功能来分析反编译的代码。
jadx -d decompiled -e UnCrackable-Level1.apk
使用 Bytecodeviewer¶
Bytecodeviewer 可以并排显示不同的反编译器。在上面的示例中,显示了 Fernflower 和 CFR。即使代码是等效的,两个反编译结果之间也存在差异。例如,CFR 倾向于在命名变量时使用可用的类型信息,而 Fernflower 只是使用 var{index}
。
请参阅 审查反编译的 Java 代码 部分,了解如何在检查反编译的 Java 代码时继续操作。