微信小程序自动化审计
主要是记录一下近期对小程序安全的研究和自动化审计思路
小程序解包反编译
微信小程序缓存位置
1. Windows
C:\Users\{用户名}\Documents\WeChat Files\Applet\
C:\Users\{用户名}\Documents\WeChat Files\{微信ID}\Applet\
文件结构:
Applet/
├── {appid}/
│ ├── __APP__.wxapkg # 主包
│ ├── __SUBPKG__*.wxapkg # 分包
│ └── ...
2. Linux
这个没做过😁
3. macOS
变化太快了每个版本都不太一样,微信版本已经普遍采用 users/<账号哈希>/applet/packages 这种多账号隔离结构
# 新版微信
~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/Applet/
# 或
~/Library/Containers/com.tencent.xinWeChat/Data/Documents/Applet/
~/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/WeChat/Applet/
但是一个最新版大概模型如下
radium/users/<账号哈希>/applet/packages
│
└── <AppID>
│
└── <版本>
├── __APP__.wxapkg
│ └── 主包
│ ├── App 启动逻辑
│ ├── 主页面
│ ├── tabBar
│ └── 分包清单
│
├── xxxx.wxapkg
│ └── 普通分包
│ └── 进入页面时下载
│
├── yyyy.wxapkg
│ └── 独立分包
│ └── 可不加载主包启动
│
└── zzzz.wxapkg
└── 插件或其他资源包
这里为 MacOS 做了一个快速查找 wxapkg的软件(https://github.com/bx33661/WxLocated),详见下面的WxLocated章节

微信小程序解包反编译工具
对于微信 4.0 版本网上已经有很多成熟方案了
这个项目目前比较成熟基于 Go 语言开发
https://github.com/wux1an/wxapkg
具体效果(但是这个 mac 端的没办法很快识别出哪个是哪个,可以用上面的我们的wxlocator)

wxapkg 分析思路
对于 wxapkg 包做逆向分析其实像前端安全分析,多了一层这个”微信运行时和客户端容器”,主要是做静态分析找点,然后再去动态尝试挖大东西
微信小程序逆向分析
WXML + WXSS + JavaScript + 微信客户端运行时
很常见思路就是找东西,硬编码,api 端口这些,我们就可以伪造凭证之类的,后利用之类的
这里有一个类比表格,感觉适合没做过小程序开发的快速类比理解
| Web 前端 | 微信小程序 |
|---|---|
| HTML | WXML |
| CSS | WXSS |
| JavaScript | JavaScript |
| 浏览器 DevTools | 微信开发者工具、客户端调试 |
| localStorage | wx.setStorage |
| fetch / axios | wx.request |
| WebSocket | wx.connectSocket |
| 页面路由 | navigateTo / redirectTo |
| iframe | web-view |
| npm 打包文件 | app-service.js |
| source map | 小程序 source map |
| webpack chunk | 主包、分包 |
开发者在小程序开发平台,写项目
app.js
app.json
app.wxss
pages/
index/
index.js
index.json
index.wxml
index.wxss
上传到微信平台后,这些内容被编译,压缩,打包最终形成:
__APP__.wxapkg
然后我们用户端下载这个包后,会:
- 解析 wxapkg 文件;
- 解包资源;
- 加载小程序配置;
- 初始化 JavaScript 逻辑层;
- 渲染 WXML/WXSS 页面。
从结构上来说,.wxapkg 文件以一个简单的文件头开始,包含索引信息,记录了每个子文件在包中的偏移量和大小
.wxapkg 是微信自己的包格式,常见结构大概是:
包头
文件索引表
文件内容区
包尾标记
不能用 unzip 直接解压
也就是说,它更像一个自定义文件系统:

.wxapkg
├── 文件名 1 → offset / size
├── 文件名 2 → offset / size
├── 文件名 3 → offset / size
└── body data
所以一般的解包器就是要先读索引表,知道每个文件名、偏移、大小,然后再从 body 区把文件切出来
wxapkg通常有什么
全局文件
app-config.json
app-service.js
app-wxss.js
common.app.js
不同微信版本、基础库版本和编译模式下,文件名可能不同
页面资源
pages/index/index.js
pages/index/index.json
pages/index/index.wxml
pages/index/index.wxss
但在实际编译包中,页面代码可能被合并。例如多个页面的 JavaScript 逻辑可能全部打包进:
app-service.js
多个页面样式可能被合并进:
app-wxss.js
静态资源
images/logo.png
assets/icon.svg
static/banner.jpg
配置信息
经常可以找到:
app.json
app-config.json
project.config.json
不过 project.config.json 通常属于开发工具本地配置,不一定会被打进线上包
主包和分包
对于大型一点的小程序不止一个wxapkg,采用的是分包机制
主要逻辑大概是这样
一个小程序版本
├── 主包:启动时通常必须加载
├── 普通分包:进入对应页面时按需加载
├── 独立分包:可以不加载主包直接启动
└── 其他包:插件包、小游戏资源包、运行时包等
比如说
packages/
└── wx1234567890abcdef/ # 小程序 AppID
├── 12/ # 小程序版本或本地版本编号
│ ├── __APP__.wxapkg
│ ├── xxxxx.wxapkg
│ └── yyyyy.wxapkg
└── 13/
├── __APP__.wxapkg
└── xxxxx.wxapkg
1. 主包
核心包,通常负责
主包
├── 默认启动页面
├── tabBar 页面
├── app.js / App() 启动逻辑
├── app.json / 全局配置
├── app.wxss / 全局样式
├── 公共组件
├── 公共工具代码
└── 所有分包都可能使用的公共资源
常见名称:
__APP__.wxapkg
开发示例,可以看一下下面的这个 json
{
"pages": [
"pages/index/index",
"pages/profile/profile"
],
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
}
]
}
}
2. 普通分包
分包逻辑
主包已经运行
↓
跳转 packageOrder/pages/list/list
↓
检查订单分包是否已缓存
↓
没有缓存则下载对应分包
↓
加载完成后展示页面
只有用户进入对应页面时,微信才会加载。
{
"pages": [
"pages/index/index"
],
"subPackages": [
{
"root": "packageOrder",
"name": "order",
"pages": [
"pages/list/list",
"pages/detail/detail"
]
},
{
"root": "packageShop",
"name": "shop",
"pages": [
"pages/list/list",
"pages/detail/detail"
]
}
]
}
所以这个就是为啥我们有时候第一次打开首页时候,本地只缓存了__APP__.wxapkg,因此我们得全面展开之后才陆续出现其他.wxapkg文件
核心思路就是”按需加载”,默认启动时下载主包,进入分包页面时再下载对应分包
小程序生命周期
小程序的生命周期不是传统的 open、close
App
├── onLaunch
├── onShow
├── onHide
└── onError
Page
├── onLoad
├── onShow
├── onReady
├── onHide
└── onUnload
Component
├── created
├── attached
├── ready
├── moved
└── detached
用户点击右上角关闭或者切换应用后,小程序可能只是进入后台,而不是立即销毁;
但是后台运行期间部分能力会受限制
这里可以关注
onLoad(options)
onShow()
onLaunch(options)
很多鉴权、参数解析和接口请求都写在这些函数里
微信开发者工具
相当于apple 的 xcode 和安卓的android studio
官网下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

