最近有业务需求,须要向已经打包的APK里面注入渠道(channel)信息,方便APK下载安装以后进行渠道归因。向APK里面注入渠道信息已经有比较成熟的方案美团walle。walle的强大和实现原理本文再也不赘述。为了理解walle的代码,而且在出现异常case的时候,可以本身解决。必需要对APK文件的结构了如指掌。html
所以,本文的目的就是以一个简单的利用walle向APK里面注入渠道号为例。带你了解APK里面的各类细节,相信读者在看完此篇文章以后,再阅读walle的代码的时候必定豁然开朗。java
APK文件本质上是一个ZIP文件,所以在了解APK的格式以前必须先了解ZIP文件的格式。详情参见zip文件格式详解 。android
ZIP文件的总体格式以下图所示,主要由三部分组成git
其中,中央目录结尾记录对于理解本文最为重要。最好看完zip文件格式详解 再继续往下阅读。此区域里面有一个值核心目录偏移地址
很是的重要,如下图为例的话,核心目录的起始地址就是0x2D(注意:是地址,不是数值)github
APK在签名(V2/V3签名)以后,会在数据区和中央目录区之间插入APK签名区(APK Signing Block)markdown
APK签名区的组成结构以下图所示工具
用表格表示以下图所示,其由4部分组成oop
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里面注入一下信息。
0x71777777
{"channel":"meituan"}
APK运行的时候,能够成功提取出渠道号信息
实例分析前,咱们再回顾一下APK的总体格式(这张图对后面步骤的理解很重要)
从APK里面寻找存ID=0x71777777的渠道信息的总体过程以下:
注意,在阅读二进制文件的时候都要把实际的数字转成小端来看
ZIP文件是按照小端存储的(除特殊部分,好比说magic部分),好比说0x7109871A,实际存储16进制存储为:1A 87 09 71。关于大端和小端的区别能够参考大端和小端
中央目录结构以下所示,zip文件格式详解 里面详细介绍了中央目录结尾记录的结构
(1)咱们首先根据ECDR的魔数0x06054b50(hex:50 4b 05 06)肯定ECDR部分的起始地址为0xBD396
(2)日后偏移16个地址(参考上图),便可计算出中央目录起始地址为0x0b4f18
咱们再简单回顾一下APK签名块的格式
(1)咱们首先跳转到0x0b4f18地址所在位置
APK Sig Block 42
(3)往前16个地址(魔数长度)取8个字节(block长度占8字节)既为APK签名块的长度
(4)所以从APK Sig Block 42的尾地址(0xb4f17)往前0x0584个地址,就是ID-Value部分的起始地址
再复习一下ID-Value序列Item每个Item的结构
根据步骤二咱们能够得知ID-Value部分是从(43 05 00 到6E 22 7D,上图的两个红框之间)。理论上,咱们根据上述Item的结构,顺序从ID-Value序列的首地址解析到尾地址便可找出咱们的注入的信息。为了方便起见,咱们直接在这部分搜索Id=0x71777777(hex: 77 77 77 71)的内容。能够看出
至此,咱们就成功的经过分析APK的二进制数据,获得了walle注入的数据。固然,walle注入的过程也是相似的,很是的简单。除此以外walle还考虑了V3对齐的问题
当你通过上述步骤分析以后,再去看walle的代码,你会发现,妈的!原来这么简单!!