十二、反编译和反汇编 Android 应用

Android 应用要么用 Java 编写,要么用 Kotlin 编写。当构建一个应用时,它们被编译成 Dalvik 字节码——用dex (Dalvik 可执行文件)表示。这个 Dalvik 字节码是二进制的,因此不可读。既然如此,如果逆向工程师想要分析一个已经编译好的 Android 应用,他们只能选择反编译或反汇编 Dalvik 可执行文件。图 12-1 突出显示了创建和逆向工程一个 Android 应用的过程。

img/509502_1_En_12_Fig1_HTML.png

图 12-1

软件开发人员和逆向工程流程视图

反编译器 java

第一种选择是使用工具将 Dalvik 字节码反编译成人类可读的 Java。这个 Java 比真正的 Java 更像伪代码,因为它是反编译器对 Dalvik 程序集所代表的内容的“最佳猜测”。虽然 Java 开发人员更熟悉这种视图,但它通常不是最佳选择,因为它不仅不代表实际的应用代码,而且也不可运行或重新编译。像dex2jarjadx这样的工具可以用来反编译 Dalvik 可执行文件。Jadx 可用于将 Jadx 项目导出到 Gradle 项目,进而允许将项目加载到 Android Studio 中。

APKTool 可用于提取。来自 APK 的 dex 文件:

apktool  -s d <apk path>

用 JADX 反编译并查看 APK 或 Dex 文件的反编译 Java:

jadx -e <apk or dex file path>

反汇编的 Dalvik 字节码(Smali)

可以使用反汇编器将 Dalvik 字节码还原为人类可读的自身表示,而不是反编译成伪 Java。Dalvik 字节码更常用的这种形式叫做 Smali。对 Smali 来说,反汇编的好处是一个dex文件可以被反汇编、读取、修改、重组和提交,并且仍然处于完全运行的状态。

apk tool 等工具可以用来反汇编 dalvik 字节码:

apktool d <path>

由于其性质,Smali 比 Java 或 Kotlin 有更大的代码占用空间。例如,以下 Java 中的 Toast 代码(一个简单的 Android 弹出消息)是 Smali 中相同代码的一半大小。

Java:

Context context = getApplicationContext();
CharSequence text = "I'm a Toast";
int duration = Toast.LENGTH_SHORT;

Toast toast = Toast.makeText(context, text, duration);
toast.show();

型式:T1

.line 13
const-string v0, "I'm a Toast!"

.line 14
.local v0, "text":Ljava/lang/String;
const/4 v1, 0x1

.line 16
.local v1, "duration":I
invoke-virtual {p0}, Lcom/example/simpletoastapp/MainActivity;->getApplicationContext()Landroid/content/Context;

move-result-object v2

move-object v3, v0

check-cast v3, Ljava/lang/CharSequence;

invoke-static {v2, v3, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object v2

.line 17
.local v2, "toast":Landroid/widget/Toast;
invoke-virtual {v2}, Landroid/widget/Toast;->show()V

从运行的设备中提取 apk

为了分析(进而反汇编或反编译)一个 Android 应用,您可能需要首先从设备中提取它。ADB shell 可以用来做这件事。

下面使用包管理器列出设备上的所有包 id:

pm list packages
pm list packages | grep chrome

接下来,可以再次使用包管理器列出所需包的基本 APK 的路径(例如包路径是/data/app/com . Android . chrome-6 PIH 3g 1 et 8 uqozatukwptq = =/base . apk):

pm path <Package ID>

查看此命令返回的目录不需要特殊权限。但是,它的父目录(/data/app)没有非 root 的读取权限,这意味着设备上的应用不能以这种方式枚举。

最后,提取 APK 最简单的方法是使用 adb,如下所示:

adb pull <package base APK path>

还值得记住的是,诸如 APK 囤积者、 1 之类的工具是免费和开源的,可以用于从设备中大量提取 apk。

Footnotes [1](#Fn1_source) [`https://github.com/user1342/APK-Hoarder`](https://github.com/user1342/APK-Hoarder)【APK 囤积者| Github】。于 2020 年 12 月 27 日访问。