微信小程序抓包
这个我平常使用 Proxifier + Yakit
这个就是动态测试,这里不过多笔墨
WxLocated(Mac原生小程序安全分析工具)
https://github.com/bx33661/WxLocated
借助Codex几天完成的一个Mac系统工具,参考了社区中成熟的方案,采用Swift+SwiftUI
因为发现对于Mac的小程序工具都差强人意
主要定位是:构建一个原生轻量化的小程序安全平台
- 快速定位小程序缓存位置
- 实现wxapkg包元信息的提取,帮助安全研究人员判断wxapkg归属
- 实现基本的解包能力,实现静态分析
- 实现程序级别的源码恢复和复原(作为Agent自动化审计的输入)
- 用户友好的UI和其他额外功能
- …
主要能力如下
扫描 macOS 上常见的 3.x、4.x 和多账户小程序缓存目录
识别 AppID、名称、Logo、版本、主包和分包
原生读取标准 wxapkg 与 V1MMWX 微信本地加密缓存包
将当前版本主包与分包组合为统一文件树
按需预览配置、脚本、页面、样式、文本和图片
检查包头、索引、文件边界、重复路径并计算 SHA-256
汇总页面、组件、能力调用线索与网络域名
安全导出单个文件或全部包内容,并生成来源及哈希清单
静态恢复 app.json、页面配置、JavaScript 模块、WXS、WXML 与 WXSS
恢复 WXML 条件链与循环嵌套优先级,清理 WXSS 中完全重复的声明并保留前缀回退
精确取回 Source Map 内嵌源码,并为推导结果标记来源和置信度
上下文感知地美化 JavaScript,保持复合运算符、字符串、正则和模板语义
为每个重建文件生成质量分,定位结构异常、编译器残留、超长行与待复核结果
从重建源码提取接口、请求参数、依赖、敏感值与能力调用线索
从源码线索一键跳转到对应文件与证据行,并在代码区高亮定位
一键导出重建工程、输入包指纹、逐文件哈希和源码分析 JSON
!!!!!!所有解析均在本机完成。应用不执行包内代码,也不主动发送网络请求。
扫描wxapkg包,这个会把这几代路径全部覆盖,但是Mac上有时候需要赋予权限

