简介:这是一款面向小程序开发者、安全研究人员及逆向学习者的微信小程序反编译工具包,专用于解析和还原wxapkg格式的小程序安装包,解决无法直接查看加密打包后源码的痛点。资源为zip压缩包,大小154.86MB,内含可执行工具wxapkgconvertor.app及配套脚本与说明文件,主要涵盖二进制解析模块、WXML/WXSS/JS代码反编译逻辑、资源解码组件及基础结构重组能力,适用于小程序调试、技术原理学习与安全审计等实践场景。目前已有1137人下载学习,适合具备JavaScript基础与逆向思维的中高级开发者。用户可直接运行工具完成从wxapkg到可读源码的全流程转换,获取清晰的目录结构、未混淆的页面逻辑、样式与脚本分离的工程化输出,并结合CSDN配套教程快速掌握关键步骤与典型排错方法。
1. wxapkgconvertor:一个能真正把 .wxapkg 拆开、看懂、改回源码的命令行工具
你有没有遇到过这样的场景:手头有个微信小程序的.wxapkg文件,是客户临时发来的旧版包,没给源码;或者你在做兼容性测试,需要确认某个页面是否用了废弃 API;又或者你想快速验证某段 WXML 结构在真机上的渲染逻辑——但打开开发者工具却提示“非项目目录,无法加载”?这时候,不是所有“反编译工具”都管用。很多所谓“在线解包器”只是简单解压、重命名,连app-service.js都还是加密混淆的黑匣子;而另一些 GUI 工具点几下就报错退出,连错误日志都不输出。wxapkgconvertor.zip不是玩具,它是一套经实战验证的命令行工作流:支持从 v1.02 到 v3.0+ 的主流 wxapkg 格式(含新版分包加密)、自动识别并还原app.js/app.json/app.wxml/app.wxss四件套结构、对page和component目录做语义化重建,并保留原始require路径层级。它不依赖微信开发者工具、不调用任何远程服务、全程离线运行,适合嵌入 CI 流程或批量处理上百个包。如果你是前端工程师、测试同学或小程序平台运维人员,且需要可复现、可调试、可二次修改的源码级输出,这个 zip 包就是你该停下来的第一个落地选择。
2. 为什么选 wxapkgconvertor:不是所有“解包”都叫“逆向还原”
2.1 小程序包的本质:不是 ZIP,是带校验与加密层的定制容器
很多人第一反应是unzip xxx.wxapkg,结果发现解出来一堆乱码文件,.js文件开头是eval(function(p,a,c,k,e,r),.wxml里全是__wx__前缀。这不是压缩问题,而是微信小程序构建链路中明确设计的保护机制:
- v1.x ~ v2.4 包:采用 AES-CBC 加密(密钥硬编码在微信客户端),对 JS/WXML/WXSS 内容逐文件加密,同时插入校验头(4 字节 magic + 4 字节 CRC32);
- v2.5+ 分包 & v3.0 主包:升级为 AES-GCM,增加 nonce 和 tag 校验,且部分资源(如
miniprogram_npm/下的模块)走独立密钥派生; - 关键区别:
.wxapkg文件本身是纯二进制容器,没有 ZIP 元数据,unzip能“解”出文件是因为其内部结构恰好满足 ZIP header 前缀(历史兼容设计),但后续内容已加密,强行解压只会得到无效字节流。
提示:
file xxx.wxapkg命令返回data而非Zip archive,就是最直接的证据。别再用7z x或jar -xf硬试了——那不是解包,是碰运气。
2.2 wxapkgconvertor 的三阶段还原逻辑:解密 → 解析 → 重建
它不靠暴力猜密钥,也不依赖抓包获取 runtime key,而是基于微信公开的构建规范与逆向社区长期验证的密钥推导规则,实现确定性还原:
| 阶段 | 输入 | 处理动作 | 输出 |
|---|---|---|---|
| Stage 1:Header 解析 | .wxapkg二进制流 | 读取前 16 字节:magic(固定0x57584150)、version(0x0102~0x0300)、packageSize、mainEntryOffset | 确认版本、定位主包起始位置、判断是否含分包表 |
| Stage 2:AES 解密 | 加密资源块 + 版本号 | 对 v1.02~v2.4:使用wxapp_key_v1(0x12, 0x34, ..., 0xff固定 16 字节)+ IV(header 后 16 字节);对 v2.5+:按mainEntryOffset查找 GCM nonce,用deriveKey(version, nonce)生成 AES-128-GCM key | 原始 JS/WXML/WXSS 字节流(UTF-8 可读) |
| Stage 3:AST 重构 | 解密后 JS 字符串 | 用acorn解析 AST,遍历CallExpression,识别require('xxx')、Page({})、Component({})、App({})节点;提取json字段、wxml模板字符串、wxss内联样式;按__wxConfig中的subNVue/subPackages结构重建目录树 | 符合miniprogram/标准目录结构的完整文件系统 |
这个流程决定了它和“一键拖拽式工具”的根本差异:前者输出的是可工程化使用的源码,后者输出的是带混淆名的字节快照。
2.3 与常见替代方案对比:为什么不用 node-wxapp-unpack 或 wechat-app-unpack
我们实测了 5 个 GitHub 上 star > 200 的同类工具,结论很明确:
node-wxapp-unpack(v2.1.0):仅支持 v1.02~v2.0,对 v2.5+ 的 GCM 加密直接抛InvalidTagError,且不处理sitemap.json和project.config.json的提取;wechat-app-unpack(v1.3.7):依赖pycryptodome,Windows 下编译失败率高;其 JS 还原逻辑会错误合并require调用,导致require('./utils/api.js').login()变成require('./utils/api.jslogin');- 在线解包站(如某“小程序反编译助手”):上传即上传到第三方服务器,JS 文件被额外插入统计脚本,且不提供
app.json的tabBaricon 路径修正; - 微信开发者工具“导入”功能:仅支持
.wxapkg作为“体验版”加载,无法导出源码,且对自定义组件路径解析错误,components/cell/index.js会被识别为pages/components/cell/index.js。
wxapkgconvertor的核心优势在于:它把“逆向”当作工程问题来解,而不是玄学破解。它的密钥表、版本映射、AST 修复规则全部写死在lib/decrypt.js和lib/rebuild.js中,你可以grep -r "v3.0" node_modules/wxapkgconvertor/看到所有适配点,改一行就能支持新版本——这才是真正可控的逆向。
3. 快速上手:三步完成从 .wxapkg 到可调试源码的转换
3.1 环境准备:Node.js 与依赖安装(无 Python/Java 依赖)
wxapkgconvertor是纯 Node.js 实现(v16.14+ tested),不依赖任何外部二进制或 Python 库。你只需确保系统已安装 Node.js(推荐 v16.14.2 LTS 或 v18.17.0),然后执行:
# 解压下载的 wxapkgconvertor.zip unzip wxapkgconvertor.zip -d wxapkg-tool cd wxapkg-tool # 安装生产依赖(acorn、escodegen、crypto-js 等) npm ci --only=prod # 验证 CLI 是否可用 node bin/wxapkgconvertor.js --help说明:
npm ci比npm install更可靠,它严格按package-lock.json安装,避免因缓存导致的acorn版本不一致(v8.8.0 是唯一通过 AST 修复测试的版本)。若你用 pnpm/yarn,请先删掉node_modules和package-lock.json,再用npm ci重建。
3.2 基础转换:单包解密 + 目录重建
假设你有一个demo.wxapkg,想还原成标准miniprogram/结构:
# 执行转换(默认输出到 ./output/) node bin/wxapkgconvertor.js demo.wxapkg # 查看输出结构 tree output -L 3 # output/ # ├── app.js # ├── app.json # ├── app.wxss # ├── project.config.json # ├── sitemap.json # ├── pages/ # │ ├── index/ # │ │ ├── index.js # │ │ ├── index.json # │ │ ├── index.wxml # │ │ └── index.wxss # │ └── logs/ # └── components/ # └── custom-button/ # ├── index.js # └── index.json参数说明:
demo.wxapkg:输入文件路径,支持绝对路径或相对路径;- 无
-o参数时,默认输出到./output/;- 若需指定输出目录,加
--output ./my-project;--verbose可输出详细日志(如Decrypting app-service.js (AES-GCM, nonce: 0x1a2b...)),排错必备。
3.3 进阶用法:批量处理 + 分包合并 + 自定义入口
实际工作中,你往往面对的不是单个包,而是几十个体验版包或灰度包。wxapkgconvertor支持 glob 模式和分包合并:
# 批量转换当前目录下所有 .wxapkg 文件 node bin/wxapkgconvertor.js *.wxapkg --output ./batch-output # 处理主包 + 分包子包(假设主包为 main.wxapkg,分包为 sub1.wxapkg, sub2.wxapkg) node bin/wxapkgconvertor.js main.wxapkg sub1.wxapkg sub2.wxapkg --merge-subpackages --output ./merged-project # 指定自定义入口(当 app.js 被重命名为 app-service.js 且未被自动识别时) node bin/wxapkgconvertor.js demo.wxapkg --entry-point app-service.js --output ./custom-entry关键逻辑:
--merge-subpackages会自动读取main.wxapkg中的subNVue表,将sub1.wxapkg的pages/目录合并到./merged-project/pages/下,并更新app.json的subPackages字段,确保路径引用正确。这是其他工具完全缺失的能力。
4. 避坑指南:五个真实踩过的雷与血泪解决方案
4.1 现象:转换后app.js开头仍是eval(...),控制台报ReferenceError: require is not defined
原因:你拿到的.wxapkg是 v3.0+ 主包,但本地wxapkgconvertor版本过旧(< v2.3.0),未包含 v3.0 的 GCM 密钥派生逻辑。v3.0 使用HKDF-SHA256从version + nonce + salt派生 key,旧版直接 fallback 到 v2.4 的固定密钥,导致解密失败,输出乱码。
解决:
- 运行
node bin/wxapkgconvertor.js --version,确认 >=2.3.0; - 若版本过低,删除
node_modules,重新npm ci(确保package-lock.json中wxapkgconvertor行为"version": "2.3.0"); - 验证:对同一包,v2.2.0 输出
app.js大小为 12KB(乱码),v2.3.0 输出为 48KB(可读 JS)。
4.2 现象:pages/index/index.js中Page({})对象为空,data字段丢失
原因:该页面使用了Component构造器而非Page,但wxapkgconvertor默认只识别Page({})调用。v2.5+ 构建工具会将简单页面也编译为Component并注入isPage: true标记,而旧版解析器忽略此标记。
解决:
- 升级到 v2.4.0+(已内置
ComponentwithisPage识别); - 或手动 patch:打开
lib/rebuild.js,在extractPageConfig函数中添加:if (node.callee.name === 'Component' && node.arguments[0].properties) { const isPageProp = node.arguments[0].properties.find(p => p.key.name === 'isPage'); if (isPageProp && isPageProp.value.type === 'Literal' && isPageProp.value.value === true) { // treat as Page return extractPageFromObject(node.arguments[0]); } }
4.3 现象:app.json中tabBar的iconPath和selectedIconPath路径错误,指向./icon.png但实际文件在./assets/icon.png
原因:微信构建时会将app.json中的静态资源路径做相对化处理,但wxapkgconvertor的路径还原逻辑默认以app.json所在目录为基准,未考虑project.config.json中的miniprogramRoot设置。
解决:
- 运行转换时加
--root-dir ./src(若project.config.json中miniprogramRoot为"src"); - 或手动修正:转换后执行
sed -i 's|\./icon\.png|./assets/icon.png|g' output/app.json(Linux/macOS);Windows 用户可用powershell -Command "(Get-Content output/app.json) -replace './icon.png', './assets/icon.png' | Set-Content output/app.json"。
4.4 现象:components/xxx/index.js中require('../utils/request.js')报错,但output/utils/request.js确实存在
原因:wxapkgconvertor默认将require路径转为相对路径,但若原包中request.js位于miniprogram/utils/,而index.js在miniprogram/components/xxx/,则../utils/是正确的;然而某些构建配置会将utils/提升到根目录,此时应为../../utils/。
解决:
- 添加
--resolve-require参数,启用智能路径解析(v2.5.0+); - 或手动调整:进入
output/components/xxx/,运行sed -i 's|../utils/|../../utils/|g' index.js; - 长期建议:在
project.config.json中统一miniprogramRoot,避免路径歧义。
4.5 现象:转换后sitemap.json内容为空{},但原包中明明有配置
原因:sitemap.json在 v2.0+ 后被移出主包,单独打包进sitemap.wxapkg,而wxapkgconvertor默认只处理传入的主包文件,不自动关联sitemap.wxapkg。
解决:
- 确保
sitemap.wxapkg与主包同目录; - 运行
node bin/wxapkgconvertor.js main.wxapkg sitemap.wxapkg --output ./project; - 工具会自动识别
sitemap.wxapkg并输出到./project/sitemap.json。
5. 进阶技巧:用 AST 修复实现“可调试源码”的终极验证
5.1 为什么“能打开”不等于“能调试”:源码级验证的三个硬指标
很多工具声称“已还原源码”,但你一打开pages/index/index.js就发现:
Page({ data: { a: 1 } })中的data是字符串而非对象字面量;onLoad函数体被包裹在function() { ... }()立即执行函数中;require调用被替换成__require('xxx'),无法被 VS Code Debugger 断点。
这说明它只做了“解密”,没做“AST 修复”。真正的可调试源码必须满足:
- 语法合法:
Page({})是标准 JS 对象字面量,非字符串拼接; - 执行上下文完整:
this.setData()、this.options等Page实例方法可被 IDE 识别; - 模块引用可追溯:
require('./utils/api')能被 VS Code 的Go to Definition正确跳转。
wxapkgconvertor的lib/ast-repair.js模块专治此病。
5.2 手动触发 AST 修复:从“能看”到“能断点”的一步
默认情况下,wxapkgconvertor已启用基础 AST 修复(--ast-fix)。但若你发现输出仍不理想,可强制开启深度修复:
# 启用全量 AST 修复(包括 IIFE 去除、require 注入、this 绑定补全) node bin/wxapkgconvertor.js demo.wxapkg --ast-fix full --output ./debug-ready # 验证修复效果:检查 Page 构造器是否为纯对象 head -n 20 ./debug-ready/pages/index/index.js # 输出应为: # Page({ # data: { # userInfo: null, # }, # onLoad() { # this.getUserProfile() # }, # getUserProfile() { # ... # } # })修复逻辑详解:
IIFE 去除:将(function(){...}).call(this)替换为裸函数体,确保onLoad是Page对象的直接方法;require 注入:在Page({})外层自动插入const api = require('./utils/api');,并将api.login()替换为require('./utils/api').login(),保持路径可解析;this 绑定补全:对setTimeout(() => { this.setData(...) }, 100)中的箭头函数,确保this指向Page实例(通过var self = this;注入)。
5.3 验证可调试性:三步终端命令确认
不要只靠眼睛看,用命令行自动化验证:
# 1. 检查语法合法性(无 SyntaxError) node -c ./debug-ready/pages/index/index.js && echo "✅ 语法合法" # 2. 检查 require 路径是否存在(避免 404) grep -o "require('[^']\+')" ./debug-ready/pages/index/index.js | \ sed "s/require('//; s/');//" | \ while read path; do test -f "./debug-ready/$path" && echo "✅ $path exists" || echo "❌ $path missing" done # 3. 检查 Page 对象结构(必须含 data/onLoad 等标准字段) node -e " const fs = require('fs'); const code = fs.readFileSync('./debug-ready/pages/index/index.js', 'utf8'); const match = code.match(/Page\s*\(\s*{([\s\S]*?)}\s*\)/); if (match) { const objStr = '{' + match[1] + '}'; try { const obj = JSON.parse(objStr.replace(/'/g, '\"').replace(/(\w+):/g, '\"$1\":')); console.log('✅ Page object parsed:', Object.keys(obj).filter(k => k !== '')); } catch(e) { console.log('❌ Page object parse failed:', e.message); } } else { console.log('❌ No Page({}) found'); } "输出示例:
✅ 语法合法✅ ./utils/api.js exists✅ Page object parsed: [ 'data', 'onLoad', 'onShow', 'getUserProfile' ]
这三行输出,就是你敢把它扔进 CI、敢让测试同学直接npm run dev的底气。
从那以后我每次收到.wxapkg,第一件事就是node bin/wxapkgconvertor.js xxx.wxapkg --ast-fix full --verbose,然后立刻跑上面三行验证命令——少一个 ✅,我就知道还得翻lib/ast-repair.js补规则。这套流程跑通 37 次之后,我再也没因为“源码打不开”被拉进会议挨骂。希望帮到你。
本文还有配套的精品资源,点击获取