这里简单介绍一下我认识的apk。对于apk 的理解其实也是逆向的基础内容,对于apk的理解和熟悉程度能直接影响我们对逆向过程的理解,也能给我们逆向过程提供更开阔是思路。
主要包括三个内容:
一、apk的包结构
二、apk的打包过程
三、apk的安装过程
四、apk加载/(启动运行)过程
先来看看apk的包结构:
其实我们的apk 也好,jar 也好,其实都是可以看做是一个zip压缩包的。不信的可以自己使用zip工具浏览下压缩包结构。
我们这里随便找一款apk,然后解压开。就可以看到类似以下的结构:
其中各个文件夹存放的内容如下:
assets 资源文件(图片、音频、数据库、网页、配置文件等)
res 资源文件,需要编译(布局layout),还有一些drawable, menu,之类的文件
libs 存放一些第三方的jar包,这个跟一般的java代码类似
META-INF
存放文件摘要,摘要加密和签名证书等,可以看到对apk中所有的资源都进行了一次hash运算,为了防止apk的文件被修改,如果其中的文件别修改就能被检测出来,很多apk都会做这个校验,以保证apk的安全
META-INF/CERT.RSA 公钥以及加密算法的描述
META-INF/CERT.SF
加密文件,它是使用私钥对摘要明文加密后得到的密文信息,只有只用私钥匹配的公钥才能正确解开密文
META-INF/MANIFEST.MF
程序清单文件,包含包中所有文件的摘要明文
resources.arsc 资源文件,经过aapt工具加密的(语言包)
AndroidManifest.xml 清单文件(图标、界面、权限、入口)
classes.dex
源代码,这是dalvik虚拟机运行的,其实其实就是smali代码。我们可以用一些工具将它转为jar,java之类的。也就是我们的源码。很多大的apk,我们解压之后可能会看到好几个dex文件,这是因为我们每一个dex的文件大小都是有限制的。其实在真正运行的时候,是需要将这些dex都整合起来的。而且一般情况下,这些dex是不会重复的。
lib 各种平台下使用的对应的so文件,继续打开可以看到:
其实不同的apk可能还会有更多别的文件夹。这就意味着这个apk支持更多的CPU平台。对于我们的语言,最底层是机器码,然后是汇编代码,C语言这样的高级语言。但是对于汇编和机器码来说,这些代码是需要具体机器支持的。即是不同的cpu有自己的一套汇编指令,所以我们如果要兼容不同的cpu,就需要程序提供不同机器上运行的汇编代码。这里的每一个文件夹就是对一个cpu平台做兼容。
这里的armeabi 就是存放支持第5代、第6代的ARM处理器二进制接口(arm是arm指令集的意思,eabi其实是 Embedded Application Binary Interface 的缩写,也就是嵌入式应用二进制接口),早期的手机用的比较多。armeabi-v7a 存放的就是第7代及以上的 ARM 处理器支持。2011年15月以后的生产的大部分Android设备都使用它。当然我们可能会遇到更多的其他cpu架构,比如:
arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
x86: 平板、模拟器用得比较多。
x86_64: 64位的平板。
其实这里面的代码可能有兼容的。比如armeabi-v7a其实就是对armeabi的扩展,所以也是兼容armeabi的,也就是说你将v7a 下的so文件移动到 armeabi 下,一般也是没有问题的。但是这里有一点需要注意的,涉及到我们的动态调试。就是我们需要注意下自己的调试机子上的cpu 架构,才能正确找得到自己真实运行的so文件。不然很可能遇到自己机子上真正运行的so平台,跟实际上静态分析上的so不同的情况。
另外我们提一下,在android 2.2到android4.4发布的一段很长的时间里,arm平台只支持armeabi 和 armeabiv7-a两种架构。查看android4.0后的源码可以从设备驱动和上层开发规范中知道,其实这时候android已经可以支持64位架构的arm处理器了,但是那时候无论是dalvik虚拟机还是ART虚拟机都只支持32位的代码。
这种情况一直到android 5.0,ART虚拟机代替了dalvik虚拟机,支持64位程序的运行,ndk也正式加入了arm64-v8a指令集的支持,这套指令集对应的就是ARMv8-A处理器架构。
接下来是介绍下apk 的打包过程,大致的流程图如下:
根据上图大致可以分为几个步骤
1、打包资源文件。
使用aapt来打包res资源文件,包括assert 、res、AndroidManifest.xml等文件。最后生成R.java和resources.arsc 文件
2、处理AIDL文件
这个不一定有,但是如果app中涉及到跨进程通信的话大多会出现,我们需要将AIDL文件转为java文件。
3、编译生成.class 代码。
项目中的java源码,aidl转化的java代码等全部编译成.class
4、将所有的.class 转为dex文件
android中并不直接执行.class。而是使用的dalvik虚拟机,所以需要将clasa转为dex。这里的class除了有我们的源码,也可能来自于第三方提供的jar。我们需要将所有的.class 都打包成dex。这里需要注意下dex的大小是有限制的,所以可能生成多个dex。
5、打包生成apk文件
通过apkbuilder工具,将aapt生成的resources.arsc和res文件、assets文件和classes.dex一起打包生成apk。打包的工具apkbuilder位于 android-sdk/tools目录下。
6、对apk文件进行签名
只有经过签名的apk才允许被安装,我们开发中能用debug版本安装其实也是开发环境帮我们用自带的debug签名了我们的apk。
7、对签名后的APK文件进行对齐处理
通过zipalign工具,将签名后的apk进行对齐处理。工具位于android-sdk/tools目录下。对齐的主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。对齐的作用就是减少运行时内存的使用。
经过这样的步骤,一个apk 就大致诞生了。
apk生成之后就到apk的安装了,我们来看看一个apk是如何被安装上的。
码字有点累了,今天到这吧。后面再找时间继续下面的内容。