具体样式,SwiftUI还是比较夯的

重建代码,参考社区的还原逻辑,支持批量导出等

这里后续想要更完美更有语义需要借助agent还原逆向一下
!xcode的UI设计挺好看的,但是实际开发体验很差

手动编译
cd .../WxLocated
### Debug 编译
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcodebuild build \
-project WxLocated.xcodeproj \
-scheme WxLocated \
-configuration Debug \
-destination 'platform=macOS' \
-derivedDataPath ./DerivedData \
CODE_SIGNING_ALLOWED=NO
运行:
open ./DerivedData/Build/Products/Debug/WxLocated.app
### Release 编译
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcodebuild build \
-project WxLocated.xcodeproj \
-scheme WxLocated \
-configuration Release \
-destination 'platform=macOS' \
-derivedDataPath ./DerivedData \
CODE_SIGNING_ALLOWED=NO
Agent自动化审计思路
项目地址在这里
https://github.com/bx33661/miniapp-audit-skills
首先这里我自己写了一套小程序审计SKILLS套件,框架如下
miniapp-audit (主编排器)
├── material-check (材料检查)
├── attack-surface (攻击面分析)
├── secret-scan (凭证扫描)
├── authz-analyzer (权限分析)
├── payment-audit (支付安全)
├── dataflow-tracer (数据流追踪)
├── privacy-compliance (隐私合规)
├── dynamic-tester (动态测试)
├── code-restorer (代码还原)
└── report-builder (报告生成)---markdown only
一些构造思路如下
完整性感知
我的思路是这样的,就是真正全面深入分析小程序的审计的SKILLS应该是:
给它 wxapkg / 微信缓存目录 / 反编译源码
↓
它自己判断当前材料够不够
↓
自己选择工具和分析路径
↓
自己建立攻击面
↓
自己生成验证任务
↓
自己排除误报
↓
输出可继续执行的证据和脚本
整体思路是,作为我们的助手,需要有一个完整性感知,不是拿到什么就扫什么,必须要先判断信息和资源是否全面
是否存在主包
声明了几个分包
实际拿到几个分包
是否存在独立分包
是否混入旧版本分包
插件包是否缺失
反编译是否完整
上述几个问题很重要,也是传统审计容易忽略的问题,在不全面的信息上做审计本身就是收益不高的事情
渐进式逆向还原
还有一个点就是代码逆向与还原,我们就算使用网上成熟项目解包反编译的还原代码,得到的结果有时候还是差强人意,

