Re: 用Go实现渠道打包工具walle
Walle,是美团点评技术团队针对 Android Signature V2 Scheme
签名过的APK的一个多渠道打包工具
近来恰巧在学习Go,就想着把它“翻译”成Go,权当作练手,同时还能给“包管理后台”用用。
Walle 的工作原理
想要把一个工具用另一种语言实现,首先得明白它的原理。
其实原理官方已经解释的很详细了,这里就不再详细缀述,主要列出一些关键的点:
APK 签名的本质
APK 文件其实是一个 ZIP(Jar包),所谓签名其实就是对包或者包中的文件做签名。
V1 其实就是 Jar 签名,“签名”要保护的是包中的文件不被修改,并不保护包本身(如ZIP Comment);V2 则是针对V1的缺陷,在V1的基础上将包本身视为Blob再做一次签名。
这就要求 V2 需要在不破坏、不影响 ZIP 文件格式(能被普通的ZIP工具解压),不污染V1签名正确性(APK能被老Andriod系统识别)的前提下写入到包中。
(图来自https://source.android.com/security/apksigning/)
故而需要针对性的在不伤害 ZIP 文件格式的前提下做一些“魔改”,将 V2 签名数据块插入到 ZIP 的 Central Directory 之前。
V2 签名之后的APK
APK 被分为 4 部分:
- Contents of ZIP entries (从文件头开始直到
APK Signing Block
) - APK Signing Block
- ZIP Central Directory
- ZIP End of Central Directory
1、3、4的内容受到APK Signing Block
保护,准确的说是受 APK Signing Block
中的签名信息保护
APK Signing Block 格式
- Signing Block 字节数 (不含自身计数) (uint64)
- “ID-value” 对序列:
- 此“ID-value” 长度 (uint64)
ID
(uint32)value
(可变长度: “ID-value” 长度 - 4个字节)
- Signing Block 字节数 (与前面的数值相同) (uint64)
magic
“APK Sig Block 42” (16 个字节)
为向后兼容考虑,ID-value
对设计成了可以存在多个。
V2 签名是数据块中的一个ID-value
对,其ID
为 0x7109871a
,值即为“签名信息”(signed data)。
在验证 V2 签名过程中,会跳过验证器不认识的 ID
。
插入渠道信息
从上面的原理可以得到结论APK Signing Block
中可以增加一个ID-value
来存储渠道,且不会破坏原有签名信息,即毋须重签名做到插入渠道
实现
具体的代码已经放到了github上,此处就不一一罗列了 https://github.com/yrom/walle-go
其中绝大部分代码参考了 https://android.googlesource.com/platform/build/+/android-7.1.2_r27/tools/signapk (毕竟开头说的就是“翻译” <(▰˘◡˘▰)>)
关键代码在 src/walle
下面,并基于包walle
实现了一个命令行工具。
对 Go 的几点总结
以下都是我本人的意见,如有谬误,欢迎指出。
- 用 Go 写一个命令行工具挺方便的,编译速度快,执行效率也还不错(就我这个新手写的代码而言,应该还有优化空间)
- Go 的数值类型转换没有“隐式转换”这一说,需要手动强转。我认为对于有大量数值运算的代码来说会很不方便(也许有我不知道的解决方案)
- Go 同时有“函数式”(Functional)和“面向对象”(OO)两个编程思想的体现,个人觉得使用“手感”略高于 Java 8,但不如 Kotlin。它没有
Class
,也没有显式的implements
关键字,对于我这样的新手来说会犯迷惑:这是什么类型,它为何可以这样用,难以置信! - Go 不像Java,不给直接用系统线程,而是用了一个
goroutine
的实现,既类似“线程”而名字又像个“协程”。使用上形似Java中向线程池提交一个Runnable。 - 学会使用Go是一回事,深入理解Go又是另一回事了。。。
EOF.
Author: Yrom
Link: https://yrom.net/blog/2017/08/28/re-implement-tool-walle-in-go/
License: 知识共享署名-非商业性使用 4.0 国际许可协议