这里主要是看书的笔记。从基础开始。不断记录。直到啃完这本书(android软件安全权威指南:丰生强)

反编译和回编

android最常用的反编译工具apktool。可以跨平台win、linux、maos。用来反编译apk文件,反汇编成smali代码。然后可以修改smali代码后。再用apktool重新编译回apk,再签名。就可以直接安装了。贴上github地址:https://github.com/iBotPeaches/Apktool

mac可以直接brew install apktool安装

反编译命令:apktool d ./app-debug.apk -o outdir

反编译后,得到若干文件。res下面是资源文件,smali下面是代码。字符串资源一般在res\values\strings.xml,也有可能硬编码进源码。在apk中。这个字符串资源被加密存储为resources.arsc存放。反编译才解密出来成strings.xml的。

在正向android开发的时候。字符串资源都在gen/R.java中,而且每个字符串都有个对应的索引。反编译后,这个对应的索引放在public.xml里面。

outdir/unknown/instant-run.zip是android studio为了快速编译的优化,会将代码生成很多个片段。然后以dex的格式打包到instant-run.zip。启动时,再用特定的方式来加载这个文件。只有编译debug版本时会出现。

回编指令:apktool b outdir -o app-debug-rel.apk

如果汇编失败,错误是ResourcesFull(Androlib.java:477)

是因为apktool使用的framework-res.apk的版本太老。例如当前apk的API是25。那就找个对应手机。

然后adb pull /system/framework/framework-res.apk导出这个文件

然后安装apt tool if ./framework-res.apk

如果汇编还是失败就删除掉layout-v22目录

我测试2.5.0的apktool没有上面的问题

dalvik执行格式与字节码

android程序不是运行在java虚拟机,而是运行在dalvik虚拟机。android4.4以后又引入了art虚拟机。之前是由jit执行dalvik,后来换成AOT编译art。art也向下兼容,可以执行dalvik字节码。

dalvik虚拟机和java虚拟机的区别

1、字节码不同,分别使用的java字节码和dalvik字节码。java代码编译后,生成java字节码保存在class,然后java虚拟机直接解码运行了。而android是java字节码再转换成dalvik字节码,再打包成dex可执行文件。最后dalvik虚拟机解释dex文件来执行字节码。

2、dalvik可执行文件的体积更小

sdk中有个叫dx的工具是用来把java字节码转换成dalvik字节码。并且重新排列java类文件。消除冗余信息,避免重复加载。dx对所有java类中的常量池进行了分解。去掉冗余信息组成一个常量池。并且所有类共享。比如A类中有参数为一个string的签名方法。B类,C类中都有。这样方法签名就重复很多了。dx就把这些签名方法统一放一起。所有类从一个地方取。相同的字符串和常量在dex中只会出现一次。所以dalvik的可执行文件更小。下面是dex文件的格式

image-20210604213905401

3、虚拟机架构不同

java虚拟机是栈架构的。dalvik是寄存器架构的。直接寄存器的操作肯定是比栈的效率要更高。

dalvik虚拟机的执行流程

android系统主要分为4个大块:linux内核、函数库、android运行时、应用程序框架、应用程序。

dalvik虚拟机属于android运行时环境。它和一些核心库一起负责应用的运行。

android系统启动->加载内核->执行init进程->init进程负责初始化,然后读取init.rc,然后启动重要的外部程序Zygote

Zygote是所有进程的孵化器进程。启动后先初始化dalvik虚拟机,然后启动system_server进程并进入zygote模式。然后通过socket等候命令。当打开一个引用时,system_server进程通过Binder IPC方式将命令发送给zygote。前面用socket等候命令的zygote收到命令了。就fork一个自己创建出了个dalvik虚拟机实例来执行应用程序的入口函数。完成了程序的启动过程。

zygote有三种创建进程方法

fork() 创建zygote进程(实际不会调用)

forkAndSpecialize() 创建非zygote进程

forkSystemServer() 创建系统服务进程

for完毕后,就是dalvik虚拟机来完成执行字节码。

dalvik的执行流程是,先用loadlCassFromdEX()装载类,每个类解析成ClassObject结构存储。所有类存储到gDvm.loadedClasses中。字节码验证器用dvmVerifyCodeFlow()对装入的代码校验。然后虚拟机用findClass查找有main()的方法类。最后调用dvmInterpret()来初始化解释器并执行字节码。这里我去查了一下。发现4.4.4版本是有dvmInterpret的。不过较新的版本就没有了。

dalvik指令格式

位描述约定:

每16位用空格分开

每个字母代表4位,每个字母按高字节到底字节排序,每4位之前可能用|分割

顺序采用大写A-Z表示4位的操作码。op表示8位的操作码

Θ表示字段所有位的值是0

指令格式标识的约定:

指令格式标识大都是三个字符组成。前两个是数字,后一个是字母

第一个数字代表由多少个16位的字组成

第二个数字是最多使用的寄存器个数

第三个字母为类型码,标识指令使用的额外数据类型

特殊情况指令末尾多出一个字母。是s表示采用静态链接,是i表示应该被内联处理。

(这块看的不是怎么明白。以后再回头仔细琢磨)

dex反汇编

android官方提供的反汇编工具dexdump和第三方的baksmali

反汇编命令dexdump -d classes.dex

结果如下。这里注意。dexdump工具是在sdk的build-tools目录中。

1
2
3
4
5
2336d0: 7010 8447 0100                         |0000: invoke-direct {v1}, Lokio/SegmentedByteString;.toByteString:()Lokio/ByteString; // method@4784
2336d6: 0c00 |0003: move-result-object v0
2336d8: 6e10 7a46 0000 |0004: invoke-virtual {v0}, Lokio/ByteString;.utf8:()Ljava/lang/String; // method@467a
2336de: 0c00 |0007: move-result-object v0
2336e0: 1100 |0008: return-object v0

baksmali是第三方的。git地址:https://github.com/JesusFreke/smali.git

下载地址:https://bitbucket.org/JesusFreke/smali/downloads/

两者的区别是baksmali的寄存器命名法采用的p命名法,dexdump采用的v命名法。另外baksmali还支持打包反汇编的代码生成dex。

反汇编命令baksmali d classes.dex -o outdata

好像老版本的命令不一样。我这里用的是2.5.2。

前面dexdump的结果是直接将解析的结果全部打印出来。而baksmali是将结果解析到一个文件夹里面。层级划分的更加好。所以个人觉得baksmali要更方便点。

dalvik寄存器

dalvik虚拟机是寄存器架构的。将部分寄存器映射到了arm寄存器上。还有一部分通过调用栈进行模拟

使用的寄存器都是32位的。使用两个相邻的寄存器处理64位的类型

最大支持的寄存器数。之前指令格式的地方,有说dalvik指令的约定。指令每16位划分为一个部分。所以寄存器最大只能是16位存储的极限65535

dalvik指令集

这里主要是看smali代码有不懂的时候。可以过来查阅对应指令的说明。详细见63页