了解Dalvik代码的反汇编?

我正在和我写的一个小型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 ()V .locals 0 .prologue .line 6 invoke-direct {p0}, Landroid/app/Activity;->()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中存在的所有类。 你能告诉我一些如何做到的信息吗? 它是否以hex模式解析文件并查找Dalvik规范然后进行适当的解析? 或者是其他事情发生了? 例如,当我在classes.dex上使用hexdump时,它给了我这样的东西:

    64 65 78 0a 30 33 …

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

  • 假设该工具能够将传入的字节码分离为单独的类,则它继续扫描classes.dex文件中的hex代码,并使用Davlik规范从表中输出相应的操作码名称?

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

您正在查看的是davlik字节码。 Java代码由dx工具转换为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;->()V 

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

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

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

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

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

BTW,下一版IDA Pro将支持反汇编.dex文件