Agent最擅长的就是逆向还原,我在SKILLS中强调了”渐进式逆向还原”
用户输入:反编译目录(可能质量不完美)
↓
material-check 检测质量
↓
质量评估
↓
┌────────┴────────┐
│ │
完美 有缺陷
│ │
直接审计 询问用户
↓
是否需要Agent逆向还原?
↓
┌─────┴─────┐
是 否
│ │
Agent还原 标注限制继续审计
Agent级别逆向还原的代码不仅有助于我们人工审计还有利于语义级别分析
能达到效果就是
1. 变量名推断: _0x1a2b → userToken (上下文语义分析)
2. 代码格式化: 压缩代码 → 规范缩进
3. 注释生成: 自动推断函数用途
4. 并行处理: Workflow 并行还原多文件
等等
证据优先
同时既然面向Agent自动化审计,我们目前就不能只依靠上下文记忆,我一直喜欢强调”证据优先”这个思路,我们需要去让Agent维护一个结构化状态,比如我下面这个初步设立的状态数据结构
{
"facts": [],
"hypotheses": [],
"confirmed_findings": [],
"rejected_findings": [],
"blocked_tasks": [],
"missing_artifacts": [],
"coverage": {},
"next_actions": []
}
在审计中不断补充和推进,效果如下
{
"hypothesis": "订单详情可能存在水平越权",
"evidence": [
"orderId 来源于 onLoad.options.id",
"请求使用当前用户 token",
"前端未校验订单 owner"
],
"missing_evidence": [
"第二个账号",
"账号 B 的订单 ID"
],
"status": "NEEDS_SECOND_ACCOUNT"
}
这样在多Agent、Agent Team、即使经历很多轮工具调用,也不容易造成常见的问题,比如说
- 重复分析
- 遗忘已验证结论
- 把猜测写成漏洞
- 前后结论冲突
这里值得一提的是维护了一个artifact-manifest.json
{
"artifact_id": "pkg-001",
"type": "wxapkg",
"role": "main_package",
"appid": "wx1234567890",
"version": "42",
"source": "wechat_macos_cache",
"original_path": "/path/to/__APP__.wxapkg",
"sha256": "xxx",
"size": 1837462,
"collected_at": "2026-08-01T14:00:00+08:00",
"toolchain": {
"extractor": "tool-name",
"extractor_version": "1.2.0"
},
"status": "EXTRACTED",
"warnings": []
}
让整个流程知道每份材料来自哪里,防止混淆和干扰
同时所有的finding 也反向关联证据
{
"evidence_refs": [
"pkg-001:pages/order/detail.js:120-146",
"request-017",
"response-019"
]
}
我们人工复审的时候能回溯到原材料,这样闭环形成一个证据链条
身份矩阵
对于API审计,权限问题是很常见的,也是审计的重中之重
我在设计这个SKILLS套件的时候,建立了身份矩阵这个意识,就是说
Skill 不应只支持”一个 Token”。
它应该理解:
匿名用户
账号 A
账号 B
会员
商户
员工
管理员
不同租户
自动建立验证矩阵:
A Token + A Resource
A Token + B Resource
B Token + A Resource
无 Token + A Resource
过期 Token + A Resource
普通用户 Token + 管理接口
租户 A Token + 租户 B Resource
同时区分:
认证问题
水平越权
垂直越权
跨租户越权
资源不存在
资源公开访问
否则很容易把公开资源误报成越权,同时也让Agent去明白和注意这个问题
总结
本文系统性地介绍了微信小程序安全审计的完整流程,从基础的wxapkg解包、分析思路,到Mac平台原生工具WxLocated的开发,再到基于Agent的自动化审计框架设计。
核心思路包括:
- 完整性感知 - 确保审计材料的完整性
- 渐进式逆向还原 - 利用Agent提升代码还原质量
- 证据优先 - 建立结构化审计状态和证据链
- 身份矩阵 - 全面的权限测试矩阵
这套方法论不仅适用于小程序审计,也可以推广到其他前端应用的安全测试中。

评论区
使用 GitHub Discussions 驱动,欢迎留言交流。