Android编译技术之编译基础

2,060 阅读6分钟

App的组成

1、AndroidManifest.xml:Apk的清单文件, 记录了 App 的名称、权限声明、所包含的组件等一系列信息.

2、classes.dex:它是由项目源码生成的 .class 文件经过进一步地转换而生成的 Android 系统可识别的 Dalvik Byte Code。并且,由于 Android 系统中的字节码和标准 JVM 中的字节码是有区别的,所以如果 App 中引用了第三方 jar 包的话,那么通常情况下它也会被包含在 classes.dex 中.

3、resources.arsc:资源索引表,包含编译后的二进制资源文件.

4、res 目录:未编译的资源文件.

5、asserts:额外建立的资源文件夹.

6、libs 目录:如果存在的话,存放的是 ndk 编出来的 so 库.

7、META-INF 目录:用于保存 App 的签名和校验信息,以保证程序的完整性。当生成 APK 包时,系统会对包中的所有内容做一次校验,然后将结果保存在这里。而手机在安装这一 App 时还会对内容再做一次校验,并和 META-INF 中的值进行比较,以避免 APK 被恶意篡改。其中包含如下 三个文件,如下所示:

  1. MANIFEST.MF(清单文件):

Name: 就是指定的文件

SHA-256-Digest: 将Name指定的文件经过SHA256后再base64编码得到到值

  1. CERT.SF文件:

开头处定义的SHA-256-Digest-Manifest 值是将MANIFEST.MF文件经过SHA-256后再base64后的值。

后面每一项也是对 MANIFEST.MF 文件中的每项再次进行SHA256数字签名并经过base64编码后的值。

  1. CERT.RSA文件:包含了公钥、加密算法等信息。首先是对前一步生成的 CERT.SF文件进行 SHA256进行数字签名,然后RSA算法加密,私钥使用开发者生成的私钥,然后在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。

下面我们来看看,如果apk文件被篡改后会发生什么。

首先,如果apk包中的任何文件被改动,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是验证失败,程序就不能成功安装。

其次,如果你对更改的过的文件相应的算出新的摘要值,然后更改MANIFEST.MF文件里面对应的属性值,那么必定与CERT.SF文件中算出的摘要值不一样,照样验证失败。

最后,如果继续计算MANIFEST.MF的摘要值,相应的更改CERT.SF里面的值,那么数字签名值必定与CERT.RSA文件中记录的不一样,还是失败。

那么能不能继续伪造数字签名呢?不可能,因为没有数字证书对应的私钥。

所以,如果要重新打包后的应用程序能再Android设备上安装,必须对其进行重签名。

App的编译打包流程

Android官方打包流程如下图

打包流程可简述为如下 八个步骤:

  1. 首先,.aidl(Android Interface Description Language)文件需要通过 aidl 工具转换成编译器能够处理的 Java 接口文件
  2. 同时,资源文件(包括 AndroidManifest.xml、布局文件、各种 xml 资源等等)将被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及之后使用 AAPT2 替代了 AAPT)处理为最终的 resources.arsc,并生成 R.java 文件以保证源码编写时可以方便地访问到这些资源
  3. 然后,通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,最终它们会统一被编译成 .class 文件
  4. 因为 .class 并不是 Android 系统所能识别的格式,所以还需要通过 dex 工具将它们转化为相应的 Dalvik 字节码(包含压缩常量池以及清除冗余信息等工作)。这个过程中还会加入应用所依赖的所有 “第三方库”
  5. 下一步,通过 ApkBuilder 工具将资源文件、DEX 文件打包生成 APK 文件
  6. 接着,系统将上面生成的 DEX、资源包以及其它资源通过 apkbuilder 生成初始的 APK 文件包
  7. 然后,通过签名工具 Jarsigner 或者其它签名工具对 APK 进行签名得到签名后的 APK。如果是在 Debug 模式下,签名所用的 keystore 是系统自带的默认值,否则我们需要提供自己的私钥以完成签名过程
  8. 最后,如果是正式版的 APK,还会利用 ZipAlign 工具进行对齐处理,以提高程序的加载和运行速度。而对齐的过程就是将 APK 文件中所有的资源文件距离文件的起始位置都偏移4字节的整数倍,这样通过 mmap 访问 APK 文件的速度会更快,并且会减少其在设备上运行时的内存占用

编译 --> DEX --> 打包 --> 签名 --> 对齐

用工具链的方式展示过程:

用文字的方式描述一下:

首先,需要参与编译的资源文件:

  • 资源文件res目录下文件
  • AIDL接口文件
  • 源代码文件
  • 第三方资源包: a.java(AAR/JAR java类)文件和 b.java(.so 非java类)文件

  1. aapt工具编译res资源文件, 把大部分xml文件编译成二进制文件,但不包含图片资源,生成 R.java和resources.arsc 两个文件,如果项目中包含 .aidl文件,也会编译生成相应但 .java文件。
  2. 将所有以java结尾的文件通过 javac 工具编译成以 class 结尾的文件
  3. 将所有以class结尾的文件通过 dx 工具生成 class.dex文件 如果分包的话 会多几个dex文件
  4. 将资源文件,dex文件和第三方的资源文件(非java类文件 如so文件等), 通过apkbuilder工具生成未签名的apk包
  5. 将apk包通过 jarsigner进行签名, debug用模式用默认签名,release用开发者签名
  6. 对apk中未压缩资源(图片 视屏等)通过 zipalign 工具进行对齐, 让资源按照4字节对齐。可以让资源的访问速度加快

从上面扩展出的几个问题:

  1. Android是如何通过R文件引用到真正的资源文件?

答:

aapt工具对每个资源文件生成唯一ID, 保存在R.java文件中,资源ID是一个无符号4字节整数,在R.java文件中用16进制形式表示。 其中最高的字节表示 Package ID, 下一个字节表示Type ID, 最低2字节表示 Entry ID. 资源ID和资源路径的对应关系就存储在 resource.arsc文件中

  1. 打包流程中的对齐是什么意思,有什么作用?

答:

让资源按照4字节方式进行对齐, 这样如果每个资源的位置都是上个资源位置的 4 * n, 那么访问资源就不用遍历的方式,这样可以大大的提升资源访问速度。

  1. 为什么将 .xml 文件编译成二进制?

答:

第一 占用空间更小, 因为所有xml文件的标签, 属性名称 属性值和内容都被搜集到一个资源池中处理,并且会去重,原来使用字符串的地方会被替换成一个索引到资源池的整数值,从而降低了存储空间。

第二 解析效率高, 二进制格式的 xml 文件解析更快,这是由于文件中不包含字符串,可以减少解析字符串所带来的开销,从而提高解析效率。




参考链接

juejin.cn/post/684490…

blog.csdn.net/q1183345443…

www.jianshu.com/p/019c73505…