简介:这是一款面向微信小程序开发者、逆向工程学习者与安全研究人员的免安装解密工具,解压后即可直接运行,省去安装配置与依赖冲突的麻烦,主要用于反编译加密代码、提取资源、分析API调用及辅助调试。压缩包共约2000个文件,整体64.94MB,以2325个js脚本、319个md说明、311个json配置、207个ts源码为主,另含html、ps1、cmd、exe、dll等运行与文档文件,目录结构完整,便于按模块查阅。目前已有4049人学习下载,说明其在开发者社区中具备一定参考价值。借助该工具,读者可将小程序加密代码还原为可读的JavaScript源码,提取图片、音频等资源,梳理微信API调用关系,并在本地模拟运行以排查问题,从而深入理解小程序的内部运作机制,为代码审计、逆向研究与二次开发提供支撑。使用时请遵守开发者协议与相关法律法规,尊重原作者版权。
1. 解压即用的微信小程序解密工具:它到底能拆出什么
上周帮朋友看一个跑路的外包留下的微信小程序,对方只给了个体验版二维码,源码早没了。我翻出这个微信小程序解密工具_免安装版本.zip,解压、双击、选包,几分钟就把__APP__.wxapkg里的业务逻辑还原成了能读的 JavaScript。这就是它存在的意义——当你手里只有一个小程序包,却需要看它到底干了什么的时候,这类解密工具是绕不开的一环。
它解决的核心问题很具体:微信把小程序代码打包成.wxapkg格式,里面的 JS 经过压缩和混淆,普通解压软件打开是一堆乱码。这个免安装版本把反编译、资源提取、API 梳理这几件事打包成一个绿色工具,不用装 Node、不用配 Python 环境,解压就能跑。适合三类人:接手遗留项目的开发者、做代码审计的安全人员、想学习别人实现思路的学习者。但先说清楚,工具本身不判断你拿到的包合不合法,用之前自己心里要有数。
2. 解密工具的技术底座:从 wxapkg 到可读 JS 的完整链路
2.1 wxapkg 包结构长什么样
要理解工具怎么工作,得先知道它拆的是什么。微信小程序的发布包本质上是一个自定义的二进制归档格式,扩展名.wxapkg。它的结构不复杂,但和标准 zip 不一样,所以直接用 7-Zip 或 WinRAR 打开会失败。
一个典型的 wxapkg 文件由三部分组成:
| 区域 | 内容 | 说明 |
|---|---|---|
| 文件头 | 固定字节序列 | 标识这是微信小程序包,工具靠它做格式校验 |
| 索引区 | 文件数量 + 每个文件的元信息 | 记录文件名、偏移量、大小 |
| 数据区 | 实际文件内容 | 压缩或未压缩的 JS、WXML、WXSS、JSON、图片等 |
索引区里每个条目包含文件名长度、文件名、数据偏移、数据长度。工具读取索引后,按偏移量把数据区的内容逐段切出来,再根据文件扩展名决定后续处理方式。JS 文件走反编译流程,图片和音频直接落盘。
这里有个容易翻车的地方:不同基础库版本导出的 wxapkg,索引区的字段顺序和压缩标志位可能有差异。常见做法是工具内部维护一张版本映射表,遇到不认识的标志位就尝试两种解析顺序,哪个能完整读出文件列表就用哪个。你如果自己写解析脚本,建议先把索引区前 16 个字节 dump 出来看看,确认文件数量字段的位置再往下走。
2.2 反编译不是"还原源码",而是"让代码可读"
很多人以为解密工具能把小程序还原成开发者写的原始代码,这是误解。微信在打包时会做几件事:ES6 转 ES5、代码压缩、变量名混淆、部分逻辑内联。工具能做的是把压缩后的代码展开成带缩进的格式,把混淆的变量名替换成可读的占位符,把内联的逻辑重新拆成函数调用。但原始变量名和注释是找不回来的。
工具内部通常调用的是社区维护的反编译引擎,核心流程分三步:
// 伪代码:反编译核心流程 const fs = require('fs'); const path = require('path'); // 第一步:解析 wxapkg,提取所有文件条目 function parseWxapkg(buffer) { const header = buffer.slice(0, 16); // 读取文件头做校验 const fileCount = buffer.readUInt32BE(16); // 索引区第一个字段是文件数量 let offset = 20; const entries = []; for (let i = 0; i < fileCount; i++) { const nameLen = buffer.readUInt32BE(offset); offset += 4; const name = buffer.slice(offset, offset + nameLen).toString(); offset += nameLen; const dataOffset = buffer.readUInt32BE(offset); offset += 4; const dataLen = buffer.readUInt32BE(offset); offset += 4; entries.push({ name, dataOffset, dataLen }); } return entries; } // 第二步:按条目切出文件内容,JS 走反编译,其他直接写盘 function extractFiles(buffer, entries, outDir) { for (const entry of entries) { const content = buffer.slice(entry.dataOffset, entry.dataOffset + entry.dataLen); const outPath = path.join(outDir, entry.name); fs.mkdirSync(path.dirname(outPath), { recursive: true }); if (entry.name.endsWith('.js')) { const readable = decompileJS(content.toString()); // 调用反编译引擎 fs.writeFileSync(outPath, readable); } else { fs.writeFileSync(outPath, content); // 图片、WXML 等原样输出 } } }这段代码的关键参数是fileCount和每个条目的dataOffset。fileCount读错会导致文件列表截断或越界;dataOffset是相对于文件起始位置的绝对偏移,不是相对于索引区结束位置。我见过有人把偏移基准搞错,结果切出来的 JS 全是乱码。如果你自己写脚本,建议先打印前三个条目的name和dataLen,确认文件名正常、长度合理,再批量处理。
2.3 免安装版本怎么做到"解压即用"
免安装的本质是把运行时依赖一起打包。这个工具目录里能看到base.css、prettify.css这类样式文件,说明它带了一个本地 HTML 界面;Ast.js.html、core.js.html、find.js.html、general.js.html、html-traverse.js.html、serialize-node.js.html这些是反编译引擎的各个模块,被拆成独立文件方便按需加载。
它不写注册表、不装系统服务、不改环境变量。所有配置走当前目录下的相对路径,临时文件也落在同目录。这意味着你可以把它放在 U 盘里,换台电脑解压就能继续用。但要注意:如果解压路径里有中文或空格,部分老版本的反编译引擎在拼接文件路径时会出错。我一般建议解压到D:\tools\wxdec这种纯英文短路径下。
3. 实操:从拿到包到跑通反编译的完整步骤
3.1 找到目标小程序的 wxapkg 文件
工具本身不负责"抓包下载",你得先拿到.wxapkg文件。PC 端微信的小程序缓存通常在%APPDATA%\Tencent\WeChat\XPlugin\Plugins\或Documents\WeChat Files\Applet\下,按小程序 AppID 分目录存放。手机端则需要先做数据备份再提取,路径更深。
拿到包之后,先确认文件完整性。一个正常的 wxapkg 文件头几个字节是固定的,用十六进制查看器打开,如果开头不是预期的标识序列,说明文件损坏或被截断。常见情况是抓包时只保存了部分数据,这种包反编译到一半会报"索引越界"。
提示:不同来源的包可能对应不同基础库版本。如果工具报"不支持的包格式",先别急着换工具,用十六进制查看器对比文件头,确认是不是包本身的问题。
3.2 用工具界面完成反编译
免安装版本带本地界面,操作路径很直接:
- 解压 zip 到纯英文路径,比如
D:\tools\wxdec - 双击目录下的入口 HTML 文件,浏览器会打开本地操作界面
- 在文件选择框里选中你的
.wxapkg文件 - 指定输出目录,建议新建一个空文件夹
- 点击"开始解密",等待进度条走完
界面背后调用的就是 2.2 节那套解析加反编译流程。输出目录里会按原始包内路径结构生成文件树,JS 文件已经过格式化处理,WXML 和 WXSS 保持原样,图片资源单独存放。
如果你更习惯命令行,工具目录里的core.js.html对应的核心逻辑可以抽出来单独跑:
# 假设你已经把核心解析逻辑抽成 node 脚本 node wxdec-core.js --input ./target.wxapkg --output ./decoded --format pretty # 参数说明: # --input 待解密的 wxapkg 文件路径 # --output 输出目录,不存在会自动创建 # --format 输出格式,pretty 表示带缩进的可读格式,raw 表示原始内容--format pretty会调用prettify.css配套的格式化逻辑,把压缩成一行的 JS 展开。如果输出文件体积异常小(比如几 KB),大概率是反编译引擎在某个语法节点上卡住后静默退出了,换--format raw先拿到原始内容,再手动用格式化工具处理。
3.3 验证反编译结果是否可用
反编译完成后别急着读代码,先做三个检查:
第一,看文件数量。输出目录里的文件数应该和工具界面显示的"提取文件数"一致。如果少了,说明部分条目解析失败。
第二,打开主入口 JS(通常是app.js或app-service.js),搜索App(和Page(这两个关键调用。如果能看到完整的注册逻辑,说明反编译基本成功。
第三,检查 WXML 文件。WXML 一般不做混淆,反编译后应该和原始结构一致。如果 WXML 里出现大量乱码,说明包在索引解析阶段就出了问题,不是反编译引擎的锅。
我一般还会用grep快速扫一遍 API 调用:
# 统计反编译结果里出现的微信 API 调用 grep -roh "wx\.[a-zA-Z]*" ./decoded --include="*.js" | sort | uniq -c | sort -rn | head -30这条命令列出调用频率最高的 30 个微信 API,能快速判断这个小程序的核心功能方向。比如wx.request出现几百次,说明它重度依赖后端接口;wx.getLocation频繁出现,大概率有定位相关功能。
4. 避坑:反编译过程中最容易翻车的五个点
4.1 现象:工具闪退或界面白屏
原因:解压路径含中文、空格或特殊字符,导致内部模块加载失败。部分老版本引擎在 Windows 上对路径编码处理不完善。
解决:把整个工具目录移到纯英文无空格路径下,比如D:\wxdec。如果仍然白屏,检查浏览器控制台报错,通常是某个.js.html文件没加载成功,确认解压时没有杀毒软件拦截。
4.2 现象:反编译到一半卡住,进度条不动
原因:目标 wxapkg 里某个 JS 文件用了较新的语法特性(如可选链、空值合并),反编译引擎的 AST 解析器版本较旧,遇到不认识的节点就挂起。
解决:先用--format raw跳过格式化步骤,拿到原始 JS 内容。然后用较新版本的格式化工具(如 Prettier)单独处理那个文件。如果 raw 模式也卡住,说明是索引解析阶段的问题,换一个更新版本的解密工具重试。
4.3 现象:输出目录里 JS 文件全是单行,没有换行
原因:--format pretty调用的格式化逻辑依赖prettify.css配套的规则文件,如果规则文件缺失或路径不对,格式化会静默降级为原样输出。
解决:检查工具目录下prettify.css是否存在且非空。如果文件在但仍不生效,手动用 VS Code 打开 JS 文件,右键选择"格式化文档",效果一样。
4.4 现象:WXML 文件能打开但图片全部丢失
原因:图片资源在 wxapkg 里可能以 base64 内联形式存在,也可能作为独立文件条目。如果工具只提取了 JS 和 WXML,图片条目会被跳过。
解决:确认工具设置里"提取所有资源"选项已勾选。如果工具本身不支持,用 2.1 节的解析逻辑自己写一段提取脚本,把所有非 JS/WXML/WXSS 的条目按原始扩展名写盘。
4.5 现象:反编译后的代码能读但逻辑对不上
原因:微信在打包时会做代码分割和懒加载,部分逻辑不在主包里,而是运行时从 CDN 拉取。你反编译的只是主包,分包和动态加载的代码不在其中。
解决:检查小程序目录下是否有多个.wxapkg文件(主包 + 分包)。把所有包都反编译后,按目录结构合并。如果代码里出现require一个不存在的模块,大概率就是分包内容,找到对应的分包文件单独处理。
5. 进阶:把反编译结果变成可检索的代码库
反编译只是第一步,真正要从中挖出有价值的信息,得让代码变得可搜索、可跳转。我一般会做两件事。
第一,用 VS Code 打开输出目录,装一个"Better Search"类插件,对整个目录做全文索引。这样搜wx.request能直接定位到所有网络请求点,搜某个接口路径能追到调用链。比在单个文件里翻效率高一个量级。
第二,写一个简单的脚本,把反编译结果里的 API 调用和页面路由抽成一张表:
import os import re import json api_pattern = re.compile(r'wx\.([a-zA-Z]+)') page_pattern = re.compile(r'Page\(\{') result = {"apis": {}, "pages": []} for root, dirs, files in os.walk("./decoded"): for f in files: if not f.endswith(".js"): continue path = os.path.join(root, f) content = open(path, encoding="utf-8", errors="ignore").read() for api in api_pattern.findall(content): result["apis"][api] = result["apis"].get(api, 0) + 1 if page_pattern.search(content): result["pages"].append(path) # 按调用次数排序输出 sorted_apis = sorted(result["apis"].items(), key=lambda x: -x[1]) print(json.dumps({"top_apis": sorted_apis[:20], "page_count": len(result["pages"])}, indent=2))这段脚本遍历反编译目录,统计每个微信 API 的出现次数,同时记录所有包含Page(注册的文件路径。输出结果能让你在几分钟内摸清一个小程序的骨架:哪些 API 用得最多、有多少个页面、页面文件分布在哪。
参数上唯一需要注意的是errors="ignore",反编译后的文件偶尔会有编码残留,不加这个参数遇到非法字节会直接抛异常中断。另外api_pattern只匹配wx.xxx形式,如果代码里用了const { request } = wx这种解构写法,统计会漏掉,需要额外加一条解构匹配规则。
从那以后我每次拿到反编译结果,都强制先跑一遍 API 统计和页面清单,再决定从哪个文件开始读。这个习惯帮我省掉了大量盲目翻代码的时间。希望帮到你。
本文还有配套的精品资源,点击获取