APK文件结构详解

前言

最近有业务需求,须要向已经打包的APK里面注入渠道(channel)信息,方便APK下载安装以后进行渠道归因。向APK里面注入渠道信息已经有比较成熟的方案美团walle。walle的强大和实现原理本文再也不赘述。为了理解walle的代码,而且在出现异常case的时候,可以本身解决。必需要对APK文件的结构了如指掌。html

所以,本文的目的就是以一个简单的利用walle向APK里面注入渠道号为例。带你了解APK里面的各类细节,相信读者在看完此篇文章以后,再阅读walle的代码的时候必定豁然开朗。java

ZIP和APK文件格式概览

ZIP文件格式概览

APK文件本质上是一个ZIP文件,所以在了解APK的格式以前必须先了解ZIP文件的格式。详情参见zip文件格式详解android

ZIP文件的总体格式以下图所示,主要由三部分组成git

  • 数据区(Contents of ZIP entries):存储文件压缩内容
  • 中央目录区(Central Directory Header):存储zip文件里面包含的全部目录
  • 中央目录结尾记录(End of Central Directory Record:ECDR):存储zip文件的总体信息

zip文件的总体格式

其中,中央目录结尾记录对于理解本文最为重要。最好看完zip文件格式详解 再继续往下阅读。此区域里面有一个值核心目录偏移地址很是的重要,如下图为例的话,核心目录的起始地址就是0x2D(注意:是地址,不是数值)github

APK文件格式概览

APK在签名(V2/V3签名)以后,会在数据区和中央目录区之间插入APK签名区(APK Signing Block)markdown

APK签名区的组成结构以下图所示工具

用表格表示以下图所示,其由4部分组成oop

  1. Block长度:APK签名区域总长度,包含后面3部分长度的总和
  2. ID-Value序列:后面会详细讲解
  3. Block长度:同1的值彻底相同
  4. 魔数:APK Sig Block 42,hex大端表示的话就是:41 50 4b 20 3 69 67 20 42 6c 6f 63 6b 20 34 32

ID-Value序列里面存储了ID-Value列表,ID-Value序列的结构以下图所示。google

列表的每个Item里面由3个元素组成(8字节长度+4字节ID+内容)。其中APK的签名信息就是其中一项item,ID为0x7109871a。序列里面的每个Item的结构以下图所示:spa

至于APK是如何计算签名签名信息保存在Item里面,以及Android系统是如何校验APK完整性的,详情参考APK签名机制之——V2签名机制详解walle的实现原理就是向ID-Value列表里面插入一个Item,因为APK安装的时候并不对插入的Item作校验,所以可以保证APK能校验成功的同时还能带上插入的信息

至此,咱们对APK文件的基本结构有了一个总体的认识。下面,我将以上述的实例APK为例子,经过二进制分析如何一步一步提取出walle注入的ID=0x71777777的渠道信息

例子简介

基于wall,向APK里面注入渠道号信息。在APK打包的时候,向APK里面注入一下信息。

APK运行的时候,能够成功提取出渠道号信息

实例分析

实例分析前,咱们再回顾一下APK的总体格式(这张图对后面步骤的理解很重要)

从APK里面寻找存ID=0x71777777的渠道信息的总体过程以下:

  1. 肯定中央目录地址(中央目录地址恰好是APK签名地址的尾地址)
  2. 根据中央目录地址和APK签名块的长度肯定ID-Value序列的首地址和尾地址
  3. 分析ID-Value块的结构,找到ID=0x71777777的内容部分,找到的内容就是咱们注入的信息

注意,在阅读二进制文件的时候都要把实际的数字转成小端来看

ZIP文件是按照小端存储的(除特殊部分,好比说magic部分),好比说0x7109871A,实际存储16进制存储为:1A 87 09 71。关于大端和小端的区别能够参考大端和小端

一、肯定中央目录的起始地址

中央目录结构以下所示,zip文件格式详解 里面详细介绍了中央目录结尾记录的结构

(1)咱们首先根据ECDR的魔数0x06054b50(hex:50 4b 05 06)肯定ECDR部分的起始地址为0xBD396

(2)日后偏移16个地址(参考上图),便可计算出中央目录起始地址为0x0b4f18

二、计算ID-Value块起始地址

咱们再简单回顾一下APK签名块的格式

(1)咱们首先跳转到0x0b4f18地址所在位置

  • 从右边的预览里面咱们能够看到地址的前面恰好就是签名块结尾的魔数APK Sig Block 42
  • 能够证实此地址恰好就是中央目录起始地址

(3)往前16个地址(魔数长度)取8个字节(block长度占8字节)既为APK签名块的长度

  • 8405000000000000 16进制小端为 0x0584

(4)所以从APK Sig Block 42的尾地址(0xb4f17)往前0x0584个地址,就是ID-Value部分的起始地址

  • 能够看出前8个字节恰好也是8405000000000000,两个长度Block的值相同,和预期相符合
  • 最下面标红部分能够看出计算出来的长度恰好是0x584
  • ID-VALUE的起始地址=0xb4f17-0x584+1=0xb4994
  • 最下面是开始地址(0xb4994)和结束地址(0xb4f17)

三、解析出ID=0x71777777的注入信息

再复习一下ID-Value序列Item每个Item的结构

根据步骤二咱们能够得知ID-Value部分是从(43 05 00 到6E 22 7D,上图的两个红框之间)。理论上,咱们根据上述Item的结构,顺序从ID-Value序列的首地址解析到尾地址便可找出咱们的注入的信息。为了方便起见,咱们直接在这部分搜索Id=0x71777777(hex: 77 77 77 71)的内容。能够看出

  • 长度:00 00 00 00 00 00 00 19=25 (长度是25字节)
  • ID:71 77 77 77 (咱们自定义的)
  • 存入内容: {"channel":"meituan"} 21字节
  • 21+4=25,恰好等于长度的值

至此,咱们就成功的经过分析APK的二进制数据,获得了walle注入的数据。固然,walle注入的过程也是相似的,很是的简单。除此以外walle还考虑了V3对齐的问题

当你通过上述步骤分析以后,再去看walle的代码,你会发现,妈的!原来这么简单!!

参考文献

相关文章
相关标签/搜索