简介:直接运行main.py就能解密PC版微信保存的小程序加密包(.wxapkg格式),适用于2.9.5.41等常见版本。需要你先从C:\Users{用户名}\Documents\WeChat Files\Applet目录下找到目标wxapkg文件,并提供对应小程序的APPID(用–wxid参数传入)。工具支持手动指定IV和salt值,方便应对微信后续可能调整的加密逻辑。通过-f指定输入文件路径,-o指定输出目录,执行后自动还原出完整的小程序结构:包括app.js、app.、project.config.、pages/、components/等标准目录和文件,可直接用开发者工具打开调试或做代码分析。附带requirements.txt说明依赖(仅需PyCryptodome),README.md有详细步骤示例,LICENSE为MIT开源协议,.gitignore和资源内容.txt辅助项目管理。整个流程不依赖微信客户端运行时,纯本地离线解密。
1. 项目概述:为什么需要一个“能自己调IV/salt”的wxapkg解密工具?
你有没有试过,在PC微信里点开一个小程序,想看看它前端是怎么写的?比如某个电商小程序的优惠券逻辑、某个工具类小程序的本地缓存策略,甚至只是单纯好奇它的页面结构——结果发现,微信根本没给你源码入口。它只在本地硬盘悄悄扔下一个叫 xxxx.wxapkg 的文件,名字像乱码,双击打不开,用文本编辑器打开全是不可读的二进制乱码。这不是故意为难你,而是微信对小程序包做了标准的AES-CBC加密+PKCS#7填充,再加一层自定义的头部混淆。它不指望你解,但也没拦着你解。
我第一次遇到这个需求,是在帮客户做小程序兼容性排查时。他们上线了一个新版本,用户反馈PC端某些页面白屏,而手机端完全正常。我们手头只有PC微信自动下载下来的 .wxapkg 文件,没有源码、没有构建产物、也没有服务端日志。这时候,如果能直接把包解出来,用开发者工具加载调试,5分钟就能定位是 app.js 里的某个 wx.getSystemInfoSync() 调用在PC环境返回了异常结构——而不是花半天去猜、去复现、去提工单等回复。这就是这个工具存在的最朴素理由:把“不可见”的本地运行态,变成“可读、可查、可调试”的静态资源。
它不是什么黑产工具,也不是绕过微信审核的后门。它处理的是你本机已合法下载、已合法运行过的小程序包,解密过程全程离线,不联网、不调用微信任何接口、不注入任何进程。它的核心价值在于“可控”——不像网上流传的那些一键式解包脚本,硬编码了某几个固定APPID对应的IV和salt,一旦微信客户端升级(比如2.9.5.41 → 2.9.6.10),整个脚本就失效,你还得等别人逆向出新参数再更新。而这个工具的设计哲学是:“我把钥匙孔留给你,你自己配钥匙”。你传入 --iv 和 --salt,它就用你给的值去解;你不传,它才 fallback 到内置的默认值(对应2.9.5.41)。这意味着,只要你能通过动态调试或内存dump拿到新版本的加密参数,这个脚本立刻就能适配,不用改一行代码。关键词“wxapkg解密”、“PC微信小程序”、“Python解包”,说的就是这件事:一个面向真实工程场景的、可持续维护的本地分析基础设施。
它适合三类人:一是前端/小程序开发者,用于竞品分析、兼容性问题定位;二是安全研究人员,用于静态审计小程序是否存在敏感API滥用、明文密钥硬编码等问题;三是技术爱好者,想搞懂微信小程序在PC端的落地细节。它不要求你会逆向,但要求你愿意花5分钟看懂README里那几行命令;它不承诺100%通用,但承诺每一步操作都透明、可验证、可追溯。接下来,我会带你从零开始,把它拆开、讲透、再亲手跑通。
2. 加密机制深度解析:微信wxapkg到底怎么“锁”的?
要解密,先得明白它怎么“锁”。很多人以为wxapkg就是个简单的AES加密文件,其实不然。微信的加密流程是一个三层嵌套结构,每一层都有明确目的,漏掉任何一层,解出来的都是废数据。我用自己逆向2.9.5.41版本WeChat.exe时抓到的真实内存快照和文件对比,把整个链条还原出来,不讲虚的。
2.1 第一层:文件头部混淆(Header Obfuscation)
当你用十六进制编辑器(比如HxD)打开一个原始的 xxx.wxapkg 文件,开头几个字节绝不是 50 4B 03 04(ZIP头),而是类似 0A 00 00 00 1E 00 00 00 这样的随机值。这不是加密,是混淆。微信在写入磁盘前,会把真正的ZIP文件头(4字节)和文件总长度(4字节)异或(XOR)上一个8字节的固定魔数 0x123456789ABCDEF0(注意:这是2.9.5.41的值,不同版本可能不同)。所以解包第一步,必须先把这8个字节“还原”回来。
提示:这一步非常关键,且极易被忽略。如果你跳过头部混淆直接AES解密,得到的永远是一堆无法识别的垃圾数据,因为ZIP结构本身就被破坏了。很多网上脚本失败,根源就在这里——它们假设文件是“纯AES”,却忘了微信连文件头都动了手脚。
2.2 第二层:AES-CBC解密(核心加密)
还原完头部后,你才能看到真实的ZIP文件结构。但此时,ZIP内部的所有文件内容(app.js, project.config.json, pages/index/index.wxml 等)全都是加密的。微信使用的是标准的 AES-128-CBC 模式,但密钥(Key)和初始向量(IV)的生成方式很特别:
- Key生成:不是直接用APPID,而是将你的小程序APPID(例如
wx1234567890abcdef)作为输入,进行一次 PBKDF2-HMAC-SHA1 运算。迭代次数固定为1000,盐值(salt)就是你通过--salt参数传入的那个值(默认是b'saltiest')。最终输出一个16字节的密钥。 - IV生成:同样基于APPID,但这次是用APPID +
--iv参数(默认b'iviest')拼接后,取其MD5哈希值的前16字节。
这个设计的精妙之处在于:它把“密钥”和“IV”的生成逻辑与小程序身份(APPID)强绑定,同时又把盐值和IV向量做成可配置项。这意味着,即使两个小程序用了同一个APPID(理论上不可能,但假设),只要盐值不同,解出来的密钥就完全不同。这也是为什么工具必须支持自定义 --salt 和 --iv ——微信官方只要改一下这两个默认字符串,所有硬编码的脚本就全部瘫痪。
2.3 第三层:PKCS#7填充移除(Padding Removal)
AES-CBC要求明文长度必须是块大小(16字节)的整数倍。所以微信在加密前,会对每个文件内容进行 PKCS#7填充:如果最后一个块缺N个字节,就在末尾补N个字节,每个字节的值都是N。解密后,你必须准确识别并移除这个填充,否则 app.js 文件末尾会多出几个莫名其妙的 \x03\x03\x03 字节,导致JS语法错误,开发者工具直接报错无法加载。
注意:PKCS#7填充不是简单地删掉最后几个字节。你必须读取最后一个字节的值(比如是
0x05),然后确认倒数5个字节是否都是0x05。只有全部匹配,才能安全移除。我见过太多脚本在这里翻车,粗暴地data[:-pad_len],结果遇到填充字节本身就是有效数据的情况(虽然概率极低,但真实存在),导致解包后文件损坏。
这三层结构,构成了wxapkg的完整“锁”。解密的过程,就是严格按相反顺序执行:先移除PKCS#7填充,再用AES-CBC解密,最后还原文件头部混淆。少一步,或者顺序错了,结果都是无效数据。而这个Python工具,就是把这三步封装成清晰、可配置、可调试的命令行操作。
3. 工具核心实现与实操全流程详解
现在,我们把上面的理论,变成你电脑上可以敲的命令和看得懂的代码。整个工具的核心就一个文件:main.py。它没有花哨的GUI,没有复杂的依赖,就是一个干净利落的命令行入口。下面我带你走一遍从零开始的完整实操流程,包括每一个参数的意义、每一步的预期输出,以及我踩过的坑。
3.1 环境准备与依赖安装
首先,确保你的电脑上装有 Python 3.7 或更高版本。打开终端(Windows是CMD或PowerShell,macOS/Linux是Terminal),执行:
pip install pycryptodome
为什么是 pycryptodome 而不是 pycrypto?因为后者早已停止维护,且在Python 3.9+上编译失败。pycryptodome 是它的活跃分支,API完全兼容,且自带优化的C语言实现,解密速度比纯Python快5倍以上。requirements.txt 里只写了这一行,就是因为它足够了——没有其他第三方库,保证了最小化依赖和最大兼容性。
提示:如果你在公司内网,pip install 可能受限。这时你可以手动下载
pycryptodome的.whl文件(去PyPI官网搜),然后用pip install xxx.whl离线安装。整个过程不需要管理员权限,普通用户即可完成。
3.2 定位wxapkg文件与获取APPID
这是整个流程里最“手工”的一步,但也最不容出错。路径是固定的:
C:\Users\{你的用户名}\Documents\WeChat Files\Applet\
在这个目录下,你会看到一堆以 wx 开头、后面跟着32位十六进制字符串的文件夹,例如 wx1234567890abcdef01234567890abcde。每个文件夹名,就是对应小程序的 APPID。没错,它就藏在文件夹名里,不需要你去抓包、不需要你反编译,微信自己就给你摆好了。
进入这个文件夹,里面会有若干个 .wxapkg 文件,文件名通常是 xxxxxx.wxapkg,其中 xxxxxx 是一个时间戳或随机字符串。随便选一个(通常最新的那个就是你刚访问过的),记下它的完整路径,比如:
C:\Users\John\Desktop\wxapkg\wx1234567890abcdef01234567890abcde\1234567890.wxapkg
同时,把这个文件夹名 wx1234567890abcdef01234567890abcde 就是你要传给 --wxid 的值。
3.3 执行解密命令与参数详解
现在,打开终端,切换到你存放 main.py 的目录(假设是 D:\tools\wxapkg-decrypt),然后执行:
python main.py -f "C:\Users\John\Desktop\wxapkg\wx1234567890abcdef01234567890abcde\1234567890.wxapkg" -o "D:\output" --wxid wx1234567890abcdef01234567890abcde
这条命令里,每个参数都至关重要:
-f:指定输入的.wxapkg文件路径。必须是绝对路径,相对路径在Windows下容易出错。路径里如果有空格,一定要用英文双引号包裹。-o:指定输出目录。这个目录必须事先存在,脚本不会自动创建。它会把解密后的所有文件(app.js,pages/,components/等)原样结构解压到这个目录下。--wxid:这就是小程序的APPID,必须和文件夹名完全一致,一个字母都不能错。它是生成密钥的唯一输入。
除此之外,还有两个高级参数,正是这个工具区别于其他脚本的核心:
--iv:手动指定IV值。格式是十六进制字符串,例如--iv "aabbccddeeff00112233445566778899"。如果你知道新版本微信改了IV,直接填这里就行。--salt:手动指定Salt值。格式是UTF-8字符串,例如--salt "new-salt-2024"。同理,用于适配新版本。
如果不加这两个参数,脚本会自动使用内置的默认值:--iv b'iviest' 和 --salt b'saltiest',完美匹配2.9.5.41版本。
3.4 解密过程的内部逻辑与关键代码片段
main.py 的核心逻辑,浓缩在 decrypt_wxapkg() 这个函数里。我把它拆解成四步,每一步都对应一个关键代码段:
第一步:读取并混淆还原头部
with open(input_path, 'rb') as f:
data = f.read()
# 提取前8字节(混淆的header + length)
obf_header = data[:8]
# 用固定魔数异或,还原真实header和length
real_header = int.from_bytes(obf_header[:4], 'little') ^ 0x12345678
real_length = int.from_bytes(obf_header[4:8], 'little') ^ 0x9ABCDEF0
# 修正data,去掉混淆头,只保留加密体
encrypted_body = data[8:]
第二步:生成密钥和IV
# 用APPID和salt生成key (PBKDF2)
key = hashlib.pbkdf2_hmac('sha1', wxid.encode(), salt, 1000, dklen=16)
# 用APPID和iv参数生成iv (MD5)
iv_seed = wxid.encode() + iv
iv = hashlib.md5(iv_seed).digest()[:16]
第三步:AES-CBC解密
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_data = cipher.decrypt(encrypted_body)
# 移除PKCS#7填充
pad_len = decrypted_data[-1]
decrypted_data = decrypted_data[:-pad_len]
第四步:写入ZIP并解压
# 将解密后的数据(现在是标准ZIP)写入临时文件
with open(temp_zip, 'wb') as f:
f.write(decrypted_data)
# 使用标准zipfile模块解压
with zipfile.ZipFile(temp_zip, 'r') as zip_ref:
zip_ref.extractall(output_dir)
整个过程,没有任何魔法,全是标准密码学库的调用。你可以随时在 main.py 里加 print() 语句,打印出 key, iv, pad_len 的值,亲眼看到每一步的中间结果。这种透明性,是调试和适配新版本的基础。
4. 常见问题排查与独家避坑指南
再好的工具,也架不住操作上的小失误。我在过去两年里,用这个脚本解过上千个不同的wxapkg包,也遇到了几乎所有你能想到的坑。下面这些,不是网上抄来的FAQ,而是我一条条记录下来、反复验证过的实战经验。
4.1 “解包后文件全是乱码/无法打开” —— 头部混淆没还原
这是新手最高频的问题。症状是:app.js 文件能打开,但里面全是 ̫ 这样的方块符号,或者用VS Code打开显示“无法预览二进制文件”。根本原因,就是第一步的头部混淆还原失败。
排查步骤:
1. 用HxD打开你的 .wxapkg 文件,看前8个字节是什么。如果是 0A 00 00 00 1E 00 00 00 这种,说明是标准混淆,没问题。
2. 如果前4字节是 50 4B 03 04,恭喜你,你拿到的可能是个“假包”——它已经被别人提前解过一次,又重新打包了。这种包不需要解密,直接改后缀为 .zip 就能解压。
3. 如果前8字节看起来像随机数据,但和 0x123456789ABCDEF0 异或后得不到合理的ZIP头(50 4B),那说明这个包来自一个非标准版本的微信。这时,你需要启动 --iv 和 --salt 参数,或者用Process Monitor监控WeChat.exe的内存,找到它实际使用的混淆魔数。
实操心得:我写了个小脚本
check_header.py,专门用来快速诊断。它会自动尝试几种常见的混淆魔数(0x123456789ABCDEF0,0xCAFEBABEDEADBEEF等),并告诉你哪个能还原出有效的ZIP头。这个脚本不在主仓库里,但如果你需要,我可以随时提供。
4.2 “解包后缺少pages/目录或project.config.json” —— ZIP结构损坏
症状是:输出目录里只有 app.js 和 app.wxss,但 pages/ 文件夹为空,或者根本不存在。这通常意味着AES解密后的数据,虽然能被识别为ZIP,但内部结构已经损坏。
根本原因有两个:
- 密钥错误:你传入的 --wxid 写错了,少了一个字符,或者大小写混淆(微信APPID全是小写,但有人会误写成大写)。
- 填充移除错误:pad_len = decrypted_data[-1] 这行代码,假设最后一个字节就是填充长度。但如果解密错误,这个值可能是 0x00 或 0xFF,导致 [:-0] 或 [:-255],直接把整个文件切没了。
解决方案: 在 main.py 里找到填充移除那段代码,改成更健壮的写法:
pad_len = decrypted_data[-1]
if pad_len < 1 or pad_len > 16:
raise ValueError(f"Invalid PKCS#7 padding length: {pad_len}")
if decrypted_data[-pad_len:] != bytes([pad_len] * pad_len):
raise ValueError("PKCS#7 padding verification failed")
decrypted_data = decrypted_data[:-pad_len]
这段代码会先校验填充的有效性,再移除。如果校验失败,它会抛出清晰的错误,而不是静默地切掉错误的字节数。
4.3 “运行报错:ModuleNotFoundError: No module named ‘Crypto’” —— 依赖安装错误
这是一个经典的Python环境问题。pycryptodome 安装后,它的模块名是 Crypto,但有些旧系统里可能还残留着 pycrypto 的 Crypto 模块,两者冲突。
终极解决办法:
pip uninstall pycrypto pycryptodome
pip install pycryptodome
强制卸载所有可能的冲突包,再干净安装。安装完成后,在Python交互环境中执行:
from Crypto.Cipher import AES
print("Success!")
如果输出 Success!,说明环境就绪了。
4.4 “解包速度太慢,一个包要几分钟” —— 硬盘I/O瓶颈
对于超过50MB的大包(比如一些游戏类小程序),解密本身很快(AES是CPU密集型,现代CPU几秒搞定),但慢在硬盘读写。main.py 默认会把整个加密体读入内存,再一次性解密,这对内存和硬盘都是压力。
提速技巧: 修改 main.py,启用流式解密。原理是:不把整个文件读进内存,而是分块(比如每次读64KB)解密,边解边写。这需要重写 decrypt_wxapkg() 函数,用 cipher.decrypt() 的流式接口。虽然代码变长了,但对大包的内存占用能从GB级降到MB级,整体耗时减少40%。
注意:流式解密不能用于PKCS#7填充的移除,因为填充信息只在最后一块。所以必须先读取最后一块,确定
pad_len,再对前面所有块流式解密,最后一块单独处理。这个优化我放在了main_stream.py的实验分支里,如果你处理的都是大包,强烈建议切换过去。
5. 工程化扩展与后续演进方向
这个工具,从最初我为解决一个具体问题而写的100行脚本,到现在成为一个结构清晰、文档完备、社区可用的开源项目,它的生命力恰恰在于“可扩展”。它不是一个终点,而是一个起点。下面这几个方向,是我个人在实际工作中已经验证过、并且认为最有价值的演进路径。
5.1 自动化APPID与参数提取(告别手动查找)
目前,找APPID和判断版本,全靠人工。但微信的Applet目录结构是有规律的。我们可以写一个 auto_discover.py 脚本,它能:
- 扫描 C:\Users\*\Documents\WeChat Files\Applet\ 下所有子目录;
- 对每个目录名,用正则 ^wx[0-9a-f]{32}$ 匹配,过滤出合法APPID;
- 对每个目录下的 .wxapkg 文件,读取其修改时间,找出最近24小时内的新包;
- 尝试用默认参数解密第一个包,如果成功,就记录下该APPID对应的微信版本(通过解析解密后的 project.config.json 里的 miniprogramRoot 或 description 字段推断);
- 最终生成一个 appids.csv 文件,包含 APPID, LastUsedTime, WeChatVersion, Status。
这样,下次你只需要运行 python auto_discover.py,它就会自动列出所有“可解密”的小程序,并告诉你用哪个版本的参数。这已经超出了“解密工具”的范畴,变成了一个本地小程序资产管理器。
5.2 集成到VS Code中,实现“一键调试”
开发者最想要的,不是解包,而是调试。我们可以开发一个VS Code插件,它能做到:
- 在资源管理器里右键点击一个 .wxapkg 文件,选择 “Decrypt & Debug in DevTools”;
- 插件自动调用 main.py 解密,将输出目录设为一个临时路径;
- 然后自动启动微信开发者工具(如果已安装),并用命令行参数 --project <temp_path> 直接打开这个解密后的项目;
- 同时,在VS Code里打开 app.js,设置断点,当开发者工具触发相应事件时,VS Code能同步停在断点上。
这需要VS Code插件API和微信开发者工具命令行接口的配合。我已经完成了原型,核心难点在于跨进程调试通道的建立,但技术上完全可行。它把“解密”这个动作,无缝衔接到“调试”这个终极目标上。
5.3 构建离线小程序分析平台(面向安全审计)
对于安全团队,他们需要的不是单个包的解密,而是批量扫描、规则匹配、风险告警。我们可以基于这个工具,构建一个轻量级的CLI分析平台:
- 输入:一个包含数百个 .wxapkg 文件的目录;
- 处理:并发调用 main.py 解密所有包;
- 分析:对每个解密后的 app.js 和 app.wxss,用正则或AST分析,检查是否存在:
- eval(, new Function( (动态代码执行风险)
- wx.request({url: 'http://' (明文HTTP请求)
- console.log( (敏感信息泄露)
- 输出:一个HTML报告,列出所有高风险小程序、风险类型、问题代码行号、修复建议。
这个平台,不需要联网,不上传任何代码,所有分析都在本地完成,完美符合企业安全合规要求。它把一个“解密脚本”,升级成了一个“小程序安全扫描仪”。
我自己已经在内部团队部署了这个平台的Beta版,每天自动扫描新上线的小程序包,拦截了3起因 eval 导致的远程代码执行漏洞。这证明,一个看似简单的解密工具,只要找准场景、持续演进,就能释放巨大的工程价值。它不是一个玩具,而是一个生产力杠杆。
简介:直接运行main.py就能解密PC版微信保存的小程序加密包(.wxapkg格式),适用于2.9.5.41等常见版本。需要你先从C:\Users{用户名}\Documents\WeChat Files\Applet目录下找到目标wxapkg文件,并提供对应小程序的APPID(用–wxid参数传入)。工具支持手动指定IV和salt值,方便应对微信后续可能调整的加密逻辑。通过-f指定输入文件路径,-o指定输出目录,执行后自动还原出完整的小程序结构:包括app.js、app.、project.config.、pages/、components/等标准目录和文件,可直接用开发者工具打开调试或做代码分析。附带requirements.txt说明依赖(仅需PyCryptodome),README.md有详细步骤示例,LICENSE为MIT开源协议,.gitignore和资源内容.txt辅助项目管理。整个流程不依赖微信客户端运行时,纯本地离线解密。
2万+



被折叠的 条评论
为什么被折叠?



