Android软件安全权威指南

读书笔记

Posted by JovenHe on December 18, 2021

Android可执行文件

Android程序的打包流程

1.打包资源文件,生成R.java文件

android-sdk/platform-tools目录下的aapt工具中

Resource.cpp文件中的buildResources()函数,该函数首先检查AndroidManifest.xml的合法性,然后使用makeFileResources()函数对res目录下的资源子目录进行处理,处理的,处理的内容包括资源文件名的合法性检查,向资源表table添加条目等,处理完后调用RecourceTable.cpp文件中的compileResourceFile()函数编译res与asserts目录下的资源并生成resources.arsc文件,该函数最后会调用parseAndAddEntry()函数生成R.java文件。

接下来调用compileXmlFile()函数对res目录的子目录下的xml文件分为进行编译,这样处理过的xml文件就简单的被加密了,最后将所有的资源与编译生成的resources.arsc文件以及加密过的AndroidManifest.xml文件打包压缩成resources.ap_文件

2.处理aidl文件,生成相应的Java文件

对于没使用到aidl的Android工程这一步可以跳过

aidl工具位于android-sdk/platform-tools目录下

工具解析接口定义文件并生成相应的Java代码供程序调用。

3.编译工程源代码,生成相应的class文件

调用javac编译工程src目录下所有的java源文件,生成的class文件位于工程bin/classes目录下。如果有native代码,也会使用Android NDK来编译。也有可能需要NDK编译C/C++代码,编译C/C++代码也可以提前到第一步或第二步

4.转换所有的class文件,生成classes.dex文件

android-sdk/platform-tools目录下的dx工具,将Java字节码转换为Dalvik字节码、压缩常量池、消除冗余信息等

5.打包生成APK文件

android-sdk/tools目录下的apkbuilder,是一个脚本文件,实际调用的是android-sdk/tools/lib/sdklib.jar文件中的co m.android.sdklib.build.ApkBuilderMain类。代码构建了一个ApkBuilder类,然后以包含resources.arsc的文件为基础生成apk文件,这个文件一般以ap_文件,接着调用addSourceFolder()函数往apk文件中添加资源,包括res目录与assets目录中的文件,添加完资源后调用addResourcesFromJar()函数往apk文件中写入依赖库,接着调用addNativeLibraries()函数添加工程libs目录下的Native库。最后调用sealApk()关闭apk文件。

6.对APK文件进行签名

一种是调试程序的签名,一种是符合要求的签名文件

7.对签名后的APK文件进行对齐处理

使用android-sdk/tools目录下的zipalign工具,将apk包进行对齐处理,使apk包中的所有资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快

Android程序的安装流程

4种安装方式

1.系统程序安装

由开机时启动的PackageManagerService服务完成,在启动时会扫描系统程序目录/system/app并重新安装所有程序。没有安装界面

2.通过Android市场安装

系统厂商的应用市场通过调用系统服务实现静默安装,也没有安装界面

3.ADB工具安装

通过adb install安装,没有安装界面

4.手机自带安装

调用Android系统的软件包packageinstaller.apk来完成安装,有安装界面

packageinstaller接收到Intent传递过来的apk文件信息,当PackageInstallerActivity启动时,会首先初始化一个PackageManager与PackageParser.Package对象,接着调用PackageUtil类的静态方法getPackageInfo()解析程序包的信息,如果这一步解析出错,程序就会失败返回,如果成功就调用setContentView()方法设置PackageInstallerActivity的显示视图,接着调用PackageUtil类的静态方法getAppSnippet()与initSnippetForNewApp()来设置PackageInstallerActivity的控件显示程序的名称与图标,最后调用initiateInstall()方法进行一些其他的初始化工作

重点有两个函数,一个是PackageUtil的getPackageInfo()方法,一个是initiateInstall()方法。

getPackageInfo是通过packageURL获取到apk文件的路径,然后构造了一个PackageParser对象,最后调用PackageParser对象的parserPackage()方法解析apk程序包。parserPackage首先调用parsePackageName()方法从AndroidManifest.xml文件中获取程序包名,接着构建了一个Package对象,接下来挨个处理AndroidManifest.xml文件中的标签,使用parseApplication()方法处理application标签,随后解析activity、receiver、service、provider并将它们添加到传递进来的Package对象的ArrayList中

initiateInstall()方法检测该程序是否已经安装,然后分别调用startInstallConfirm()显示安装界面或调用showDialogInner(DLG_REPLACE_APP)弹出替换程序对话框。startInstallConfirm()方法设置了安装与取消按钮的监听器,最后是onClick方法的按钮点击响应了,使用startActivity()方法启动了一个Activity:InstallAppProgress,在初始化onCreate()方法中最后调用了PackageManager的installPackage()方法来安装apk程序。installPackage是一个虚函数,PackageManagerService.java实现了它,installPackage()调用了installPackageWithVerfication()方法,首先验证调用者是否具有程序安装的权限,最后通过消息处理的方式调用progressPendingInstall()进行安装或替换。

安装与替换最终都会调用scanPackageLI()方法,其中会解析apk程序包,完成apk的依赖库检测、签名的验证、sharedUser的签名检查、更新Native库目录文件、组件名称的检查等工作,最后调Install类的对象的install()方法来安装程序。

dex文件格式

dex header 为dex文件头,指定dex文件的一些属性,记录其它6部分数据结构在dex文件中的物理偏移

string_ids 以下为索引结构区,其中各数据结构的偏移地址都是从header结构的stringIdsOff-classDefsOff字段的值指定的

type_ids

proto_ids

field_ids

method_ids

class_def

data 真实数据

link_data 静态链接数据区,目前始终为空

odex文件格式

是经过优化的dex文件

生成方式,一种是从apk程序中提取出来,与apk文件存放在同一目录且文件后缀为odex的文件,这类多是Rom的系统程序;另一种是dalvik-cache缓存文件,这类odex仍然以dex作为后缀,存放在cache/dalvik-cache目录下,保存的形式为“apk路径@apk名@classes.dex”,例如:“system@app@Calculator.apk@classes.dex”表示安装在system/app目录下Calculator.apk程序的odex文件

因为Dalvik虚拟机每次加载apk都需要从apk中读取class.dex文件,会耗费很多cpu时间,而采用odex方式优化的dex问津啊,已经包含了加载dex必须的依赖库文件列表,Dalvik虚拟机只需检测并加载所需的依赖库即可执行相应的dex文件,大大缩短了读取dex文件所需的时间