1. 项目概述为什么我们需要关注PC微信小程序的加密包如果你是一名前端开发者、安全研究员或者单纯对微信小程序的技术实现感到好奇那么你很可能已经发现直接从PC端微信获取到的小程序包.wxapkg文件和移动端的不太一样。它不再是那个可以直接用反编译工具打开的“源码包”而是变成了一个以V1MMWX开头的、经过加密处理的二进制文件。这个变化背后是微信为了提升小程序代码安全性、防止源码被轻易获取而设置的一道门槛。我最初接触这个需求是因为团队需要分析一个竞品小程序的特定动画实现。在移动端通过一些常规手段比如安卓模拟器还能相对容易地拿到包但PC端却卡在了这层加密上。市面上流传的工具虽然能用但要么报毒要么对命令行不友好更重要的是作为一个技术人如果只知其然会用工具而不知其所以然加密原理心里总是不踏实。于是我决定深入探究一下这个V1MMWX加密的来龙去脉并整理出一套从解密到源码解析的完整、透明且可复现的指南。这篇文章我将带你彻底拆解PC微信小程序包的加密机制。我们不仅会详细解读V1MMWX这个魔幻前缀背后的AES和XOR双重加密算法还会用Python亲手实现一个解密脚本让你摆脱对“360报毒”的第三方工具的依赖。最后我们会将解密后的标准.wxapkg包通过成熟的反编译流程还原成可读的WXML、WXSS和JS源码。整个过程我会穿插我在实际操作中踩过的坑和总结的技巧目标是让你读完就能独立操作真正理解每一个字节的变化。2. 核心原理深度拆解V1MMWX加密的“两层锁”拿到一个PC端的小程序包用十六进制编辑器打开你会看到文件头赫然写着V1MMWX。这六个字节就像一个封印宣告着后面的内容已被加密。要解开它我们必须先理解微信工程师设计的两道“锁”第一道是标准的AES-128-CBC加密第二道是自定义的XOR异或加密。这种组合拳既利用了成熟加密算法的强度又增加了一层自定义的混淆提高了逆向的门槛。2.1 第一层锁基于小程序ID的AES-128-CBC加密微信对包的前1024个字节准确说是前1023字节的数据加上1字节的填充或对齐使用了AES加密。这里有几个关键点需要厘清密钥的生成PBKDF2算法密钥并非直接使用小程序ID而是通过PBKDF2Password-Based Key Derivation Function 2这个密钥派生函数生成的。你可以把它理解为一个“密钥搅拌机”输入一个简单的密码口令和一段随机数据盐经过多次“搅拌”迭代输出一个强度高、随机性好的密钥。这样做的好处是即使小程序ID本身比较简单生成的密钥也会非常复杂能有效抵御暴力破解。 具体参数如下密码Password 小程序ID字符串。例如你的小程序存放在C:\Users\YourName\Documents\WeChat Files\Applet\wx2abc123456789def\那么wx2abc123456789def就是密码。盐Salt 固定字符串saltiest。这是一个硬编码的值在所有PC微信小程序加密中都是一样的。迭代次数Iterations 1000次。决定了“搅拌”的强度。密钥长度dkLen 32字节256位。但AES-128只需要16字节的密钥实际上PBKDF2会生成32字节我们只取前16字节作为AES密钥。哈希算法 通常为HMAC-SHA1。加密模式CBC与初始向量IV加密模式采用的是CBCCipher Block Chaining密码分组链接模式。这种模式的特点是每个明文块在加密前会先与前一个密文块进行异或操作。对于第一个块没有“前一个密文块”所以需要一个初始向量IV。这里的IV是固定的16字节字符串the iv: 16 bytes。固定IV在密码学实践中通常不被推荐因为它可能降低安全性但在这里它成为了加密方案的一部分。所以第一层加密的流程可以概括为小程序ID “saltiest”经过1000次PBKDF2“搅拌”生成32字节的派生密钥取前16字节作为AES-128密钥再配合固定的IVthe iv: 16 bytes对原始.wxapkg文件的前1023字节数据进行CBC模式加密。2.2 第二层锁基于ID字符的简单XOR加密在AES加密了文件头部之后微信对从第1024字节开始即AES加密数据之后的所有剩余数据施加了第二层加密逐字节的XOR异或运算。XOR运算的规则很简单相同为0不同为1。在加密中用一个密钥字节对数据字节进行XOR就能得到密文用同样的密钥字节对密文再次XOR就能还原数据。这里的巧妙之处在于密钥的选择XOR密钥 取小程序ID字符串的倒数第二个字符的ASCII码值。举例 如果小程序ID是wx2abc123456789def其倒数第二个字符是ee的ASCII码是101十六进制0x65那么XOR密钥就是101。特例 如果小程序ID长度小于2理论上几乎不会出现则使用默认密钥0x66十进制102。这意味着从第1024字节开始每一个字节都需要与这个固定的密钥值如101进行XOR运算。这层加密虽然简单但如果没有正确的密钥解密出来的数据将是乱码与第一层AES加密形成了互补。2.3 文件组装V1MMWX的诞生经过上述两步加密后微信按以下顺序组装成最终的__APP__.wxapkg文件写入6字节的固定文件头V1MMWX。写入经过AES-128-CBC加密后的1024字节数据这1024字节包含了加密后的前1023字节明文和可能的填充。写入经过XOR加密后的剩余所有数据。至此一个标准的、未加密的.wxapkg包就变成了我们看到的、带V1MMWX头的加密包。理解了这个结构解密就是其逆过程。注意 这里有一个极易混淆的点。很多文章和工具描述为“加密前1024字节”实际上更准确的说法是原始明文的前1023字节被加密后与必要的填充一起组成了一个1024字节的AES加密块。在解密时我们也是将这1024字节作为一个整体进行AES解密得到前1023字节的原始数据。这一点在自行编写解密代码时至关重要否则会导致解密失败。3. 实战手写Python解密脚本告别“报毒”工具理解了原理我们就可以动手了。依赖第三方exe工具总有心结一是安全问题二是无法定制。下面我将用Python逐步实现解密逻辑你可以将这段代码保存为pc_wxapkg_decrypt.py随时使用。3.1 环境准备与依赖安装首先确保你的Python环境是3.x版本。我们需要两个关键的库pycryptodome用于AES和PBKDF2和argparse用于处理命令行参数。pip install pycryptodomeargparse是Python标准库无需额外安装。3.2 解密脚本核心代码实现脚本的核心是逆向我们刚才分析的加密步骤。我们定义一个解密函数并添加友好的命令行接口。#!/usr/bin/env python3 PC微信小程序加密包V1MMWX格式解密脚本 用法python pc_wxapkg_decrypt.py -i 加密文件路径 -o 输出路径 -id 小程序ID import os import argparse from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 from Crypto.Util.Padding import unpad def decrypt_wxapkg(encrypted_path, output_path, wxid): 解密PC端微信小程序包 :param encrypted_path: 加密的 .wxapkg 文件路径 :param output_path: 解密后的输出文件路径 :param wxid: 微信小程序ID try: with open(encrypted_path, rb) as f: data f.read() # 1. 检查文件头 if not data.startswith(bV1MMWX): raise ValueError(文件头不是V1MMWX可能不是有效的PC端加密wxapkg文件。) # 2. 剥离6字节的V1MMWX头 encrypted_data data[6:] # 3. 生成AES密钥 (PBKDF2) # 参数密码(小程序ID字节串), salt(bsaltiest), 迭代次数1000, 密钥长度32 password wxid.encode(utf-8) salt bsaltiest iterations 1000 dk_len 32 derived_key PBKDF2(password, salt, dk_len, iterations) # AES-128 需要16字节密钥取派生密钥的前16字节 aes_key derived_key[:16] # 固定IV iv bthe iv: 16 bytes # 4. 解密前1024字节 (AES-128-CBC) # 前1024字节是AES加密块 aes_encrypted_block encrypted_data[:1024] cipher AES.new(aes_key, AES.MODE_CBC, iv) decrypted_front_part cipher.decrypt(aes_encrypted_block) # 移除可能的填充PKCS#7填充 decrypted_front_part unpad(decrypted_front_part, AES.block_size) # 注意decrypted_front_part 长度应为1023字节 if len(decrypted_front_part) ! 1023: print(f[警告] AES解密后前部数据长度为 {len(decrypted_front_part)}预期为1023。可能填充方式有误但将继续处理。) # 5. 解密剩余部分 (XOR) # XOR密钥小程序ID倒数第二个字符的ASCII码 if len(wxid) 2: xor_key ord(wxid[-2]) else: xor_key 0x66 # 默认密钥 print(f[警告] 小程序ID长度小于2使用默认XOR密钥 0x{xor_key:02x}) remaining_encrypted_data encrypted_data[1024:] # 将字节数据转换为bytearray以便修改然后进行XOR操作 decrypted_rear_part bytearray(remaining_encrypted_data) for i in range(len(decrypted_rear_part)): decrypted_rear_part[i] ^ xor_key # 6. 合并解密后的数据并写入文件 with open(output_path, wb) as f: f.write(decrypted_front_part) # 写入前1023字节明文 f.write(decrypted_rear_part) # 写入XOR解密后的剩余部分 print(f[成功] 文件已解密并保存至: {output_path}) print(f AES密钥前16字节: {aes_key.hex()}) print(f XOR密钥: {xor_key} (字符: {chr(xor_key) if 32 xor_key 127 else 非打印字符})) except FileNotFoundError: print(f[错误] 找不到输入文件: {encrypted_path}) except ValueError as e: print(f[错误] {e}) except Exception as e: print(f[错误] 解密过程中发生未知错误: {e}) import traceback traceback.print_exc() def main(): parser argparse.ArgumentParser(description解密PC微信小程序加密包V1MMWX格式) parser.add_argument(-i, --input, requiredTrue, help加密的.wxapkg文件路径) parser.add_argument(-o, --output, requiredTrue, help解密后的输出文件路径) parser.add_argument(-id, --wxid, requiredTrue, help微信小程序ID) args parser.parse_args() if not os.path.exists(args.input): print(f错误输入文件 {args.input} 不存在。) return decrypt_wxapkg(args.input, args.output, args.wxid) if __name__ __main__: main()3.3 脚本使用详解与实操示例将上述代码保存后你就可以在命令行中使用了。最关键的一步是如何找到正确的小程序ID和加密包路径。步骤1定位加密包和小程序ID在PC微信中运行一次目标小程序。然后打开文件资源管理器导航至C:\Users\[你的用户名]\Documents\WeChat Files\Applet\在这个目录下你会看到一系列以小程序ID命名的文件夹例如wx2abc123456789def。进入对应文件夹就能找到名为__APP__.wxapkg的加密包。文件夹的名字就是小程序ID。步骤2运行解密脚本假设你的脚本保存在D:\tools\小程序ID是wx2abc123456789def加密包在桌面。cd /d D:\tools python pc_wxapkg_decrypt.py -i C:\Users\YourName\Desktop\__APP__.wxapkg -o D:\decrypted.wxapkg -id wx2abc123456789def如果一切顺利你会看到“成功”提示并在D:\目录下得到decrypted.wxapkg文件。这个文件已经是标准的、未加密的小程序包了。实操心得路径中的空格 如果文件路径或小程序ID包含空格或特殊字符务必在命令行参数中使用双引号包裹如上例所示。ID确认 最稳妥的方法是查看Applet目录下文件夹的名称而不是凭记忆或猜测。一个错误的小程序ID会导致生成的AES密钥完全错误解密必然失败。错误排查 如果脚本报错“文件头不是V1MMWX”请确认你获取的是否是PC端的包通常从上述路径获取。移动端的包是不加密的不需要此步骤。4. 从解密包到可读源码反编译全流程指南拿到标准的.wxapkg包后接下来的目标就是将其反编译成我们熟悉的WXML、WXSS、JS和JSON文件。这里我推荐使用开源的wxappUnpacker项目它基于Node.js透明且可定制。4.1 反编译环境搭建与工具准备首先你需要安装Node.js运行环境建议版本12以上。然后获取wxappUnpacker工具。# 1. 克隆仓库假设你已安装git git clone https://github.com/xuedingmiaojun/wxappUnpacker.git cd wxappUnpacker # 2. 安装依赖 npm install如果网络问题导致npm install失败可以尝试使用淘宝镜像npm install --registryhttps://registry.npmmirror.com安装完成后工具目录下会有一个核心脚本wuWxapkg.js。4.2 执行反编译命令与结果解析反编译命令非常简单。将我们上一步解密得到的decrypted.wxapkg文件复制到wxappUnpacker目录下然后执行node wuWxapkg.js decrypted.wxapkg或者指定输出目录node wuWxapkg.js decrypted.wxapkg -o ./output执行成功后会在当前目录或指定的./output目录下生成一个以小程序ID或包名命名的文件夹。进去后你就能看到熟悉的源码结构了小程序名/ ├── app-service.js # 小程序的逻辑代码压缩过的 ├── app.json # 小程序全局配置 ├── app.wxss # 全局样式 ├── pages/ # 页面目录 │ ├── index/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── ...其他页面 ├── utils/ # 工具函数 ├── components/ # 自定义组件 └── ...其他资源文件4.3 处理反编译中的常见问题与代码美化反编译过程很少一帆风顺以下是几个我高频遇到的问题及解决方案问题1反编译后JS代码是压缩的难以阅读。这是常态因为小程序上传时会自动压缩代码。我们需要使用JavaScript代码美化格式化工具。使用在线工具 将app-service.js或页面JS文件内容复制到如 https://beautifier.io/ 这类网站进行格式化。使用本地工具 安装js-beautify。npm install -g js-beautify # 格式化单个文件 js-beautify app-service.js -o app-service-beautified.js # 批量格式化目录下所有js文件 find ./output -name *.js -exec js-beautify -r {} \;问题2wuWxapkg.js报错提示某些版本不支持。wxappUnpacker项目可能无法兼容微信开发者工具的所有版本生成的小程序包。可以尝试以下步骤更新工具 确保你使用的是最新的wxappUnpacker代码。寻找分支或修改版 在GitHub上搜索wxappUnpacker的fork或修改版本有些开发者会针对新版本微信进行适配。手动调整 如果错误信息明确可以尝试根据错误提示修改wuWxapkg.js或同目录下的其他.js文件中的解析逻辑。这需要一定的JavaScript和二进制分析能力。问题3反编译出的WXML/WXSS文件有乱码或结构错乱。这种情况较少但偶尔发生。可能的原因是包本身损坏或者反编译工具在解析特定语法时出错。可以尝试用不同的反编译工具交叉验证例如另一个知名的工具CrackMinApp但其为闭源exe需自行权衡安全风险。重点关注主要业务页面的代码一些非核心页面的错误可能不影响整体分析。避坑技巧 对于复杂或大型的小程序尤其是使用游戏引擎如Cocos、Unity开发的反编译出的代码结构可能非常复杂包含大量的webgl、.data、.bin等资源文件。此时wxappUnpacker可能只能解出部分资源。你需要结合专门的游戏资源提取工具如AssetStudio for Unity来进一步分析图片、模型等资源正如参考文章后半部分所提及的那样。这属于更专业的逆向工程领域需要单独研究。5. 高级话题与安全边界探讨完成解密和反编译我们终于看到了“庐山真面目”。但在兴奋之余我们必须清醒地认识到技术行为的边界。5.1 不同引擎小程序的特殊处理微信小程序不仅支持原生开发还能嵌入Cocos Creator、Unity等游戏引擎构建的内容。这类小程序的包结构更为复杂Cocos Creator 反编译后你可能会在assets目录下找到大量的图像、音频资源在jsb-default或src目录下找到游戏的JavaScript逻辑。分析重点在于资源管理和核心游戏循环的代码。Unity WebGL 这类小程序会包含.wasm(WebAssembly) 文件和.data.unityweb.bin等资源包。.wasm文件是编译后的二进制代码逆向难度极大。资源包通常使用Brotli.br后缀压缩。你需要先使用Brotli解压工具如Python的brotli库解压然后再用Unity资源提取工具如AssetStudio来查看纹理、模型等资源。参考文章中提供的批量解压.br文件的Python脚本就是一个非常实用的工具。5.2 逆向工程的法律与道德红线必须强调本文所有技术讨论仅限用于学习、研究和安全审计等合法合规目的。著作权法 小程序源码是开发者的智力成果受著作权法保护。未经授权复制、分发、用于商业用途或制作实质性相似的竞争产品构成侵权。用户协议 使用微信小程序即表示你同意其用户协议其中通常包含禁止逆向工程的条款。合规用途学习研究 分析优秀的UI交互、动画实现或架构设计提升自身技能。安全审计 受委托对自己或公司的小程序进行安全评估查找潜在漏洞如敏感信息硬编码、不安全的通信等。兼容性分析 在获得授权的前提下分析第三方组件或服务的集成方式。绝对禁止将逆向所得代码用于直接抄袭上线、窃取商业逻辑、绕过付费墙、制作外挂或进行任何形式的非法攻击。5.3 从防御者视角看如何保护你的小程序代码既然我们知道了如何逆向那么作为开发者如何更好地保护自己的代码呢完全防止逆向是不可能的但可以提高门槛代码混淆与压缩 使用微信开发者工具自带的“上传时代码压缩”功能是基础。可以考虑更高级的JavaScript混淆工具如UglifyJS、Terser的深度配置或商业混淆方案重命名变量、函数插入无意义代码增加阅读难度。关键逻辑后端化 将核心业务逻辑、算法、加密密钥等放在服务器端通过API接口提供服务。前端只负责展示和交互这样即使前端代码被逆向核心资产依然安全。敏感信息隔离 绝对不要将API密钥、数据库连接字符串等敏感信息硬编码在小程序前端代码中。使用云函数或自有服务器作为中继。定期安全扫描 使用自动化工具扫描自己的小程序包看看反编译后的代码中是否意外泄露了敏感信息。法律手段 在代码注释或用户协议中明确声明版权和禁止逆向的条款。技术是一把双刃剑。通过剖析V1MMWX加密到反编译的完整链条我们不仅掌握了破解一道技术屏障的方法更重要的是我们得以窥见一个大型平台在安全与开放之间的权衡设计。作为开发者理解这些机制能让我们在“攻”与“防”的两端都更加从容。最终将这些知识用于构建更安全、更健壮的应用才是技术探索最有价值的归宿。
PC微信小程序V1MMWX加密包逆向解析:AES+XOR双重加密原理与Python解密实战
发布时间:2026/7/4 16:32:05
1. 项目概述为什么我们需要关注PC微信小程序的加密包如果你是一名前端开发者、安全研究员或者单纯对微信小程序的技术实现感到好奇那么你很可能已经发现直接从PC端微信获取到的小程序包.wxapkg文件和移动端的不太一样。它不再是那个可以直接用反编译工具打开的“源码包”而是变成了一个以V1MMWX开头的、经过加密处理的二进制文件。这个变化背后是微信为了提升小程序代码安全性、防止源码被轻易获取而设置的一道门槛。我最初接触这个需求是因为团队需要分析一个竞品小程序的特定动画实现。在移动端通过一些常规手段比如安卓模拟器还能相对容易地拿到包但PC端却卡在了这层加密上。市面上流传的工具虽然能用但要么报毒要么对命令行不友好更重要的是作为一个技术人如果只知其然会用工具而不知其所以然加密原理心里总是不踏实。于是我决定深入探究一下这个V1MMWX加密的来龙去脉并整理出一套从解密到源码解析的完整、透明且可复现的指南。这篇文章我将带你彻底拆解PC微信小程序包的加密机制。我们不仅会详细解读V1MMWX这个魔幻前缀背后的AES和XOR双重加密算法还会用Python亲手实现一个解密脚本让你摆脱对“360报毒”的第三方工具的依赖。最后我们会将解密后的标准.wxapkg包通过成熟的反编译流程还原成可读的WXML、WXSS和JS源码。整个过程我会穿插我在实际操作中踩过的坑和总结的技巧目标是让你读完就能独立操作真正理解每一个字节的变化。2. 核心原理深度拆解V1MMWX加密的“两层锁”拿到一个PC端的小程序包用十六进制编辑器打开你会看到文件头赫然写着V1MMWX。这六个字节就像一个封印宣告着后面的内容已被加密。要解开它我们必须先理解微信工程师设计的两道“锁”第一道是标准的AES-128-CBC加密第二道是自定义的XOR异或加密。这种组合拳既利用了成熟加密算法的强度又增加了一层自定义的混淆提高了逆向的门槛。2.1 第一层锁基于小程序ID的AES-128-CBC加密微信对包的前1024个字节准确说是前1023字节的数据加上1字节的填充或对齐使用了AES加密。这里有几个关键点需要厘清密钥的生成PBKDF2算法密钥并非直接使用小程序ID而是通过PBKDF2Password-Based Key Derivation Function 2这个密钥派生函数生成的。你可以把它理解为一个“密钥搅拌机”输入一个简单的密码口令和一段随机数据盐经过多次“搅拌”迭代输出一个强度高、随机性好的密钥。这样做的好处是即使小程序ID本身比较简单生成的密钥也会非常复杂能有效抵御暴力破解。 具体参数如下密码Password 小程序ID字符串。例如你的小程序存放在C:\Users\YourName\Documents\WeChat Files\Applet\wx2abc123456789def\那么wx2abc123456789def就是密码。盐Salt 固定字符串saltiest。这是一个硬编码的值在所有PC微信小程序加密中都是一样的。迭代次数Iterations 1000次。决定了“搅拌”的强度。密钥长度dkLen 32字节256位。但AES-128只需要16字节的密钥实际上PBKDF2会生成32字节我们只取前16字节作为AES密钥。哈希算法 通常为HMAC-SHA1。加密模式CBC与初始向量IV加密模式采用的是CBCCipher Block Chaining密码分组链接模式。这种模式的特点是每个明文块在加密前会先与前一个密文块进行异或操作。对于第一个块没有“前一个密文块”所以需要一个初始向量IV。这里的IV是固定的16字节字符串the iv: 16 bytes。固定IV在密码学实践中通常不被推荐因为它可能降低安全性但在这里它成为了加密方案的一部分。所以第一层加密的流程可以概括为小程序ID “saltiest”经过1000次PBKDF2“搅拌”生成32字节的派生密钥取前16字节作为AES-128密钥再配合固定的IVthe iv: 16 bytes对原始.wxapkg文件的前1023字节数据进行CBC模式加密。2.2 第二层锁基于ID字符的简单XOR加密在AES加密了文件头部之后微信对从第1024字节开始即AES加密数据之后的所有剩余数据施加了第二层加密逐字节的XOR异或运算。XOR运算的规则很简单相同为0不同为1。在加密中用一个密钥字节对数据字节进行XOR就能得到密文用同样的密钥字节对密文再次XOR就能还原数据。这里的巧妙之处在于密钥的选择XOR密钥 取小程序ID字符串的倒数第二个字符的ASCII码值。举例 如果小程序ID是wx2abc123456789def其倒数第二个字符是ee的ASCII码是101十六进制0x65那么XOR密钥就是101。特例 如果小程序ID长度小于2理论上几乎不会出现则使用默认密钥0x66十进制102。这意味着从第1024字节开始每一个字节都需要与这个固定的密钥值如101进行XOR运算。这层加密虽然简单但如果没有正确的密钥解密出来的数据将是乱码与第一层AES加密形成了互补。2.3 文件组装V1MMWX的诞生经过上述两步加密后微信按以下顺序组装成最终的__APP__.wxapkg文件写入6字节的固定文件头V1MMWX。写入经过AES-128-CBC加密后的1024字节数据这1024字节包含了加密后的前1023字节明文和可能的填充。写入经过XOR加密后的剩余所有数据。至此一个标准的、未加密的.wxapkg包就变成了我们看到的、带V1MMWX头的加密包。理解了这个结构解密就是其逆过程。注意 这里有一个极易混淆的点。很多文章和工具描述为“加密前1024字节”实际上更准确的说法是原始明文的前1023字节被加密后与必要的填充一起组成了一个1024字节的AES加密块。在解密时我们也是将这1024字节作为一个整体进行AES解密得到前1023字节的原始数据。这一点在自行编写解密代码时至关重要否则会导致解密失败。3. 实战手写Python解密脚本告别“报毒”工具理解了原理我们就可以动手了。依赖第三方exe工具总有心结一是安全问题二是无法定制。下面我将用Python逐步实现解密逻辑你可以将这段代码保存为pc_wxapkg_decrypt.py随时使用。3.1 环境准备与依赖安装首先确保你的Python环境是3.x版本。我们需要两个关键的库pycryptodome用于AES和PBKDF2和argparse用于处理命令行参数。pip install pycryptodomeargparse是Python标准库无需额外安装。3.2 解密脚本核心代码实现脚本的核心是逆向我们刚才分析的加密步骤。我们定义一个解密函数并添加友好的命令行接口。#!/usr/bin/env python3 PC微信小程序加密包V1MMWX格式解密脚本 用法python pc_wxapkg_decrypt.py -i 加密文件路径 -o 输出路径 -id 小程序ID import os import argparse from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 from Crypto.Util.Padding import unpad def decrypt_wxapkg(encrypted_path, output_path, wxid): 解密PC端微信小程序包 :param encrypted_path: 加密的 .wxapkg 文件路径 :param output_path: 解密后的输出文件路径 :param wxid: 微信小程序ID try: with open(encrypted_path, rb) as f: data f.read() # 1. 检查文件头 if not data.startswith(bV1MMWX): raise ValueError(文件头不是V1MMWX可能不是有效的PC端加密wxapkg文件。) # 2. 剥离6字节的V1MMWX头 encrypted_data data[6:] # 3. 生成AES密钥 (PBKDF2) # 参数密码(小程序ID字节串), salt(bsaltiest), 迭代次数1000, 密钥长度32 password wxid.encode(utf-8) salt bsaltiest iterations 1000 dk_len 32 derived_key PBKDF2(password, salt, dk_len, iterations) # AES-128 需要16字节密钥取派生密钥的前16字节 aes_key derived_key[:16] # 固定IV iv bthe iv: 16 bytes # 4. 解密前1024字节 (AES-128-CBC) # 前1024字节是AES加密块 aes_encrypted_block encrypted_data[:1024] cipher AES.new(aes_key, AES.MODE_CBC, iv) decrypted_front_part cipher.decrypt(aes_encrypted_block) # 移除可能的填充PKCS#7填充 decrypted_front_part unpad(decrypted_front_part, AES.block_size) # 注意decrypted_front_part 长度应为1023字节 if len(decrypted_front_part) ! 1023: print(f[警告] AES解密后前部数据长度为 {len(decrypted_front_part)}预期为1023。可能填充方式有误但将继续处理。) # 5. 解密剩余部分 (XOR) # XOR密钥小程序ID倒数第二个字符的ASCII码 if len(wxid) 2: xor_key ord(wxid[-2]) else: xor_key 0x66 # 默认密钥 print(f[警告] 小程序ID长度小于2使用默认XOR密钥 0x{xor_key:02x}) remaining_encrypted_data encrypted_data[1024:] # 将字节数据转换为bytearray以便修改然后进行XOR操作 decrypted_rear_part bytearray(remaining_encrypted_data) for i in range(len(decrypted_rear_part)): decrypted_rear_part[i] ^ xor_key # 6. 合并解密后的数据并写入文件 with open(output_path, wb) as f: f.write(decrypted_front_part) # 写入前1023字节明文 f.write(decrypted_rear_part) # 写入XOR解密后的剩余部分 print(f[成功] 文件已解密并保存至: {output_path}) print(f AES密钥前16字节: {aes_key.hex()}) print(f XOR密钥: {xor_key} (字符: {chr(xor_key) if 32 xor_key 127 else 非打印字符})) except FileNotFoundError: print(f[错误] 找不到输入文件: {encrypted_path}) except ValueError as e: print(f[错误] {e}) except Exception as e: print(f[错误] 解密过程中发生未知错误: {e}) import traceback traceback.print_exc() def main(): parser argparse.ArgumentParser(description解密PC微信小程序加密包V1MMWX格式) parser.add_argument(-i, --input, requiredTrue, help加密的.wxapkg文件路径) parser.add_argument(-o, --output, requiredTrue, help解密后的输出文件路径) parser.add_argument(-id, --wxid, requiredTrue, help微信小程序ID) args parser.parse_args() if not os.path.exists(args.input): print(f错误输入文件 {args.input} 不存在。) return decrypt_wxapkg(args.input, args.output, args.wxid) if __name__ __main__: main()3.3 脚本使用详解与实操示例将上述代码保存后你就可以在命令行中使用了。最关键的一步是如何找到正确的小程序ID和加密包路径。步骤1定位加密包和小程序ID在PC微信中运行一次目标小程序。然后打开文件资源管理器导航至C:\Users\[你的用户名]\Documents\WeChat Files\Applet\在这个目录下你会看到一系列以小程序ID命名的文件夹例如wx2abc123456789def。进入对应文件夹就能找到名为__APP__.wxapkg的加密包。文件夹的名字就是小程序ID。步骤2运行解密脚本假设你的脚本保存在D:\tools\小程序ID是wx2abc123456789def加密包在桌面。cd /d D:\tools python pc_wxapkg_decrypt.py -i C:\Users\YourName\Desktop\__APP__.wxapkg -o D:\decrypted.wxapkg -id wx2abc123456789def如果一切顺利你会看到“成功”提示并在D:\目录下得到decrypted.wxapkg文件。这个文件已经是标准的、未加密的小程序包了。实操心得路径中的空格 如果文件路径或小程序ID包含空格或特殊字符务必在命令行参数中使用双引号包裹如上例所示。ID确认 最稳妥的方法是查看Applet目录下文件夹的名称而不是凭记忆或猜测。一个错误的小程序ID会导致生成的AES密钥完全错误解密必然失败。错误排查 如果脚本报错“文件头不是V1MMWX”请确认你获取的是否是PC端的包通常从上述路径获取。移动端的包是不加密的不需要此步骤。4. 从解密包到可读源码反编译全流程指南拿到标准的.wxapkg包后接下来的目标就是将其反编译成我们熟悉的WXML、WXSS、JS和JSON文件。这里我推荐使用开源的wxappUnpacker项目它基于Node.js透明且可定制。4.1 反编译环境搭建与工具准备首先你需要安装Node.js运行环境建议版本12以上。然后获取wxappUnpacker工具。# 1. 克隆仓库假设你已安装git git clone https://github.com/xuedingmiaojun/wxappUnpacker.git cd wxappUnpacker # 2. 安装依赖 npm install如果网络问题导致npm install失败可以尝试使用淘宝镜像npm install --registryhttps://registry.npmmirror.com安装完成后工具目录下会有一个核心脚本wuWxapkg.js。4.2 执行反编译命令与结果解析反编译命令非常简单。将我们上一步解密得到的decrypted.wxapkg文件复制到wxappUnpacker目录下然后执行node wuWxapkg.js decrypted.wxapkg或者指定输出目录node wuWxapkg.js decrypted.wxapkg -o ./output执行成功后会在当前目录或指定的./output目录下生成一个以小程序ID或包名命名的文件夹。进去后你就能看到熟悉的源码结构了小程序名/ ├── app-service.js # 小程序的逻辑代码压缩过的 ├── app.json # 小程序全局配置 ├── app.wxss # 全局样式 ├── pages/ # 页面目录 │ ├── index/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── ...其他页面 ├── utils/ # 工具函数 ├── components/ # 自定义组件 └── ...其他资源文件4.3 处理反编译中的常见问题与代码美化反编译过程很少一帆风顺以下是几个我高频遇到的问题及解决方案问题1反编译后JS代码是压缩的难以阅读。这是常态因为小程序上传时会自动压缩代码。我们需要使用JavaScript代码美化格式化工具。使用在线工具 将app-service.js或页面JS文件内容复制到如 https://beautifier.io/ 这类网站进行格式化。使用本地工具 安装js-beautify。npm install -g js-beautify # 格式化单个文件 js-beautify app-service.js -o app-service-beautified.js # 批量格式化目录下所有js文件 find ./output -name *.js -exec js-beautify -r {} \;问题2wuWxapkg.js报错提示某些版本不支持。wxappUnpacker项目可能无法兼容微信开发者工具的所有版本生成的小程序包。可以尝试以下步骤更新工具 确保你使用的是最新的wxappUnpacker代码。寻找分支或修改版 在GitHub上搜索wxappUnpacker的fork或修改版本有些开发者会针对新版本微信进行适配。手动调整 如果错误信息明确可以尝试根据错误提示修改wuWxapkg.js或同目录下的其他.js文件中的解析逻辑。这需要一定的JavaScript和二进制分析能力。问题3反编译出的WXML/WXSS文件有乱码或结构错乱。这种情况较少但偶尔发生。可能的原因是包本身损坏或者反编译工具在解析特定语法时出错。可以尝试用不同的反编译工具交叉验证例如另一个知名的工具CrackMinApp但其为闭源exe需自行权衡安全风险。重点关注主要业务页面的代码一些非核心页面的错误可能不影响整体分析。避坑技巧 对于复杂或大型的小程序尤其是使用游戏引擎如Cocos、Unity开发的反编译出的代码结构可能非常复杂包含大量的webgl、.data、.bin等资源文件。此时wxappUnpacker可能只能解出部分资源。你需要结合专门的游戏资源提取工具如AssetStudio for Unity来进一步分析图片、模型等资源正如参考文章后半部分所提及的那样。这属于更专业的逆向工程领域需要单独研究。5. 高级话题与安全边界探讨完成解密和反编译我们终于看到了“庐山真面目”。但在兴奋之余我们必须清醒地认识到技术行为的边界。5.1 不同引擎小程序的特殊处理微信小程序不仅支持原生开发还能嵌入Cocos Creator、Unity等游戏引擎构建的内容。这类小程序的包结构更为复杂Cocos Creator 反编译后你可能会在assets目录下找到大量的图像、音频资源在jsb-default或src目录下找到游戏的JavaScript逻辑。分析重点在于资源管理和核心游戏循环的代码。Unity WebGL 这类小程序会包含.wasm(WebAssembly) 文件和.data.unityweb.bin等资源包。.wasm文件是编译后的二进制代码逆向难度极大。资源包通常使用Brotli.br后缀压缩。你需要先使用Brotli解压工具如Python的brotli库解压然后再用Unity资源提取工具如AssetStudio来查看纹理、模型等资源。参考文章中提供的批量解压.br文件的Python脚本就是一个非常实用的工具。5.2 逆向工程的法律与道德红线必须强调本文所有技术讨论仅限用于学习、研究和安全审计等合法合规目的。著作权法 小程序源码是开发者的智力成果受著作权法保护。未经授权复制、分发、用于商业用途或制作实质性相似的竞争产品构成侵权。用户协议 使用微信小程序即表示你同意其用户协议其中通常包含禁止逆向工程的条款。合规用途学习研究 分析优秀的UI交互、动画实现或架构设计提升自身技能。安全审计 受委托对自己或公司的小程序进行安全评估查找潜在漏洞如敏感信息硬编码、不安全的通信等。兼容性分析 在获得授权的前提下分析第三方组件或服务的集成方式。绝对禁止将逆向所得代码用于直接抄袭上线、窃取商业逻辑、绕过付费墙、制作外挂或进行任何形式的非法攻击。5.3 从防御者视角看如何保护你的小程序代码既然我们知道了如何逆向那么作为开发者如何更好地保护自己的代码呢完全防止逆向是不可能的但可以提高门槛代码混淆与压缩 使用微信开发者工具自带的“上传时代码压缩”功能是基础。可以考虑更高级的JavaScript混淆工具如UglifyJS、Terser的深度配置或商业混淆方案重命名变量、函数插入无意义代码增加阅读难度。关键逻辑后端化 将核心业务逻辑、算法、加密密钥等放在服务器端通过API接口提供服务。前端只负责展示和交互这样即使前端代码被逆向核心资产依然安全。敏感信息隔离 绝对不要将API密钥、数据库连接字符串等敏感信息硬编码在小程序前端代码中。使用云函数或自有服务器作为中继。定期安全扫描 使用自动化工具扫描自己的小程序包看看反编译后的代码中是否意外泄露了敏感信息。法律手段 在代码注释或用户协议中明确声明版权和禁止逆向的条款。技术是一把双刃剑。通过剖析V1MMWX加密到反编译的完整链条我们不仅掌握了破解一道技术屏障的方法更重要的是我们得以窥见一个大型平台在安全与开放之间的权衡设计。作为开发者理解这些机制能让我们在“攻”与“防”的两端都更加从容。最终将这些知识用于构建更安全、更健壮的应用才是技术探索最有价值的归宿。