简介:这份资源是面向PC端微信小程序逆向分析场景的wxapkg解密工具包,主要解决微信小程序加密包无法直接解包查看的问题,适合具备一定Python基础、从事小程序安全研究或爬虫分析的技术人员使用。压缩包共6个文件,以Python脚本为核心,辅以txt说明、md文档、license及gitignore等配置类文件,整体仅5KB,轻量易部署。脚本在PC微信2.9.5.41版本上测试可用,通过指定微信小程序ID、加密包路径与输出路径即可完成解密,并支持自定义iv与salt参数,若后续微信更新导致按键参数变化,可自行替换适配。使用前需定位到WeChat Files下的Applet目录,找到目标wxapkg文件及其父级目录中的APPID。目前已有170人学习关注,对于需要研究小程序包结构、提取资源或做安全分析的读者,这份脚本提供了可直接运行的解密思路与命令行工具,具备一定参考价值。
1. PC微信小程序wxapkg解密:从加密包到可读源码的完整路径
PC 端微信缓存下来的小程序包,后缀是.wxapkg,直接拿十六进制编辑器打开,头部往往是一串看不懂的二进制,而不是预期的文件目录结构。这就是很多人第一次接触「PC微信小程序wxapkg解密」时的真实场景:明明文件就在AppData目录里躺着,却打不开、搜不了、改不了。wxapkg 是微信小程序的打包格式,PC 端微信在落盘时会对包体做一层加密处理,尤其是较新版本的微信,加密头、异或密钥、分包结构都会影响后续解析。解密的目标很明确——把加密的 wxapkg 还原成标准的、能被解包工具识别的明文包,进而提取出app-service.js、app-config.json、WXML、WXSS 等资源,用于代码审计、接口分析、性能排查或二次学习。这套流程适合做小程序逆向的移动安全从业者、需要定位线上问题的前端工程师,以及想搞清楚小程序运行机制的技术人。下面按「先搞懂包结构,再动手解密,最后避坑」的顺序讲透。
2. wxapkg 的文件结构与 PC 端加密逻辑
2.1 明文 wxapkg 的头部长什么样
标准未加密的 wxapkg 结构非常朴素,本质是一个带索引的归档文件。开头 4 字节是魔数0xBE加三个保留字节,紧接着 4 字节是文件信息段长度,再往后是文件索引区,最后才是真正的文件内容。索引区里每一项记录文件名长度、文件名、文件内容偏移和文件大小。理解这个布局是判断「包到底有没有被加密」的第一步。
用 Python 读一下头部就能快速判断:
import struct def peek_wxapkg(path): with open(path, 'rb') as f: head = f.read(16) # 第一个字节是魔数,明文包通常是 0xBE magic = head[0] # 第 5-8 字节是索引段长度(小端) info_len = struct.unpack('<I', head[4:8])[0] print(f"magic=0x{magic:02X}, info_len={info_len}") return magic, info_len peek_wxapkg("example.wxapkg")逻辑说明:head[0]是魔数,明文包应为0xBE;head[4:8]按小端解析出索引段长度。如果魔数不是0xBE,或者info_len大得离谱(超过文件本身大小),基本可以判定这个包被加密或做了头部混淆。参数上,<I表示小端无符号 32 位整数,这是 wxapkg 所有长度字段的统一字节序,搞错字节序会得到完全错误的偏移。
2.2 PC 微信为什么要加密,加密加在哪
PC 端微信把小程序包缓存到本地时,出于防止随意篡改和资源保护的考虑,会对包体做处理。常见做法有两类:一类是在文件头部追加一段自定义头,真正的 wxapkg 数据从某个偏移开始;另一类是对整个包体按固定密钥做异或(XOR)运算。较新版本的 PC 微信(4.x 系列)在数据库和缓存文件上的加密策略都有调整,wxapkg 也不例外,这也是为什么老教程里的「一键解密脚本」在新版本上经常翻车。
判断加密类型,可以对比文件大小和头部特征。如果文件开头是一段规律性很强的重复字节,多半是异或加密;如果开头是一段长度固定的自定义头,后面才出现0xBE,那就是偏移型加密。实操中我一般先用xxd看前 64 字节:
xxd -l 64 example.wxapkg如果在前 64 字节里能搜到be开头的疑似魔数,就记录下它的偏移,后续从这个偏移开始截取即可。如果整段都是乱码,没有明显魔数,就要考虑异或还原。
2.3 定位 PC 微信的小程序缓存目录
解密的前提是先找到包。PC 微信的小程序缓存通常藏在用户目录下的Applet相关文件夹里,路径随微信版本变化。常见做法是直接在微信数据目录里按扩展名搜索:
# Windows 下在微信文档目录里找所有 wxapkg find "/c/Users/<你的用户名>/Documents/WeChat Files" -name "*.wxapkg" 2>/dev/null逻辑说明:find按文件名匹配*.wxapkg,2>/dev/null屏蔽权限报错。找到的包通常按小程序 AppID 分目录存放,一个 AppID 目录下可能有主包和多个分包。参数上,-name支持通配符,如果目录层级很深,可以加-maxdepth限制搜索深度来提速。注意不同微信版本目录名可能是Applet、applet或带版本号的子目录,找不到时优先确认微信版本和登录账号对应的数据目录。
3. 动手解密:从加密包还原出可解包的明文
3.1 偏移型加密的截取还原
偏移型加密最直接,找到真正 wxapkg 数据的起始偏移,把前面的自定义头丢掉即可。写个脚本自动扫描魔数位置:
def strip_custom_header(src, dst): with open(src, 'rb') as f: data = f.read() # 从前往后找第一个 0xBE 且后续长度合理的偏移 for i in range(len(data) - 8): if data[i] == 0xBE: info_len = int.from_bytes(data[i+4:i+8], 'little') # 索引段长度必须小于剩余数据,才算合理魔数 if 0 < info_len < len(data) - i: with open(dst, 'wb') as out: out.write(data[i:]) print(f"found wxapkg at offset {i}, saved to {dst}") return i raise ValueError("no valid wxapkg magic found") strip_custom_header("encrypted.wxapkg", "decrypted.wxapkg")逻辑说明:遍历字节流找0xBE,并用「索引段长度必须小于剩余数据」这个约束过滤掉误命中的假魔数。参数上,int.from_bytes(..., 'little')与前面保持一致的字节序;0 < info_len < len(data) - i是核心校验条件,缺了它很容易在随机数据里误判。还原后的decrypted.wxapkg应该能被标准解包工具直接识别。
3.2 异或加密的密钥试探
异或加密的难点在于密钥未知。如果加密只用了单字节密钥,可以通过已知明文攻击反推:明文 wxapkg 开头固定是0xBE加三个保留字节,通常保留字节为0x00,那么密文头几字节与BE 00 00 00异或就能得到密钥。
def guess_xor_key(src): with open(src, 'rb') as f: head = f.read(8) # 假设明文前四字节是 BE 00 00 00 known = bytes([0xBE, 0x00, 0x00, 0x00]) key = bytes([head[i] ^ known[i] for i in range(4)]) print("candidate key:", key.hex()) return key def xor_decrypt(src, dst, key): with open(src, 'rb') as f: data = f.read() out = bytes([data[i] ^ key[i % len(key)] for i in range(len(data))]) with open(dst, 'wb') as f: f.write(out) key = guess_xor_key("encrypted.wxapkg") xor_decrypt("encrypted.wxapkg", "decrypted.wxapkg", key)逻辑说明:guess_xor_key用已知明文头反推密钥,xor_decrypt按密钥长度循环异或还原全文件。参数上,key[i % len(key)]支持单字节和多字节密钥;如果密钥是多字节,前四字节只能推出密钥的前四位,需要结合更多已知明文(比如索引段里的固定结构)继续推导。还原后同样用 2.1 的peek_wxapkg验证魔数是否为0xBE。
3.3 解密后的解包与资源提取
拿到明文 wxapkg 后,解包就是把索引区解析出来,按偏移和大小把每个文件写到磁盘。核心逻辑如下:
import struct, os def unpack_wxapkg(path, out_dir): with open(path, 'rb') as f: data = f.read() assert data[0] == 0xBE, "not a plain wxapkg" info_len = struct.unpack('<I', data[4:8])[0] pos = 8 end = 8 + info_len count = struct.unpack('<I', data[pos:pos+4])[0] pos += 4 for _ in range(count): name_len = struct.unpack('<I', data[pos:pos+4])[0] pos += 4 name = data[pos:pos+name_len].decode('utf-8') pos += name_len offset, size = struct.unpack('<II', data[pos:pos+8]) pos += 8 out_path = os.path.join(out_dir, name) os.makedirs(os.path.dirname(out_path), exist_ok=True) with open(out_path, 'wb') as out: out.write(data[offset:offset+size]) print(f"unpacked {count} files to {out_dir}") unpack_wxapkg("decrypted.wxapkg", "./output")逻辑说明:先校验魔数,再读索引段长度,然后循环读取每个文件的名称、偏移和大小并落盘。参数上,<II一次解析偏移和大小两个 32 位整数;os.makedirs(..., exist_ok=True)保证多级目录能自动创建。解包后重点看app-service.js(业务逻辑)、app-config.json(页面配置)和pages目录下的 WXML/WXSS,这些是后续分析的主要对象。
4. 解密与解包路上的避坑清单
4.1 现象:脚本跑完魔数还是不对
原因:PC 微信版本更新后换了加密方式,老脚本假设的偏移或密钥失效。解决:先用xxd人工确认头部特征,判断是偏移型还是异或型,再选对应脚本;不要盲目套用旧脚本。同时确认拿到的包是不是分包,分包可能只有部分数据。
4.2 现象:解包报「not a plain wxapkg」
原因:解密不彻底,或者截取偏移时多切/少切了字节。解决:用peek_wxapkg打印魔数和info_len,确认info_len小于文件剩余长度;如果魔数对但info_len异常,多半是偏移差了几个字节,微调起始位置重试。
4.3 现象:解出来的 JS 是乱码或压缩过的
原因:小程序发布时开了代码压缩或混淆,这属于正常现象,不是解密失败。解决:先确认文件本身是合法 UTF-8,再用格式化工具还原可读性;混淆后的变量名无法完全还原,重点看字符串常量和接口路径。
4.4 现象:找不到 wxapkg 文件
原因:微信版本不同,缓存目录结构不同,或者小程序还没在 PC 端打开过。解决:先在 PC 微信里实际打开目标小程序触发缓存,再按扩展名全盘搜索;注意区分主包和分包目录。
4.5 现象:解密后文件能打开但图片/字体缺失
原因:部分资源不在 wxapkg 里,而是运行时从网络加载或存在独立缓存。解决:解包只覆盖包内静态资源,网络资源需要结合抓包单独获取,不要误以为解密不完整。
5. 批量处理与版本适配的进阶技巧
单个包跑通之后,真正提效的是批量化。我一般写一个目录扫描脚本,把某个 AppID 下的主包和所有分包一次性解密加解包,输出到按 AppID 命名的目录里,方便对比不同版本。批量脚本的关键是把「判断加密类型」做成自动分支:先试偏移截取,验证魔数;失败再试异或还原;都失败就标记为「未知加密」单独人工处理。这样面对微信版本升级时,至少能快速定位哪些包需要重新分析。
版本适配上,我的习惯是每次微信大版本更新后,拿一个已知的小程序重新跑一遍全流程,确认魔数位置和密钥推导是否仍然成立,把结论记在一个小笔记里。下面这张表是我常用的判断对照:
| 头部特征 | 判断类型 | 处理方式 |
|---|---|---|
开头即BE 00 00 00 | 明文 | 直接解包 |
前若干字节后出现BE | 偏移型 | 截取偏移后数据 |
| 全段无明显魔数 | 异或型 | 已知明文反推密钥 |
| 魔数对但长度异常 | 偏移偏差 | 微调起始位置 |
验证解密是否成功,最可靠的标准不是「脚本没报错」,而是解包出来的app-config.json能被 JSON 解析器正常读取、app-service.js是合法文本。我踩过最深的一个坑,是早期太信任脚本的「成功」输出,结果解出来一堆空文件,后来养成习惯:每次解密后先peek_wxapkg看魔数,再抽查一个 JSON 文件能否解析,两步都过才算真的成功。这套流程不复杂,难的是对版本变化保持敏感,别让一次成功变成永久依赖。希望帮到你。
本文还有配套的精品资源,点击获取