一、前言

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 命令即可查看,aassemble 的缩写。

可以看到,使用 -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项目源码。

推荐阅读:

用 Smali 手写一个可运行的 HelloWorld!!!的更多相关文章

  1. 手写一个Java程序输出HelloWorld

    ` 创建一个Hello.java文件使用记事本打开 public class Hello{ public static void main(String [] args){ System.out.pr ...

  2. 放弃antd table,基于React手写一个虚拟滚动的表格

    缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...

  3. 只会用就out了,手写一个符合规范的Promise

    Promise是什么 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Prom ...

  4. webview的简单介绍和手写一个H5套壳的webview

    1.webview是什么?作用是什么?和浏览器有什么关系? Webview 是一个基于webkit引擎,可以解析DOM 元素,展示html页面的控件,它和浏览器展示页面的原理是相同的,所以可以把它当做 ...

  5. 浅析MyBatis(二):手写一个自己的MyBatis简单框架

    在上一篇文章中,我们由一个快速案例剖析了 MyBatis 的整体架构与整体运行流程,在本篇文章中笔者会根据 MyBatis 的运行流程手写一个自定义 MyBatis 简单框架,在实践中加深对 MyBa ...

  6. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  7. 『练手』手写一个独立Json算法 JsonHelper

    背景: > 一直使用 Newtonsoft.Json.dll 也算挺稳定的. > 但这个框架也挺闹心的: > 1.影响编译失败:https://www.cnblogs.com/zih ...

  8. 教你如何使用Java手写一个基于链表的队列

    在上一篇博客[教你如何使用Java手写一个基于数组的队列]中已经介绍了队列,以及Java语言中对队列的实现,对队列不是很了解的可以我上一篇文章.那么,现在就直接进入主题吧. 这篇博客主要讲解的是如何使 ...

  9. 【spring】-- 手写一个最简单的IOC框架

    1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...

随机推荐

  1. Mysql中的like模糊查询

    MySql的like语句中的通配符:百分号.下划线和escape %代表任意多个字符 _代表一个字符 escape,转义字符后面的%或_,使其不作为通配符,而是普通字符匹配   数据库数据如下: 1. ...

  2. javaSE(九)之泛型(Generics)

    前言 这几天分享了怎么搭建集群,这一篇给大家介绍的是泛型,在我们的很多java底层的源代码都是有很多复杂的泛型的!那什么是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是 ...

  3. [解读REST] 4.基于网络应用的架构风格

    上篇文章介绍了一组自洽的术语来描述和解释软件架构:如何利用架构属性评估一个架构风格:以及对于基于网络的应用架构来说,那些架构属性是值得我们重点关注评估的.本篇在以上的基础上,列举一下一些常见的(RES ...

  4. 【框架学习与探究之定时器--Quartz.Net 】

    声明 本文欢迎转载,原文地址:http://www.cnblogs.com/DjlNet/p/7572174.html 前言 这里相信大部分玩家之前现在都应该有过使用定时器的时候或者需求,例如什么定时 ...

  5. LuoguP1196_银河英雄传说_KEY

    银河英雄传说 题目描述 公元5801年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历799年,银河系的两大军事集团在巴米利恩星域爆 ...

  6. 再探Spring IOC

    这次做了提纲 下面再来一个case study case描述: 这是工具类  //bean的配置信息略去 class MyUtil{ private static UserDao userDao; p ...

  7. 关于web前端代码艺术

    以前一直都以为html代码要分离得很好,html一个文件,css一个文件,js一个文件,然后最好一个html页面里面不要要太多冗余的代码,不要恶心地引入一个又一个的js,连jquery的引入我都觉得有 ...

  8. 宿命的PSS

    宿命的PSS 时间限制: 1 Sec  内存限制: 128 MB提交: 60  解决: 37[提交][状态][讨论版] 题目描述 最小生成树P.S.S在宿命的指引下找到了巫师Kismi.P.S.S希望 ...

  9. JQ判断浏览器以及版本

    JQuery 使用jQuery.browser 来判断浏览器,返回值可以为: safari(safari) opera(Opera) msie(IE) mozilla(Firefox). if($.b ...

  10. web应用程序 前段部分调优

    1. 使用瀑布图初步诊断网站性能瓶颈 一般来说,打开一个网页的速度会受到以下几项的影响: 1) 服务器花了太长的时间将.aspx页面的内容转化为html. 2) .aspx页面花了太长的时间从服务器端 ...