(注:根据网上所看博文整理而成)
何为APK?
APK全称:AndroidPackage,即安卓应用安装包;
一个APK生成的流程:
(图片来自于 http://www.cnblogs.com/devinzhang/archive/2011/12/20/2294686.html)
(这里是参照这篇博文所写的笔记)
1.假定系统(Linux和Windows无所谓)已经安装了(JDK和 android sdk), android sdk的路径已知为:Android_sdk_home,想要编译的android 版本为
android_os_version,
我们重点关注的:
1.此过程的输入;
2.此过程输出;
3.此过程使用了什么工具
名称 | 功能介绍 | 在操作系统中路径 |
---|---|---|
aapt | android资源打包工具 | ${ANDROID_SDK_HOME}/platform-tools/appt |
aidl | Android接口描述语言转化为.java文件的工具 | ${ANDROID_SDK_HOME}/platform-tools/aidl |
javac | Java Compiler | ${JDK_HOME}/javac或/usr/bin/javac |
dex | 转化.class文件为为Davik VM能识别的.dex文件 | ${ANDROID_SDK_HOME}/platform-tools/dx |
apkbuilde | 生成apk包 | ${ANDROID_SDK_HOME}/tools/opkbuilder |
jarsigner | .jar文件的签名工具 | ${JDK_HOME}/jarsigner或/usr/bin/jarsigner |
zipalign | 字节码对齐工具 | ${ANDROID_SDK_HOME}/tools/zipalign |
第一步:打包资源文件,生成r.java文件
输入:
resource文件(就是工程中res中的文件),Assets文件(另外一种资源,此类资源安卓系统不像对res中的文件那样去优化),AndroidManifest.xml
(清单文件)(包名就从这里读取,生成r.java就需要包名),android基础类库(android.jar文件)四类文件;
输出:
打包好的资源(即:一般在Android工程的bin目录可以看到一个叫resources.ap_的文件就是它了);R.java文件(在gen目录中)
工具:
aapt工具,它的路径在${ANDROID_SDK_HOME}/platform-tools/aapt(如果你使用的是Windows系统,按惯例路径应该这样写:%ANDROID_SDK_HOME%\platform-tools\aapt.exe,下同)。
第二步:处理AIDL文件,生成对应的.java文件(有很多工程没有用到AIDL,那么此过程就可以省略)
输入:
源码文件、aidl文件、framework.aidl文件
输出:
对应的.java文件
工具:
aidl工具
第三步:
编译java文件,生成对应的.class文件
输入:
源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)
输出:
.class文件
工具:
javac工具
第四步:
把.class文件转化成Davik VM支持的.dex文件
输入:
.class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
输出:
.dex文件
工具:
javac工具
第五步:
打包生成未签名的.apk文件
输入:
打包后的资源文件、打包后类文件(.dex文件)、libs文件(包括.so文件)
输出:
未签名的.apk文件
工具:
apkbuilder工具
第六步:
对未签名.apk文件进行签名
输入:
未签名的.apk文件
输出:
签名的.apk文件
工具:
jarsigner
第七步:
对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的)
输入:
签名后的.apk文件
输出:
对齐后的.apk文件
工具:
zipalign工具
了解这个过程可以实现自动化脚本,利用Python、Perl等,类似于Windows下的批处理,linux下的Bash,Java下的Ant,可以将sdk精简到更小。
2.apk结构
一个典型的apk结构:(原文件名为2,后改为2.zip,然后将其解压)
2.1AndroidManifest.xml(清单文件)
这里的AndroidManifest.xml就是源代码中的AndroidManifest.xml编译后所得到的文件。AndroidManifest.xml是安卓应用程序的全局配置文件,该文件保存了apk的包名,,版本信息,sdk版本,四大组件(Activity ,Service, Boardcast Receiver, Content Provider)的配置信息,程序所需要的权限也在其(AndroidManifest.xml)中;
Android应用程序签名详解见:
http://blog.csdn.net/lyq8479/article/details/6401093
http://blog.csdn.net/jiangwei0910410003/article/details/50402000
AndroidManifest.xml配置文件详解
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虚拟机的过程分析。
虽然在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编译和生成过程中(参考流程图),AAPT一方面会在源代码中为每个资源文件都赋予一个32位的整数做为标记,这些整数全部保存在R.java这个源文件中,R.java是系统自动生成的,用户是无法对其进行修改的。这里需要注意一点:assets文件夹中的资源文件是不会被编译的,关于assets文件夹与res文件夹的区别,感兴趣的童靴请看这篇文章:Android中资源文件夹res/raw和assets的使用 。另一方面,AAPT会生成一个resources.arsc文件,这个二进制文件包含了所有的资源名字以及标记这些资源的数字。resources.arsc的作用是当app运行时,手机设备通过该文件能够很方便匹配和解析apk中的资源。
2.4 res文件夹
apk中的res文件夹由drawable文件夹,layout文件夹,menu文件夹这3个子文件夹组成。其中,drawable文件夹用于存放apk的图片资源,layout文件夹用于存放布局文件,menu文件夹用于存放菜单文件。与原工程项目中的res文件夹相比,apk中的res文件夹少了values这个文件夹
2.5META-INF文件夹
META-INF文件夹存放着apk的签名信息。如果你还不了解安卓签名机制,请看上面链接;
打开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中。是否听得迷迷糊糊,下面手动模拟MANIFEST.MF文件生成classes.dex的SHA1-Digest的过程:
(1)通过HashTab插件可以查看到classes.dex文件的SHA-1,如图所示
(2)可以得知classes.dex文件的SHA-1值为:AF37C43BD22A4022FA1998F82F08B278B8844EDB(请注意:这是一串16进制数字,不是字符串),然后 在线对该SHA-1值进行base64编码可以得到其对应的base64编码为: rzfEO9IqQCL6GZj4LwiyeLiETts=
打开MANIFEST.MF文件,可以看到classes.dex的SHA1-Digest值为 rzfEO9IqQCL6GZj4LwiyeLiETts= 如图所示:
这与我们所得到的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所示:
可以看出该文本文件的SHA-1值为: 3AD0EBD342ABC2CF801CFAACFAAA45334E0A4337, 将该SHA-1值进行base64编码可以得到base64编码值:OtDr00Krws+AHPqs+qpFM04KQzc=
(3)打开CERT.SF文件,我们可以看到在文件中classes.dex对应的SHA1-Digest值为: OtDr00Krws+AHPqs+qpFM04KQzc=
如图9所示:
第(2)步最后得到的base64值与该SHA1-Digest值一致,所以可以证明我们手动模拟生成classes.dex的SHA1-Digest值是正确的。关于这3个文件的详细介绍,有兴趣的童靴请看这篇文章:android APK签名汇总整理 。如果从事安卓逆向,我们必须要清楚一点:没有签名的apk是无法在真机(模拟器)上安装运行的,所以签名文件对apk是很重要的。
3.总结
安卓项目的文件结构与安卓apk的文件结构存在着一一对应的关系;
安卓应用开发的本质是:
将源代码和各种资源文件编译整合成一个apk。
安卓逆向的本质是:
想办法将apk转化为源代码和资源文件。 简单来说,apk就是一个带有签名的zip格式的压缩包,签名为了保护开发者的权益和标识apk。
做为android逆向学习的第一步,了解apk的文件结构和生成过程是很有必要的。这篇文章总结了很多安卓大牛博客的知识点,主要介绍了基本的一个apk的文件结构和生成过程,但是真实的商业apk的文件结构会更复杂,为了提升apk的安全性能,现在很多安卓应用程序的核心代码都采用NDK开发,所以生成的apk中会多出一个lib文件夹用于存放so文件。