5

我正在我编写的一个小型 Hello World Android 应用程序上玩smali 和 baksmali 。我的源代码是:

package com.hello;

import android.app.Activity;
import android.os.Bundle;

public class Main extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

然后将其拆解为:

.class public Lcom/hello/Main;
.super Landroid/app/Activity;
.source "Main.java"


# direct methods
.method public constructor <init>()V
    .locals 0

    .prologue
    .line 6
    invoke-direct {p0}, Landroid/app/Activity;-><init>()V

    return-void
.end method


# virtual methods
.method public onCreate(Landroid/os/Bundle;)V
    .locals 1
    .parameter "savedInstanceState"

    .prologue
    .line 10
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 11
    const/high16 v0, 0x7f03

    invoke-virtual {p0, v0}, Lcom/hello/Main;->setContentView(I)V

    .line 12
    return-void
.end method

我知道这是某种中间表示,但不确定它是什么。据我了解,必须有一些关于如何理解这种表示的规范,但我无法弄清楚如何搜索它。因此,给定一个 apk 文件,有人可以用外行术语解释如何使用Dalvik 操作码规范来获得这种表示吗?我目前的理解是这样的:

  • 给定一个 APK,我可以提取二进制 XML 格式的 AndroidManifest.xml 并使用诸如 axml2xml.pl 之类的工具来获取不完整的清单的“文本”版本,或者我可以使用 apktool来获得更具可读性形式。但我仍然不确定他们使用什么规范将二进制 XML 转换为文本。
  • 反汇编程序以某种方式利用 Dalvil 操作码规范来读取 dex 文件并将其转换为上述表示形式。

关于上述两个步骤的任何信息(可能带有一些简单的示例)都可以很好地帮助我正确理解这些概念。

更新 1(在 Chris 回复后发布):

所以本质上,我会做以下事情来得到 Dalvik 字节码:

  • 获取一个 apk 并将其解压缩以获取 classes.dex 文件。
  • 然后反汇编程序读取 classes.dex 文件并确定 apk 中存在的所有类。你能给我一些关于这是如何完成的信息吗?它是否以十六进制模式解析文件并查找 Dalvik 规范,然后正确解析?还是发生了其他事情?例如,当我在 classes.dex 上使用 hexdump 时,它给了我这样的信息:

    64 65 78 0a 30 33 ...

这些现在用于操作码查找吗?

  • 假设该工具能够将传入的字节码分成单独的类,然后它会继续扫描 classes.dex 文件中的十六进制代码并使用 Davlik 规范从表中输出适当的操作码名称?

实际上,简而言之,我有兴趣知道所有这些“魔法”是如何完成的。例如,如果我要学习编写这个工具,我应该遵循的高级路线图是什么?

4

2 回答 2

14

您正在查看的是 davlik 字节码。dx 工具将 Java 代码转换为 Dalvik 字节码。清单是一个单独的问题,我将在稍后讨论。实际上,当您编译 Android 应用程序时,dx 工具使用 256 个 dalvik 操作码将您的 Java 代码转换为字节码(与 javac 将 Java 转换为标准 JVM 应用程序的 Java 字节码的方式相同)。

例如,invoke-super是指示 dvm(dalvik 虚拟机)调用超类上的方法的操作码。同样,invoke-interface指示 dvm 调用接口方法。

所以你可以看到

super.onCreate(savedInstanceState);

翻译成

invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)

在这种情况下,invoke-super需要两个参数,{p0,p1组和Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)参数,这是它用来查找和解析方法的方法规范(如果需要)。

然后invoke-direct是构造函数区域中的调用。

invoke-direct {p0}, Landroid/app/Activity;-><init>()V

每个类都有一个init用于初始化类的数据成员的方法,也称为构造函数。当你构造一个类时,虚拟机也必须调用超类的构造函数。这解释了为什么你的类的构造函数调用Activity构造函数。

关于清单,会发生什么(如果您查看源代码,这一切都在 Dalvik 规范中)是编译器(生成 apk 文件)将清单转换为更压缩的格式(二进制 xml)节省空间。清单与您发布的代码没有任何关系,它更多地指示 dvm 如何处理应用程序是一个整体,关于Activities,Services等。您发布的是实际执行的内容。

这是对您问题的高级回答。如果您需要更多,请告诉我,我会尽力而为。

编辑你基本上是对的。反编译器将二进制数据作为字节流从 dex 文件中读取。它了解格式应该是什么,并且能够提取常量、类等信息。关于操作码,这正是它所做的。它了解每个操作码的字节值是什么(或它在 dex 文件中的表示方式),并且能够将其转换为人类可读的字符串。如果你要实现这一点,除了了解编译器的一般基础知识之外,我将从深入了解 dex 文件的结构开始。从那里,您需要构建一个将操作码值与人类可读字符串匹配的表。有了这些信息和一些关于字符串常量等的附加信息。您可以构建已编译类的文本文件表示。那有意义吗?

于 2011-01-27T18:40:50.297 回答
3

操作码规范仅描述指令。dex 文件格式不仅如此——它包含 Dalvik VM(和反汇编程序)解释文件所需的所有元数据——字符串、类、类型、方法等。另请参阅官方操作码规范,它比您链接的更完整和详细。

<plug>顺便说一句,IDA Pro的下一个版本将支持 .dex 文件的反汇编</plug>

于 2011-01-27T18:38:40.193 回答