gooobooo 发表于 2019-9-18 22:03:00

安卓逆向学习笔记(0)- HelloWorld.apk的文件结构

## 0.什么是apk?

apk是AndroidPackage的缩写,即Android应用安装包(apk)。
新建一个安卓工程HelloWorld(本文采用的SDK版本为android 4.4),该工程的源代码结构如图1所示:



图1 HelloWorld源代码的结构

​编译运行该工程,我们会在HelloWorld文件夹的bin目录下面得到一个HelloWrold.apk,这个就是我们所要的apk文件。

## 1. apk是怎样练成的

​ 我们在进行安卓应用开发的时候,只需要右击项目 -> Run as -> 1Android Application就可以生成一个apk。看起来这一切都非常简单,但是实际上生成apk的过程是比较复杂的。对于安卓应用开发者来说,我们不需要在意apk的生成过程,但是对于逆向人员来说,了解apk的编译和生成过程是很有必要的,因为这样能够帮助我们更好地了解apk的体系架构。一个apk的生成需要经历几个步骤,中间需要用到很多工具,具体流程如图2所示:



图2 apk的编译和生成过程(图片来源:(http://www.cnblogs.com/devinzhang/archive/2011/12/20/2294686.html))


## 2.一个apk的文件结构

​ apk文件实质上是一个.zip格式的压缩包,所以我们可以将HelloWorld.apk修改为HelloWorld.zip。然后用WinRAR或者其他压缩软件打开HelloWorld.zip,我们会看到以下文件结构,如图3所示:



图3 HelloWorld.apk的文件结构

​ 将HelloWorld.zip解压后,我们会得到5个文件(文件夹),如图4所示:



图4 HelloWorld.zip解压缩后得到的文件

​ 下面,我们分别来看一下这5个文件(文件夹)的内容。

### 2.1 AndroidManifest.xml

​ 这里的AndroidManifest.xml就是源代码中的AndroidManifest.xml编译后所得到的文件。AndroidManifest.xml是安卓应用程序的全局配置文件,该文件保存了apk的包名,版本信息,sdk版本,Activity ,Service, Boardcast Receiver, Content Provider等应用组件的配置信息。还有程序所需要的权限也是在AndroidManifest.xml这个文件中声明的。关于AndroidManifest.xml的详细信息,请参考这篇文章:(http://blog.chinaunix.net/uid-9185047-id-3460344.html)

### 2.2 classes.dex

​ 虽然Android开发的源语言是java,但是Android应用程序却不在标准的java虚拟机上运行。Google为Android平台专门设计了一套用于运行Android程序的虚拟机,这就是Dalvik虚拟机(Dalvik Virtual Machine)。而classes.dex就是运行在Dalvik虚拟机上的可执行文件。从图2中可以看出,我们编写的java源代码经过java编译器编译后会生成.class文件;而Android SDK自带的dx工具(dx.bat在\sdk安装目录\sdk\build-tools\android-4.4这条路径下,dx.jar在\sdk安装目录\sdk\build-tools\android-4.4\lib这条路径下)会将这些.class文件转换为classes.dex。值得一提的是,在Android5.0中,Dalvik虚拟机已经被ART虚拟机(Android Runtime)所取代。有兴趣的童靴可以看这篇文章:[ Android ART运行时无缝替换Dalvik虚拟机的过程分析](http://blog.csdn.net/luoshengyang/article/details/18006645)。

​ 虽然在Android5.0中ART虚拟机已经取代了Dalvik虚拟机,但是使用Android5.0 SDK生成的apk与低版本SDK生成的apk相比,二者的文件结构变化不大。采用Android5.0 SDK生成的apk中也有classes.dex这个文件。   

### 2.3 resources.arsc

​ arsc,全称为application resource files,是一个包含了已被编译好的资源的二进制格式文件。我们知道,一个安卓应用程序往往包含了很多的资源(例如:layout文件,图片,字符串,菜单)。那么程序在运行时,是怎么调用这些资源的呢?其实在apk编译和生成过程中(参考图2),AAPT一方面会在源代码中为每个资源文件都赋予一个32位的整数做为标记,这些整数全部保存在R.java这个源文件中,R.java是系统自动生成的,用户是无法对其进行修改的。这里需要注意一点:assets文件夹中的资源文件是不会被编译的,关于assets文件夹与res文件夹的区别,感兴趣的童靴请看这篇文章:(http://blog.csdn.net/zuolongsnail/article/details/6444806) 。另一方面,AAPT会生成一个resources.arsc文件,这个二进制文件包含了所有的资源名字以及标记这些资源的数字。resources.arsc的作用是当app运行时,手机设备通过该文件能够很方便匹配和解析apk中的资源。

### 2.4 res文件夹

​ apk中的res文件夹由drawable文件夹,layout文件夹,menu文件夹这3个子文件夹组成。其中,drawable文件夹用于存放apk的图片资源,layout文件夹用于存放布局文件,menu文件夹用于存放菜单文件。与原工程项目中的res文件夹相比,apk中的res文件夹少了values这个文件夹。(至于为什么少了values这个文件夹,我也不知道,还请路过的大牛指点一下)

### 2.5 META-INF文件夹

​ META-INF文件夹存放着apk的签名信息。如果你还不了解安卓签名机制,请看这篇文章:[ Android应用程序签名详解](http://blog.csdn.net/lyq8479/article/details/6401093)
​ 打开META-INF文件夹,可以看到3个文件:CERT.RSA,CERT.SF,MANIFEST.MF。其中CERT.RSA包含了公钥信息和发布机构信息;MANIFEST.MF中保存了除自身以外所有其他文件的SHA-1并进行base64编码后的值(注意:对于xml等文本格式的资源文件,系统先将这些文本文件编译成二进制文件,再获取二进制文件的SHA-1值并进行base64编码);CERT.SF的生成过程分两步:(1)读取MANIFEST.MF文件的SHA-1,然后将该SHA-1值base64编码后保存为SHA1-Digest-Manifest,所以与MANIFEST.MF文件相比,CERT.SF会多出一个SHA1-Digest-Manifest值,(2)然后再逐个读取MANIFEST.MF中每个资源文件的Name和SHA1-Digest值再添加2个空行后进行SHA-1,将该SHA-1值进行base64编码后依次写到CERT.SF中。是否听得迷迷糊糊,下面小弟就用HelloWorld.apk举个栗子,手动模拟MANIFEST.MF文件生成classes.dex的SHA1-Digest的过程:

​(1)通过HashTab插件(该插件下载地址见文章末尾)可以查看到classes.dex文件的SHA-1,如图5所示:



图5 classes.dex文件的SHA-1值

​ (2)可以得知classes.dex文件的SHA-1值为:AF37C43BD22A4022FA1998F82F08B278B8844EDB(请注意:这是一串16进制数字,不是字符串),然后[在线](http://tomeko.net/online_tools/hex_to_base64.php?lang=en)对该SHA-1值进行base64编码可以得到其对应的base64编码为: rzfEO9IqQCL6GZj4LwiyeLiETts=
打开MANIFEST.MF文件,可以看到classes.dex的SHA1-Digest值为 rzfEO9IqQCL6GZj4LwiyeLiETts= 如图6所示:



图6 MANIFEST.MF中的classes.dex的SHA1-Digest

​ 这与我们所得到的base64编码是一样的,由此可见我们手动生成MANIFEST.MF中classes.dex的SHA1-Digest值是正确的。

​ 下面是手动模拟CERT.SF生成classes.dex的SHA1-Digest的过程:

​(1)新建一个文本文件,将MANIFEST.MF中的classes.dex的Name和SHA1-Digest这两行拷贝出来,后面跟上两个空行,保存到文件中,如图7所示:



图7用于保存MANIFEST.MF文件中classes.dex所对应的Name与SHA1-Digest值的文本文件

​(2)通过HashTab插件查看该文本文件的SHA-1值,如图8所示:



图8 文本文件的SHA-1值

​ 可以看出该文本文件的SHA-1值为: 3AD0EBD342ABC2CF801CFAACFAAA45334E0A4337, 将该SHA-1值进行base64编码可以得到base64编码值:OtDr00Krws+AHPqs+qpFM04KQzc=

(3)打开CERT.SF文件,我们可以看到在文件中classes.dex对应的SHA1-Digest值为: OtDr00Krws+AHPqs+qpFM04KQzc=

​ 如图9所示:



图9 CERT.SF文件中classes.dex对应的SHA1-Digest值

​ 第(2)步最后得到的base64值与该SHA1-Digest值一致,所以可以证明我们手动模拟生成classes.dex的SHA1-Digest值是正确的。关于这3个文件的详细介绍,有兴趣的童靴请看这篇文章:(http://blog.csdn.net/asmcvc/article/details/9312293)。如果从事安卓逆向,我们必须要清楚一点:没有签名的apk是无法在真机(模拟器)上安装运行的,所以签名文件对apk是很重要的。
## 3.总结

​ 安卓项目的文件结构与安卓apk的文件结构存在着一一对应的关系。安卓应用开发的本质是:将源代码和各种资源文件编译整合成一个apk。安卓逆向的本质是:想办法将apk转化为源代码和资源文件。

​ 简单来说,apk就是一个带有签名的zip格式的压缩包,签名为了保护开发者的权益和标识apk。做为android逆向学习的第一步,了解apk的文件结构和生成过程是很有必要的。

​ 这篇文章总结了很多安卓大牛博客的知识点,主要介绍了HelloWorld.apk的文件结构和生成过程,但是真实的商业apk的文件结构会更复杂,为了提升apk的安全性能,现在很多安卓应用程序的核心代码都采用NDK开发,所以生成的apk中会多出一个lib文件夹用于存放so文件。
页: [1]
查看完整版本: 安卓逆向学习笔记(0)- HelloWorld.apk的文件结构