1.Dalvik虚拟机概述谷歌在2007年底正式发布了AndroidSDK,Dalvik虚拟机第一次进入了我们的视野。它是由丹·伯恩斯坦(DanBornstein)编写的,以他的祖先曾经居住的小渔村达尔维克(Dalvik)的名字命名。Dalvik虚拟机作为Android平台的核心组件,具有以下特点。(1)体积小,内存空间小。(2)专用的DEX可执行文件格式,体积更小,执行速度更快。(3)常量池使用32位索引值,对类方法名、字段名、常量的寻址速度更快。(4)基于寄存器体系结构,有一套完整的指令系统。(5)提供对象生命周期管理、栈管理、线程管理、安全和异常管理、垃圾回收等重要功能。(6)所有Android程序都运行在Android系统进程中,每个进程对应一个Dalvik虚拟机实例。Dalvik虚拟机与传统的Java虚拟机有很多不同,两者并不兼容。它们的显着差异主要表现在以下几个方面。(1)Java虚拟机运行Java字节码,Dalvik虚拟机运行Dalvik字节码。(2)Dalvik可执行文件较小。(3)Java虚拟机基于栈架构,Dalvik虚拟机基于寄存器架构。2.Smali概述Dalvik虚拟机(DalvikVM)是Google专门为Android平台设计的一套虚拟机。与标准Java虚拟机JVM的class文件格式不同,DalvikVM有自己的DEX可执行文件格式和指令集代码。Smali和Baksmali是DEX执行文件格式的汇编器和反汇编器。反汇编后,DEX文件会生成后缀为.smali的代码文件。Smali代码具有特定的格式和语法。对代码的解释。Smali语言最初是一位名叫JesusFreke的黑客对Dalvik字节码的翻译。它不是官方标准语言,因为Dalvik虚拟机的名字来源于冰岛一个小渔村的名字。Smali和Baksmali取自冰岛语。“汇编器”和“反编译器”。目前,Smali是GoogleCode上的一个开源项目。虽然现在有很多主流的DEX可执行文件反汇编工具,比如Dedexer、IDAPro,但是Smali不仅提供了反汇编功能,还提供了打包反汇编代码重新生成DEX的功能,所以Smali被广泛应用于App广告注入、汉化以及破解、ROM定制等。3.APKtool工具介绍APKtool工具是在Smali工具的基础上进行了封装和改进。除了对DEX文件的汇编和反汇编功能外,它还可以对APK中已经编译成二进制文件的资源文件进行反编译和重新编译。.同时还支持在Smali代码中添加调试信息,支持断点调试。在介绍APKtool的使用之前,我们先看一下一个APK包的组成。APK文件实际上是一个zip压缩包格式。使用工具解压后,以HelloWorld.apk为例,如图1所示。图1解压文件,使用APKtool反编译HelloWorld.apk文件,如图2所示。图2反编译成功后的文件执行apktoold命令,在HelloWorld目录下会生成如下一级目录结构,如图3所示。图3一级目录结构浏览各个子目录的结构后,我们可以发现原App项目目录结构基本相同。编译丢失签名信息。反编译后会额外生成一个apktool.yml文件。该文件记录了APKtool版本、APK文件名、是否为框架文件等基本信息。它会在APKtool重新编译时使用。4.Smali格式结构介绍(1)文件格式无论是普通类、抽象类、接口类还是内部类,在反编译后的代码中,都保存在一个单独的Smali文件中。每个Smali文件的前3行描述了当前类的一些信息,格式如下。.class<访问权限>[修饰关键字]<类名>.super<父类名>.source<源文件名>打开HelloWorld.smali文件,前3行代码如下。.classpublicLHelloWorld;.superLandroid/app/Activity;.source"HelloWorld.java"第1行“.class”指令指定当前类的类名。本例中类的访问权限为public,类名为“LHelloWorld;”。类名开头的L遵循Dalvik字节码的相关约定,表示后面的字符串是一个类。第2行的“.super”指令指定当前类的超类。“LHelloWorld”的父类;在这个例子中是“Landroid/app/Activity;”。第3行的“.source”指令指定当前类的源文件名。经过混淆后的DEX文件,反编译的Smali代码可能没有源文件信息,所以“.source”这一行的代码可能是空的。前3行代码之后是类的主体部分,一个类可以由多个字段或方法组成。(2)类结构无论是普通类、抽象类、接口类还是内部类,在反编译时都会为每个类生成一个单独的smali文件,但内部类有比较特殊的地方。内部类文件以“[外部类]$[内部类].smali”的形式命名,匿名内部类文件以“[外部类]$[编号].smali”的形式命名。内部类访问外部类的私有方法和变量时,必须通过编译器生成的“合成方法”间接访问。编译器将对外部类的引用作为第一个参数插入到内部类的构造函数参数列表中。内部类的构造函数首先将外部类的引用保存到一个“合成变量”中,然后初始化外部类,最后初始化自身。
