- Android V2签名包组成:
- 1.被压缩后的数据(.dex, res等压缩数据, 开始位置必为0,假设长度m)
- 2.签名块(8+n+8+16)
- 3.中央目录,查找第1块数据的目录(假设长度为x, 尾部必为0x06054b50)
- 4.中央目录尾部(开始就是0x06054b50,长度不固定,因为有comment注释是不定长的)
解析APK包-从包尾开始往前找
1. 寻找中央目录尾部(0x06054b50)
伪代码
lastIndex = apk.size -1
// 每次读4个字节
flagIndex = lastIndex
for(i = lastIndex-4; i>=0; i--) {
curByte = read[i, i+4]
if (curByte == 0x06054b50) {
flagIndex = i
break
}
}
找到标记位0x06054b50后,开始解析中央目录尾部组成:
- 1.标记位0x06054b50,4Byte
- 2.当前磁盘编号,2Byte
- 3.中央目录开始位置的磁盘编号,2Byte
- 4.该磁盘上记录的核心目录数,2Byte
- 5.中央目录结构数,2Byte
- 6.中央目录大小,4Byte
- 7.中央目录开始位置,4Byte
- 8.注释长度,2Byte
- 9.注释,不定长
2.读取中央目录开始位置
伪代码
flagIndex = 19999 // 假设为19999,为0x06054b50的索引,包含自己
readStartIndex = flagIndex + 4 + 2 + 2 + 2 + 2 + 4 // 对照上面寻找的位移
centerDirStartIndex = read[readStartIndex, readStartIndex + 4]
找到中央目录开始位置之后,往前就是签名块了,开始解析签名块组成:
- 1.block长度,8Byte
- 2.签名数据,不定长,格式是:Length(8Byte,long) + ID(4Byte,int) + Value(不定长)
- 3.block长度,8Byte
- 4.魔数,16Byte ( 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32)
3.读取签名块大小
伪代码
centerDirStartIndex = 18888 // 假设为18888,为中央目录起点,也是签名的尾部Index
readStartIndex = centerDirStartIndex - 16 - 8 // 对照上面找位移
resignBlockSize = read[readStartIndex, readStartIndex + 8]
4.读取签名数据
解析签名数据块格式(长度为4096Byte的倍数): 签名数据块由key-value对组成
- V2签名的key为固定ID: 0x7109871a
- 如果V3签名开启的话,V2签名数据块后面接着V3的签名数据块
- 前面签名数据块(V2 || V3 || (V2 && V3))长度不足4096Byte的倍数就会用空白数据块(固定ID为:0x42726577)补够,因此空白数据块有可能会没有,写渠道的时候不可依赖这块空白块
伪代码
resignBlockEndIndex = centerDirStartIndex // 签名结尾Index也就是中央目录开始Index
resignBlockStartIndex = resignBlockEndIndex - resignBlockSize
resignDataStartIndex = resignBlockStartIndex - 8
resignDataEndIndex = resignBlockEndIndex - 16 - 8
curIndex = resignDataStartIndex
while(curIndex < resignDataEndIndex) {
resignData = read[curIndex, curIndex + 4]
}
由于签名数据是以键值对存放,那么我们也可以把自己的信息(比如:渠道信息)以键值对写入到apk中
16进制查找流程:
使用winHex工具查看
- 找中央目录结束标记位0x06054b50:(注意0x0ff8,在 zip 文件中是这样存放的:f8 0f)
往后加12Byte,找到中央目录开始位置(0x00434FF8):
跳转0x00434FF8:
前面的16Byte就是魔数了41 50 4B 20 53 69 67 20 42 6C 6F 63 6B 20 34 32,即为签名块结束的标记位, 魔数前面的F8 1F 00 00 00 00 00 00就是签名数据块的大小,那么签名的开始的Index=0x00434FF8-0x00001FF8=0x00433000
可以看到签名块前面也是签名块的大小记录