一、前言
Android 的 App 实际上并不是运行在 Java 虚拟机中,而是运行在 Dalvik 虚拟机中。Dalvik 虚拟机对 Java 虚拟机做了一些额外的优化,让它更适用于移动设备。而 Dalvik 也有自己独特的汇编语言,Dalvik 就是通过这些汇编的指令集,来运行我们编译好的 Apk 程序。
一般这些内容,我们正常开发 App 是接触不到的,但是如果你有反编译的需求,那你就需要花点时间研究一下它。本文不会介绍 Dalvik 的汇编指令集,它本身已经有完备的文档,没什么好说的。
本文就从逆向思维的路子,教你如何写一个可在 Dalvik 上独立运行的 Hello World 程序。
在这个过程中,我们需要了解 smali 语法,smali 是一种宽松的 Jasmin/dedexer 语法,它可以通过 baksmali 将我们已经编译好的 dex 格式的汇编语言,反汇编成 smali 文件,供我们阅读。
那么,我们的第一个 Dalvik 版本的 Hello World ,就从一个编写一个 smali 文件开始吧。
二、开始编写 Smali
既然是 smali 文件,当然是以 .smali
为文件后缀,这里先创建一个 SmaliHello.smali 文件,直接上代码,再来看每行的含义。
第 1~3 行,实际上是声明了 smali 文件的头,每个 smali 文件都会有它们。.class
表示类名,这里定义了一个 public 的类,全类名是 com.cxmyDev.smalidemo.SmaliHelo
。.super
表示它的父类,这里是 Object。.source
表示它对应的 Java 文件的文件名,这只是个标记,实际上在真实反编译的场景下,如果代码被混淆了,.source
可能会没有值。
第 6 行,定义了一个 # direct methos
,它是 baksmali 为我们添加的一行注释,表示之后紧跟着这个类相对应的方法,需要注意的是,只会包含构造方法和静态方法,这里不展开讨论了。
第 7~14 行,以一个 .method
开始,.end
结尾,表示它是一个方法,而 publi constructor
表示它是一个公有的构造方法,这里其实就是 Java 类默认的构造方法,如果我们不声明构造方法,编译器会为我们创建一个�无参的构造方法,这里就是它了。没啥好说的,直接写就好了。
第 16~28 行,它也是一个方法,public static
表示它是一个公有的静态方法,方法名是 main。而之后紧跟的 ([LJava/lang/String;])V
表示它需要传递一个 String 数组,并且返回值是 void。再来看看方法内部的代码,第 17 行,.registers
表示寄存器的声明,这里声明了 3 个寄存器,供后面使用,.param
表示了方法传递的参数,参数名叫 args
,并且是一个 String 数组类型。.prologue
表示一个开场,之后跟随的才是我们业务逻辑的代码。
第 21 行,sget-object
表示创建了一个 PrintStream 对象,并存入 v0 寄存器中。
第 23 行,const-string
表示什么了一个字符串 "Hello CxmyDev!",并存入 v1 寄存器中。
第 25行,invoke-virtual
表示调用了 PrintStream 中的 printIn()
方法,参数传递的是 v1 寄存器中的值,就是之前存储的 "Hello CxmyDev!"。
到这里,smali 中的代码,我们已经逐行认清楚它是干嘛的了,有些细节就不展开讲了,不了解的可以看看 Dalvik 的语法和 smali 的语法,有兴趣可以先看看这两个链接。
Dalvik-bytecode:
https://source.android.com/devices/tech/dalvik/dalvik-bytecode
Dex 格式:
https://source.android.com/devices/tech/dalvik/dex-format
三、编译 smali
编写完 smali 代码之后,接下来就要将它编译成 dex 文件了,这就需要用到 smali.jar 这个工具。你可以在 Bitbucket 上直接下载到 jar 包。
https://bitbucket.org/JesusFreke/smali/downloads/
smali.jar 最新的版本版本是 2.2.1,所以这里下载这个版本就可以了。(不方便下载的话,文末有下载方式)
先来看看 smali.jar 的帮助文档,直接使用 java -jar
命令即可。
我们这里主要会用到它的 assemble
命令,再来看看 assemble 的帮助文档,使用 java -jar snali.jar a
命令即可查看,a
是 assemble
的缩写。
可以看到,使用 -o
就可以指定输出的 dex 文件,然后再指定编译的 smali 文件即可。
java -jar smali-2.2.1.jar a -o hello.dex SmaliHello.smali
执行完成,如果没有报错的话,可以在当前目录下,生成一个 hello.dex 文件。如果有其它输出,应该就是报错了,查看一下报错信息解决它就好了。
得到 hello.dex 文件之后,我们还需要将它放到我们的 Android 设备上,才可以运行,这个非常简单,使用 adb push
命令即可。
最终运行这个 dex 文件,还需要使用到 dalvikvm ,使用 adb shell dalvikvm -h
命令,查看帮助文档,文档比较长,这里截取关键部分。
这里我们主要是使用 -cp
指定 classpath 即可执行,它后续接收的是类的完整签名,包含包名。
然后我们就需要使用 dalvikvm -cp
命令即可执行,主要指定要执行的类,需要包含包名的全类名。
这里,就可以输出我们之前编写的 Hello CxmyDev! 了。
下面备份一下输入的命令。
adb shell push hello.dex /sdcard/
adb shell dalvikvm -cp /sdcard/hello.dex com.cxmydev.smalidemo.SmaliHello
四、Dex 的 Java 代码
到这里就算是将清楚,从零编写一个 smali 代码,到编译成 dex 并成功执行的所有过程了。
我们再来看看,我们编辑的 smali 代码,到底用 Java 代码编写,是什么内容,可以帮助我们更好的理解它。
其实很简单,再使用 jadx 工具,对 dex 进行反编译。因为我们这里也不涉及混淆,所以代码结构非常的清晰。
这里就是初学 Java 的时候,一个标准的 Java 程序,有个 main 函数为程序的入口函数。
五、小结
本文到这里就算是完成了整个逆向的反逆向流程,相信能让你加深对反编译和 smali 的理解。
有些工具如果不方便下载(原因你懂的),可以在承香墨影公众号回复 smali工具 进行下载,可以下载到本文所有涉及到的资源文件。
更多反编译的细节,可以在承香墨影公众号回复 Android反编译,你将获得我整理好的一些关于反编译的资料。
今天在承香墨影公众号的后台,回复 成长。我会送你一些我整理的学习资料,包含:Android反编译、算法、设计模式、Web项目源码。
推荐阅读:
- Glide 这样用,更省内存!!!
- 跳槽前,不思考这些等于白跳!!!
- 哪些年熬过的夜,终将会"回报"你的
- 当你只有一个设备的时候,如何做好屏幕适配
- Android 源码,在线阅读更方便!!!