# 0x1 前言
最近分析两个apk,将后缀改为zip后,解压提示需要密码:
`7z l -slt *.zip`可以查看zip包内文件加密情况,大部分文件都如下图未标记加密:# 0x2 ZipCenOp
```java -jar ../ZipCenOp/ZipCenOp.jar r **.zip
success!```# 0x3 伪加密原理
ZipInfo)都有一个 `flag_bits`通用标志位(general purpose bit flag),通常 16 位。```flag_bits = 0b0000000000000001 # 第0位为1,表示加密```而ZIP文件采用多段式结构,包含文件头、数据、目录等部分:
* 本地文件头 (Local file header) + 文件数据
* 中央目录 (Central directory)
* 中央目录结束记录 (End of central directory record)
## 0x31 本地文件头格式 (Local file header)
每个文件在ZIP中都有一个本地文件头,结构如下(偏移量从0开始):
```
偏移 大小 字段名 说明0 4 local file header signature = 0x04034b50 (本地文件头签名)
4 2 version needed to extract 解压所需版本
6 2 flag_bits 通用目的比特标志
8 2 compression method 压缩方法
10 2 last mod file time 最后修改时间
12 2 last mod file date 最后修改日期
14 4 crc-32 CRC-32校验值
18 4 compressed size 压缩后大小
22 4 uncompressed size 压缩前大小
26 2 file name length 文件名长度
28 2 extra field length 额外字段长度
30 变长 file name 文件名
30+n 变长 extra field 额外字段
```## 0x32 中央目录文件头 (Central Directory File Header)结构:
```
偏移 大小 字段名 说明 0 4 central file header signature = 0x02014b50 4 2 version made by 创建ZIP的版本 6 2 version needed to extract 解压所需版本 8 2 **general purpose bit flag** **flag_bits字段** 10 2 compression method 压缩方法 12 2 last mod file time 最后修改时间 14 2 last mod file date 最后修改日期 16 4 crc-32 CRC-32校验值 20 4 compressed size 压缩后大小 24 4 uncompressed size 压缩前大小 28 2 file name length 文件名长度 30 2 extra field length 额外字段长度 32 2 file comment length 文件注释长度 34 2 disk number start 文件开始的磁盘号 36 2 internal file attributes 内部文件属性 38 4 external file attributes 外部文件属性 42 4 relative offset of local header 本地头相对偏移 46 变长 file name 文件名 ... 变长 extra field 额外字段 ... 变长 file comment 文件注释
```
未标记加密的中央目录文件头flag_bits为0808
标记加密的中央目录文件头flag_bits为0801
## 0x33 flag_bits字段详解
```
位置:本地文件头偏移6字节处,2字节(16位) 同样存在于中央目录文件头中(偏移8字节处) 比特位 含义 说明 0 加密文件 1=文件已加密 1-2 压缩选项 1=最大压缩,2=最快压缩 3 数据描述符 1=存在数据描述符(重要的标志位) 4 强化的deflate 1=使用增强型deflate压缩 5 补丁数据 1=文件是补丁数据集 6 强加密 1=使用强加密(如DES) 7 未使用 保留位 8 未使用 保留位 9 文件名使用UTF-8编码 1=文件名使用UTF-8编码 10 加密中心目录 1=中央目录加密 11-15 未使用 保留位```
```#!/usr/bin/env python3
"""
unfake_apk_crypto.py —— 一键去掉 APK 伪加密
用法:
python3 unfake_apk_crypto.py fake.apk [clean.apk]
如果省略第二个参数,则原地覆盖。
"""
import os
import sys
import zipfile
import shutil
def clean_pseudo_encryption(src_apk: str, dst_apk: str | None = None) -> str:
"""返回生成的新 apk 路径"""
if dst_apk is None or src_apk == dst_apk:
# 原地修改:先写到临时文件,最后再覆盖
tmp = src_apk + ".tmp"
clean_pseudo_encryption(src_apk, tmp)
shutil.move(tmp, src_apk)
return src_apk
with zipfile.ZipFile(src_apk, "r") as zin, \
zipfile.ZipFile(dst_apk, "w", compression=zipfile.ZIP_STORED) as zout:
for info in zin.infolist():
# 把加密标志位清 0
info.flag_bits &= ~0x01
# 按原数据写出
zout.writestr(info, zin.read(info.filename))
return dst_apk
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python3 unfake_apk_crypto.py <fake.apk> [clean.apk]")
sys.exit(1)
src = sys.argv[1]
dst = sys.argv[2] if len(sys.argv) > 2 else None
out = clean_pseudo_encryption(src, dst)
print(f"伪加密已去除 -> {out}")
```