Android V2签名随手记

331 阅读3分钟

image.png

  • 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
    }
}

image.png 找到标记位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]

image.png

找到中央目录开始位置之后,往前就是签名块了,开始解析签名块组成:

  • 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]

image.png

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工具查看

  1. 找中央目录结束标记位0x06054b50:(注意0x0ff8,在 zip 文件中是这样存放的:f8 0f)

image.png

往后加12Byte,找到中央目录开始位置(0x00434FF8):

image.png

跳转0x00434FF8:

image.png

image.png

前面的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

image.png

image.png

可以看到签名块前面也是签名块的大小记录