资讯动态

PC端小程序wxapkg包解密:密钥提取与AES还原实战

发布时间:2026/10/17 18:31:45 来源:尧图企业网站定制
最近有几个搞前端的朋友问我能不能把 PC 上小程序包里的源码捞出来看看。在他们眼里小程序就是个大号的网页按个 F12 应该就能看到点东西。但真打开某款常用聊天软件的小程序缓存目录拖一个 wxapkg 文件进十六进制编辑器大部分人第一反应都是愣住——全是乱码连个明文字符串都搜不到。这个不算奇怪的因为现在的 PC 端小程序包并不是简单打包它先做了一层加密处理你得先过了这层壳才能看到里面的 JS、模板和数据文件。我前后花了两个周末把这条链路走通最后总结下来就三步定位包、提取密钥、执行解密。听起来简单但每一步都有不少暗坑。这篇就把我自己的操作过程、绕过的弯路、以及最后怎么稳定复现的方法全部写出来给同样在研究 wxapkg 解密算法的同学做个参考。需要提前说明的是本文只讨论你有权分析的小程序包或者你自己开发的测试小程序别拿这套思路去打任何未经授权的线上目标。1. 先搞懂wxapkg 到底是什么为什么要加密1.1 一个“看起来像是加密”的二进制容器wxapkg 是这个生态里小程序的前端运行包格式里面装的是编译后的 WXML 模板、WXSS 样式、JavaScript 逻辑、JSON 配置等。在手机端有些版本还能直接看到类似 zip 的结构但 PC 端从某个版本开始包里的内容做了整体加密。具体表现为两点第一你把 wxapkg 拖进文本编辑器全部是高熵数据没有任何可见 ASCII 字符串第二文件大小和压缩后的 zip 接近但你用解压工具直接开一定报错因为文件头已经不是PK了。我最早拿到的包长这样文件头是几个固定的魔数字节紧接着就是大段无法解释的字节流。用 010 Editor 看 entropy基本都是 7.9 以上说明内容做过随机化处理也就是加密。这时候你需要明白一个事实你面对的不是一个格式转换的问题而是一个对称加密的问题。只要能找到密钥和算法用脚本就能还原。1.2 加密不是无边界的密钥一定在本地很多人一听“加密”就觉得无从下手但要记住一个原则客户端要运行小程序就必须在本地把密文还原成明文再交给执行引擎。问题只在于它在内存里还原还是先还原成临时文件又或者是在加载时逐块解密。但不管哪一种密钥和算法必然存在于这个软件的运行环境中。从这个角度看破解的支点不是“硬破算法”而是“找密钥”。只要你在本机拿到密钥或者通过调试器在内存中拿到解密后的明文后续整个过程就是体力活。这也是为什么我把“提取密钥”单独作为第二步因为它是整个链路里门槛最高、最容易翻车的环节。2. 三步行事的总体逻辑2.1 第一步你的目标不是文件而是“路径规则”我第一次去找小程序包的时候习惯性去安装目录里搜*.wxapkg结果搜出一堆但都是零散的。后来发现这些文件实际存放在一个跟用户数据目录相关的路径下而不是安装目录。路径结构一般是这样的根目录是某个AppData下的用户数据目录里面按appid级联创建子目录每个 appid 文件夹下才是一个个 wxapkg 文件。所以第一步的重点是摸清目标软件的缓存结构不要傻乎乎全盘搜索。正确做法是先打开目标软件手动运行一次你要分析的小程序让它把包完整下载到本地然后去看该目录下的文件列表用修改时间“最后的写入”来锁定目标。这个操作并不复杂但很多人倒在第一步——因为一个 appid 下可能会有 3 到 5 个 wxapkg 文件分别对应主包、分包、独立分包如果你只看文件名很容易搞错。2.2 第二步判断“加密方式”而不是直接猜密钥拿到文件后你要做的不是赶紧用 AES 去试而是先判断加密类型。我见过有人拿一个只有 6 字节头部的文件硬套 AES-128-CBC解密出来的全是乱码后来才发现那个文件根本没有加密只是带了一个自定义头真正的负载在头部之后直接是明文存储的。通常判断思路是先用十六进制看文件头。如果前几字节是固定魔数比如0xCB 0xCB 0xCB之类而且魔数后面跟着的字节熵值正常那你面对的可能只是带头的裸包如果魔数后面整个文件熵值极高而且没有明显的结构规律才是加了密。在 PC 端小程序这个场景下现网常见的加密方案是对整个文件体做 AES 对称加密密钥长度为 16 字节或 32 字节分组模式多为 CBC。这个判断不需要逆向你软件本体通过对比同一个小程序在“开发版”和“线上版”之间的数据特征就能验证。2.3 第三步一切交给脚本减少手工操作如果第二步确定了是 AES 类加密那第三步就是写脚本批量处理。你不需要理解加密算法的数学细节只需要找一个成熟的密码库传入“密文 密钥 IV”三个参数就可以得到明文。真正的工作量其实在前两步把密钥找对、把文件裁切对。我自己的经验是尽量把步骤三的脚本写成“输入一个 wxapkg 路径自动读头、自动判断长度、自动解密、自动输出”的形式这样后续分析别的包时能直接复用。3. 从零开始完整解密一个 wxapkg 包3.1 环境准备一张干净的工作桌先交代一下我用到的工具清单都不是什么冷门东西Python 3.9 以上版本pycryptodome 库负责 AES 加解密一个顺手的三方解析库用于最终切分包内文件没有也能手写HxD 或者 010 Editor看十六进制用。安装依赖我习惯用 virtualenv 隔离避免把系统环境搞脏。命令也是常规操作pip install pycryptodome这一步没什么可说的唯一要注意的是别在系统 Python 里混装版本pycryptodome 和 pycrypto 是两套不兼容的东西我当年就在这上面吃过亏。3.2 第一步实操用脚本定位目标目录因为不同版本的软件路径可能不同我建议不要硬编码整个绝对路径而是先动态枚举。思路是遍历用户 AppData 目录下和目标软件相关的所有目录筛选出包含*.wxapkg的文件夹再按修改时间排序。参考脚本如下import os import glob from datetime import datetime search_root os.path.expanduser(~/AppData/Roaming) # 按实际平台调整 matches [] for dirpath, dirnames, filenames in os.walk(search_root): for fname in filenames: if fname.endswith(.wxapkg): full_path os.path.join(dirpath, fname) mtime os.path.getmtime(full_path) matches.append((mtime, full_path)) matches.sort(reverseTrue) for mtime, path in matches[:20]: print(datetime.fromtimestamp(mtime), path)跑完以后你会看到一列按时间倒序排列的 wxapkg 路径。此时先去目标软件里点开一个你自己写的小程序让它出现界面然后重新跑一遍脚本列表最前面的那个文件通常就是刚刚加载的小程序包。这一步的巧妙之处在于你不需要关心 appid 是什么只要靠“最后写入时间”来定位基本不会错。3.3 第二步实操分析文件头并定位密钥拿到目标文件后用十六进制编辑器打开看前 16 字节。我遇到的情况是文件头存在一组固定魔数比如16 00 00 00 01 00 00 00 00 00 00 00后面追加 4 字节表示包体长度再往后就是真正的密文区。如果你看到的头部只有 6 个字节且剩余数据看起来像有结构则可能是未加密包这时候直接跳到本文第 4 节的包内解析即可。如果确认是加密包那就得想办法找密钥。密钥来源通常是目标软件本地的某个配置文件或者是从主进程内存中导出的字节序列。最简单的办法是在配置文件夹里搜索常见的关键词比如key、aes、encrypt。但大多时候不会这么明显你需要更贴近目标的做法——找到客户端的核心 DLL 或者 JS 运行时模块用调试器附加到进程在内存区域里搜索已知的固定密钥字节比如从魔数或者包名推断。我自己的经验是先尝试打开目标软件的调试端口如果你有权这么做用内存搜索工具查找 16 字节或 32 字节的连续高熵值。找到之后最好用一个小型明文样本验证。怎么验证你可以拿一个已知内容的 wxapkg 包用搜索到的密钥做一次 AES 解密看输出里是否出现appid或page等 JSON 字段。如果出现了说明找对了如果全是乱码则换个密钥再试。我在实际操作中还遇到另一种情况IV 并不是固定值而是从 wxapkg 文件头部的某个字段派生出来的。比如取头部第 6 到第 21 字节作为 IV那你在脚本里就得先读文件头动态切片不能写死。3.4 第三步实操AES 解密脚本的完整写法这里给出一个可用的参考脚本。假设通过之前的分析你确认加密模式是 AES-128-CBCIV 从文件头部偏移 6 处取 16 字节密文从头部偏移 1024 处开始按实际情况调整。实际脚本应该是这种手感import sys from Crypto.Cipher import AES def decrypt_wxapkg(filepath, key_hex, offset_body1024, iv_offset6): with open(filepath, rb) as f: data f.read() key bytes.fromhex(key_hex) iv data[iv_offset:iv_offset16] # 有些包体长度会在头部记录这里直接取余下所有数据 ciphertext data[offset_body:] cipher AES.new(key, AES.MODE_CBC, iv) plaintext cipher.decrypt(ciphertext) out_path filepath .decrypted with open(out_path, wb) as f: f.write(plaintext) # 简单验证打印前 64 字节可读字符 print(out_path, plaintext[:64])注意AES-CBC 加解密要求密文长度必须是 16 的倍数如果你的包体末尾有补充位数需要自行处理 PKCS7 unpadding否则最后几个字节可能是乱码。我在上面脚本里没有写 unpadding实际使用时要补上。更稳妥的做法是使用Cipher的“不填充”模式然后手动去掉尾部的\x0N。3.5 验证解密结果的三个信号解密完不要急着高兴先验证三件事输出文件里是否出现了明文 JSON比如包含pages、window等字段是否能找到app.json的字符串输出的整体结构是否像一个文件包比如以0xBE 0x00 0x00 0x00作为文件项头后续跟文件名。如果以上都不成立那基本就是密钥不对或者文件头偏移不对。千万别试图把所有解密结果都拿去硬解你会发现在错误的方向上越走越远。4. 解密之后包内文件结构与进一步分析4.1 解密成功后你会看到什么解密后的 wxapkg 本质上是一个自定义文件包头部包含文件索引索引项里为每个文件记录了文件名、偏移和大小随后是文件数据区。你可以写一个简单的解析器把里面所有小文件导出到磁盘。用 Python 实现大概也就是几十行的事核心逻辑是读索引表然后用seek到每个文件的偏移切片保存。导出后你会发现至少有这几种文件.js小程序所有页面逻辑和公共逻辑的编译产物.json页面配置和app.json.wxml模板文件但很多版本里不是明文 HTML而是编译后的“代码包”字节码.wxss样式文件同样是经过编译处理的。所以严格来说解密只是第一步它让你从“无法读取的二进制”变成了“结构化的二进制”。你仍然需要去理解这些编译产物的格式才能还原出接近源码的东西。4.2 不要误把“解密”当成“破解”很多人以为解密 wxapkg 就等于拿到了完整源代码这是不对的。我遇到的情况是JS 文件里大量使用了压缩和混淆变量名是a,b,c字符串被拆成多个数组统一编码。WXML 模板也被转换成了虚拟 DOM 构建指令可读性极低。所以解密后的“放大镜作用”是让你能分析逻辑流程、接口调用、数据存储方式而不是直接让你复制源码到编辑器里改两行就能运行。更现实的做法是把解密后的 JS 丢进一个美化工具里整理缩进再配合本地接口调用做动态调试逐步还原业务逻辑。这个过程更像侦探工作而不是解压缩。5. 实操中的几个大坑与排查思路5.1 密钥会随版本更新变化第一次解密成功后我顺手把密钥记在本子上以为以后都能用。过了一个月好友更新了客户端到新版本我再拿旧密钥去解新包结果全是乱码。后来对比两个版本的客户端发现其内置的密钥在版本升级时会被刷新。所以正确的做法是固定一个可复现的版本环境把密钥和对应的软件包哈希一起记录好。至少在同一个版本的本地环境中密钥是稳定的。如果你在测试不同版本请重新提取密钥不要拿旧密钥去处理新包会浪费大量时间。5.2 文件头偏移不是固定的我遇到过一个情况同一个包在客户端下载到一半时缓存目录会出现一个临时文件它的头部只有十几个字节后续数据缺失。如果有人把这个临时文件当成完整包来解密就算密钥正确也只会得到半截乱码。这里需要注意wxapkg 并不总是“一次网络请求写入”而是分块落盘所以判断文件是否完整很重要。最直接的办法看长度是否和包体长度字段声明的一致或者用十六进制看末尾正常包解出来的尾部应该有固定的填充字节缺失则尾部异常。5.3 内存搜索密钥时容易误报用调试器在进程内存里搜索密钥串会把一堆毫无相关的字节当成候选。尤其当你搜的是 16 字节连续值整个进程里符合条件的对象多了去了。我的经验是不要只搜密钥本身而是搜“密钥的前几位 已知 IV 的后几位”这种组合或者利用解密失败的反馈来排除。我自己的方法是先准备一个只有 1KB 明文内容的小测试包然后在内存里搜索包含该明文片段附近的位置再往附近倒推找到密钥所在选区。这个方法虽然绕但命中率极高。5.4 解密脚本的编码问题Python 处理二进制文件时编码问题看似无关痛痒但经常让你在最后一步翻车。例如用open(out_path, w)而不是wb文件会被做默认编码转换直接破坏字节。另外在 Windows 上文件路径中如果有中文字符可能会触发编码异常。我建议所有文件操作都显式用二进制读写并且传入errorsignore避免因为一个字符的编码问题中断整个批次任务。5.5 不要忽略包内的“环境校验”有一个容易被忽略的点小程序包解密后如果直接试图在非官方环境下运行可能会遇到内置的环境检测。这类检测不一定是防解密而是防止你篡改包后运行在模拟器里。所以你解密后如果只是做静态分析问题不大但如果你想改逻辑再跑起来就会遇到各种奇怪的校验错误。这也侧面说明解密只是逆向分析的最外层真正的工作量在分析逻辑和绕过保护上而且后者带很强的合规风险我一般不做也不建议别人做。6. 写到最后说点我的亲身体会做 PC 端小程序包的逆向分析技术难点并不是那个加密算法本身而在于定位密钥和搞清包结构。我在中途曾经因为密钥提取方式过于复杂一度想放弃后来转念一想客户端既然能解密就一定会在某个时间点把密钥或明文暴露在内存里与其费劲去逆向那套密钥派生流程不如直接在内存里找答案。这个思路帮我把整个流程缩短了大半。如果你也打算在这种方向上深入研究我建议你从“自己写一个小程序然后在 PC 上跑起来”开始。自己是最容易获取权限的目标你可以随意打断点、翻内存、试错还不用有任何合规顾虑。等你把整个解密链路在自己的包上跑通了再去思考怎么应用到其他场景心里才有底。这套方法论是通用的但目标一定要选对。最后分享一个小技巧在解密之前先去目标软件设置里关闭“自动更新”并且把当前版本的客户端备份一份。因为一旦升级所有分析环境都会变密钥也会跟着变你前面定位破绽的全部经验可能瞬间失效。保持环境固定比什么技巧都重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