博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python 后台基于 PackerNg 格式动态生成 APK 渠道包
阅读量:6947 次
发布时间:2019-06-27

本文共 2458 字,大约阅读时间需要 8 分钟。

hot3.png

本文代码的原理基于 git@github.com:mcxiaoke/packer-ng-plugin.git 项目。

该项目用于向打好的 APK 包快速写入渠道信息,因为是直接在 APK 的尾巴上加数据而没有解包打包的过程,故速度较快。本文实现的是另一种需求,即后台实时给 APK 加数据,然后返回给前端下载。

比如邀请码这样没法提前写入的数据,就适合实时生成。得到的好处就是用户在注册时可以免去填邀请码这一步。

但显然这会带来带宽的压力,依旧去实现它是考虑到了实际分享应用的场景:绝大多数是在 微信、QQ 等平台内传播,都会跳转到应用宝等市场上去。因此这种实时打包流量其实很低,除非有人非要别扭着在微信里使用【在浏览器中打开】按钮来下载,否则本接口是不会被访问到的。

所以这个实现其实一点实际用处都没有 →_→

原理

APK 使用的是 ZIP 的打包格式,所以它的结尾定义了两个字段 —— Comment Len, Comment,Comment 是个变长字段,长度写在 Comment Len 里,占两个字节。

ZIP 的详细解释参见:

https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html

APK 在安装后,还会在系统里留下一个副本,安装过的应用通过读取这个副本的 Comment,就可以获得这个数据。

PackerNg

开头的 github 项目便实现了这个功能,通过 Java 和 Python 两种语言。本文重写了 Python 的版本,使其可以更改 Comment,以便实现动态加尾巴的功能。

PackerNg 的 comment 格式 是 data + data length(2 bytes) + !ZXK!。最后的 !ZXK! 是个 magic 字段,用于判断该文件是否有有效的尾巴,再通过这个字段之前的两个字节来获取偏移量,读取尾巴。

但我感觉这个字段是可选的,如果直接以偏移量结尾,我们便可以通过检查最后两个字节是不是 "\x00\x00" 来判断该文件有没有加尾巴。可能的混淆情景仅为,该文件有一个非我们加的尾巴,而这可以通过人为约束来控制。

加尾巴代码

APK_COMM_LEN = 2  # bytesAPK_PACK_FMT = '

流式加尾巴代码

因为 Comment 信息位于 APK 的尾部,而我司渠道包又非常多且迭代较快,我没有选择在服务器本地保存 APK 包,而是实时从 cdn 取数据流,然后在最后一段上进行更改,即实现成了一个代理。

def inv_apk_wrapper(url, inv_code):	r = requests.get(url, timeout=300, stream=True)	# 生成符合要求的 chunk_size	content_length = json.loads(r.headers['content-length'])	tail_length = content_length % APK_BASE_CHUNK_SIZE	if tail_length < APK_COMM_SAFE_LEN:		less = APK_COMM_SAFE_LEN - tail_length		chunk_num = int(math.ceil(content_length / float(APK_BASE_CHUNK_SIZE)))		chunk_size = APK_BASE_CHUNK_SIZE - int(math.ceil(less / (chunk_num - 1.0)))	else:		chunk_size = APK_BASE_CHUNK_SIZE	new_apk_gen = inv_apk_generator(r.iter_content(chunk_size), inv_code)	# update content-length	new_headers = dict(r.headers)	new_headers['content-length'] = str(content_length + len(', "inv_code": "{inv_code}"'.format(inv_code=inv_code)))	return Response(new_apk_gen, headers=new_headers)def inv_apk_generator(apk_iterator, inv_code):	data = []	while True:		try:			data.append(next(apk_iterator))		except StopIteration:  # 确认到尾部, 添加 inv_code			if data:				content = BytesIO(data[0])				comm_info = get_apk_comm_info(content)				comm_info['inv_code'] = inv_code				comm_len, comment = gen_apk_comment(comm_info)				content = set_apk_comment(content, new_comm_len=comm_len, new_comment=comment)				content.seek(0, 0)				yield content.read()			raise StopIteration		if len(data) >= 2:			yield data[0]			data = data[1:]

注意这种接口因为太占 I/O,最好不要和正常业务部署在一起,或者,最好不要部署。。。

转载于:https://my.oschina.net/lionets/blog/718930

你可能感兴趣的文章
建立cover组 成员有cover01 cover02 建立team组 成员有team 01 team02 建立user组 成员有user...
查看>>
linux pxe 系统自动化安装
查看>>
iOS游戏开发有奖征文
查看>>
控制台读写
查看>>
LVS+keepalived负载均衡实战
查看>>
使用 IntraWeb (17) - 基本控件之 TIWRadioButton、TIWRadioGroup、TIWCheckBox
查看>>
KVM虚拟化搭建及其KVM中LVM扩容
查看>>
管理磁盘和文件系统
查看>>
CSS解决高度自适应问题
查看>>
鼠标css样式:cursor
查看>>
pdf password recovery remove去除pdf文件密码
查看>>
使用.NET连接Sybase数据库的几种方法
查看>>
myeclipse6.5安装 svn插件方法
查看>>
Spring AOP 实现原理
查看>>
肠子的小心思(三):通往消化道的入口也很神奇
查看>>
服务器负载突然飙高事件
查看>>
原来还可以这样坑人的!!切勿恶作剧!
查看>>
Memcache简介
查看>>
day18--linux下gzip、bzip2、zip、xz三种压缩工具的介绍
查看>>
Oracle 删除主键
查看>>