微信小程序反编译完整教程(Windows 新手版)

367 阅读9分钟

微信小程序反编译完整教程(Windows 新手版)

本文基于一次真实的完整实操整理,包含全部步骤、踩过的 8 个坑及解决方案。 环境:Windows 11 + PowerShell 5.1 + Node.js v22 + 微信 4.x + 微信开发者工具 1.06


⚠️ 使用前必读:法律边界

只在以下场景使用:

  • ✅ 反编译你自己开发的小程序(源码丢失、找回历史版本)
  • ✅ 有书面授权的安全评估、渗透测试
  • ✅ 学习研究小程序打包与编译原理

禁止:

  • ❌ 反编译他人商业小程序并使用/发布其代码 —— 涉及著作权侵权
  • ❌ 破解付费功能、绕过业务校验
  • ❌ 窃取他人业务逻辑或数据接口

反编译的技术门槛不高,但法律责任是真实的。本教程默认你在前两种合法场景下操作。


目录

  1. 原理速览
  2. 环境准备
  3. 第一步:定位 wxapkg 包
  4. 第二步:判断加密状态
  5. 第三步:解密解包
  6. 第四步:导入开发者工具
  7. 常见问题大全(8 个真实坑)
  8. 方法论总结
  9. 工具清单

一、原理速览

1.1 小程序是怎么存在于你电脑上的

当你在微信里打开一个小程序,微信会把它的代码包下载到本地缓存,文件后缀是 .wxapkg。反编译就是把这个包还原成可编辑的工程源码。

获取 .wxapkg → 解密 → 解包 → 还原工程结构 → 导入开发者工具验证

1.2 包的结构

文件含义
__APP__.wxapkg主包 — 小程序主体代码
_pages_xxx_.wxapkg分包 — 按页面拆分出去的部分

关键:主包和分包必须同一版本、全部凑齐才能还原出完整工程。

1.3 源码去哪了

小程序编译后,源码被打散成这几个文件:

编译产物还原出什么
app-service.js所有 .js 逻辑代码
page-frame.js / app-wxss.js所有 .wxml 视图 + .wxss 样式
app-config.jsonapp.json 和各页面 .json 配置

其中 wxml 最难还原 —— 它被编译成了名为 $gwx 的 JS 生成函数,需要反解视图树。这也是后面最大的坑所在。


二、环境准备

2.1 需要装的东西

node -v          # 必需,>= 16
git --version    # 必需

2.2 建立工作目录

New-Item -ItemType Directory -Force -Path "C:\work\decompile" | Out-Null
cd C:\work\decompile

后文都以 C:\work\decompile 为工作目录,可换成你自己的路径。


三、第一步:定位 wxapkg 包

3.1 先让小程序被缓存

打开电脑版微信 → 搜索并打开目标小程序 → 多点几个页面(确保主包和分包都加载过)→ 关闭。

不打开就没有缓存,后面搜不到任何东西。

3.2 找到缓存目录

不同微信版本路径不同:

微信版本缓存路径
微信 4.x(个人版)%APPDATA%\Tencent\xwechat\radium\users\<用户哈希>\applet\packages\<AppId>\<版本号>\
微信 3.x(老版本)Documents\WeChat Files\Applet\<AppId>\
企业微信Documents\WXWork\<ID>\Applets\Applet\<AppId>\<版本>\

直接全盘搜索最省事:

Get-ChildItem -Path "C:\" -Recurse -Filter "*.wxapkg" -ErrorAction SilentlyContinue |
    Select-Object FullName, Length, LastWriteTime |
    Sort-Object LastWriteTime -Descending

搜索较慢,耐心等 1-3 分钟。也可以只搜微信目录:

Get-ChildItem -Path "$env:APPDATA\Tencent","$env:LOCALAPPDATA\Tencent" -Recurse -Filter "*.wxapkg" -ErrorAction SilentlyContinue |
    Select-Object FullName, Length, LastWriteTime |
    Sort-Object LastWriteTime -Descending

3.3 锁定你的小程序 AppId

缓存目录下有一堆 AppId(你用过的所有小程序)。用这个脚本按最近使用排序:

# 把 <用户哈希> 换成你实际的那一串
$root = "$env:APPDATA\Tencent\xwechat\radium\users\<用户哈希>\applet\packages"
Get-ChildItem $root -Directory | ForEach-Object {
  $files = Get-ChildItem $_.FullName -Recurse -Filter *.wxapkg -ErrorAction SilentlyContinue
  $latest = $files | Sort-Object LastWriteTime -Descending | Select-Object -First 1
  [PSCustomObject]@{
    AppId  = $_.Name
    包数量 = $files.Count
    最近修改 = $latest.LastWriteTime
    总大小KB = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1KB)
  }
} | Sort-Object 最近修改 -Descending | Format-Table -AutoSize

最近修改时间最新的那个,就是你刚打开的小程序。 AppId 也能在微信公众平台后台查到。

3.4 把包拷到工作区

$src = "$env:APPDATA\Tencent\xwechat\radium\users\<用户哈希>\applet\packages\<你的AppId>"
Copy-Item -Path $src -Destination "C:\work\decompile\<你的AppId>" -Recurse -Force

拷贝而不是直接操作原目录 —— 避免误删微信缓存。

3.5 确认包的完整性

Get-ChildItem "C:\work\decompile\<你的AppId>" -Recurse -Filter *.wxapkg |
  Select-Object @{n='版本目录';e={$_.Directory.Name}},
                Name,
                @{n='KB';e={[math]::Round($_.Length/1KB)}},
                LastWriteTime |
  Sort-Object 版本目录, Name | Format-Table -AutoSize

检查要点:

  • 找到包含 __APP__.wxapkg 且文件数最多的那个版本目录
  • 主包 + 全部分包必须在同一个版本目录
  • 如果散落在多个版本目录(如 651662),用最新且最全的那一套

四、第二步:判断加密状态

$pkg = "C:\work\decompile\<你的AppId>\<版本号>\__APP__.wxapkg"
$bytes = [System.IO.File]::ReadAllBytes($pkg)[0..15]
"前16字节(十六进制): " + (($bytes | ForEach-Object { $_.ToString('X2') }) -join ' ')
"前16字节(可见字符): " + ([System.Text.Encoding]::ASCII.GetString($bytes) -replace '[^\x20-\x7E]','.')
文件头含义处理方式
56 31 4D 4D 57 58(V1MMWX)PC 微信加密包需要用 AppId 解密
第一字节 BE未加密标准包可直接解包

为什么 AppId 是解密密钥的来源:PC 微信用 AppId 参与派生密钥。这也意味着 —— 你必须知道 AppId 才能解密,天然限定在"你能识别的小程序"上。


五、第三步:解密解包

5.1 工具选择

工具优点缺点
unveilr对各种 wcc 编译器版本兼容性最好,wxml 还原成功率高npm 包已下架,需从 fork 源码构建
KillWxapkg开箱即用的 exe,自动化程度高对老版本 wcc 的 wxml 还原会失败(见坑 #1)

推荐:直接用 unveilr。 想先快速验证能否解密,可以先跑一遍 KillWxapkg。


5.2 方案 A:KillWxapkg(快速上手)

下载github.com/Ackites/Kil… → 下载 Windows amd64 版 → 改名为 KillWxapkg.exe 放到工作目录。

⚠️ 第三方开源二进制,建议先杀软扫描。

cd C:\work\decompile
.\KillWxapkg.exe -h    # 先看参数

.\KillWxapkg.exe -id <你的AppId> `
                 -in "C:\work\decompile\<你的AppId>\<版本号>" `
                 -out "C:\work\decompile\output" `
                 -restore -pretty -noClean
参数作用
-idAppId(解密密钥来源)
-in输入目录(整个版本目录,主包分包一起)
-out输出目录
-restore还原工程目录结构
-pretty美化代码
-noClean保留中间文件(page-frame.js/app-wxss.js)—— 排查问题的关键,强烈建议加上

5.3 方案 B:unveilr(推荐,wxml 还原正确)

步骤 1:克隆源码

原仓库 r3x5ur/unveilr 已失效,npm 包也下架了(npm i -g unveilrENOVERSIONS),需要用 fork:

cd C:\work\decompile
git clone https://github.com/hzzheyang/unveilr-v2.0.0.git unveilr

备用 fork:

步骤 2:安装依赖
cd C:\work\decompile\unveilr
npm install --legacy-peer-deps --registry=https://registry.npmmirror.com

--legacy-peer-deps 是必需的 —— 项目的 rollup 版本和 rollup-plugin-terser 有 peer 冲突(见坑 #3)。

步骤 3:配置参数(重要!)

unveilr 用 ts-node 跑源码时会进入开发模式,直接忽略命令行参数(见坑 #4)。所以要把参数写进配置文件。

编辑 src\utils\getConfigurator.ts,把 wx: { ... } 那段改成:

    wx: {
      appid: '你的AppId',
      format: false,
      clearDecompile: true,
      clearSave: true,
      parse: true,
      depth: 1,
      output: 'C:/work/decompile/output',
      packages: ['C:/work/decompile/你的AppId/版本号'],
    },

format 必须设为 false! 设为 true 会格式化代码,把 wxml 表达式里的字符串折断成多行导致编译失败(见坑 #2)。

路径用正斜杠 /,避免转义问题。

步骤 4:运行
cd C:\work\decompile\unveilr
npm run run

不要加任何命令行参数 —— 参数已写死在代码里,加了反而会被当成路径。

步骤 5:验收(最重要的一步!)

别只看文件数量,一定要看内容:

$all = Get-ChildItem "C:\work\decompile\output" -Recurse -Filter *.wxml -File
"总数: " + $all.Count
"空的: " + ($all | Where-Object Length -eq 0).Count
"平均KB: " + [math]::Round(($all | Measure-Object Length -Average).Average / 1KB, 1)
# 抽查真实内容
Get-Content "C:\work\decompile\output\pages\home\index.wxml" -TotalCount 10 -Encoding UTF8
现象判断
空文件 0 个,平均 2-5 KB,内容是 <view class="..."> 标签树成功
大量 0 字节,或内容是 <text>页面路径</text>失败(占位符,见坑 #1)

六、第四步:导入微信开发者工具

6.1 导入前的必要修复

反编译产物是编译后的成品,有些配置是运行时格式,需要转回源码格式。用这个脚本一次修完:

$root = "C:\work\decompile\output"
$utf8 = New-Object System.Text.UTF8Encoding $false

# --- 1. app.json 三处修复 ---
$appPath = Join-Path $root "app.json"
$app = Get-Content $appPath -Raw -Encoding UTF8 | ConvertFrom-Json

# 1a. componentFramework 从对象改成字符串
if ($app.componentFramework -and $app.componentFramework -isnot [string]) {
    $app.componentFramework = "exparser"
}

# 1b. plugins 里删掉运行时字段 subpackage
if ($app.plugins) {
    foreach ($p in $app.plugins.PSObject.Properties) {
        $p.Value.PSObject.Properties.Remove('subpackage')
    }
}

# 1c. pages 里移除插件页面
$app.pages = @($app.pages | Where-Object { $_ -notlike '__plugin__*' })

[System.IO.File]::WriteAllText($appPath, ($app | ConvertTo-Json -Depth 100), $utf8)
"app.json 已修复"

# --- 2. project.config.json:关闭所有二次编译 ---
$pcPath = Join-Path $root "project.config.json"
if (Test-Path $pcPath) {
    $pc = Get-Content $pcPath -Raw -Encoding UTF8 | ConvertFrom-Json
    $pc.setting.es6        = $false
    $pc.setting.postcss    = $false
    $pc.setting.minified   = $false
    $pc.setting.enhance    = $false
    $pc.setting.minifyWXML = $false
    # 关键!防止未引用文件被剔除
    $pc.setting | Add-Member -NotePropertyName "ignoreUploadUnusedFiles" -NotePropertyValue $false -Force
    $pc.setting | Add-Member -NotePropertyName "ignoreDevUnusedFiles" -NotePropertyValue $false -Force
    [System.IO.File]::WriteAllText($pcPath, ($pc | ConvertTo-Json -Depth 100), $utf8)
    "project.config.json 已修复"
}

# --- 3. 移走插件残留目录 ---
foreach ($dir in @("__plugin__", "wxlive-components")) {
    if (Test-Path "$root\$dir") {
        Move-Item "$root\$dir" "C:\work\decompile\${dir}_backup" -Force
        "$dir 已移出备份"
    }
}

wxlive-components 是直播插件的组件目录,如果你的小程序没用直播插件,可能不存在,脚本会自动跳过。

6.2 导入

  1. 打开微信开发者工具导入项目
  2. 目录C:\work\decompile\output
  3. AppID:填你的 AppId
  4. 后端服务:选「不使用云服务」
  5. 导入后进 详情 → 本地设置,勾选:
    • ✅ 不校验合法域名、web-view、TLS 版本以及 HTTPS 证书
    • ✅ 关闭 ES6 转 ES5
    • ✅ 关闭上传代码时样式自动补全
    • ✅ 关闭上传代码时自动压缩

修改了 project.config.json 后,必须重新打开项目才生效。


七、常见问题大全

以下 8 个坑全部来自真实实操,按遇到顺序排列。


坑 #1:wxml 全是空文件或占位符 ⭐ 最关键

现象:

wxml 总数: 347   空的: 210

打开非空的 wxml,内容却是:

<!--pages/test/settings.wxml--><text>pages/test/settings.wxml</text>

日志里明明满屏 Saved file: xxx.wxml,看起来一切正常。

原因:

小程序的 wxml 被编译成名为 $gwx 的 JS 生成函数,存放在 page-frame.js(分包)或 app-wxss.js(主包)里。

某些编译器版本会给这个函数加混淆后缀,变成 $gwx_XC_29$gwx_XC_31 这类变体:

// app-wxss.js 里的真实形态
var gf = $gwx_XC_29('./components/common/card1.wxml');

KillWxapkg 只匹配标准的 $gwx,遇到变体名找不到 → 不报错,而是兜底写占位符。这是最阴险的地方:失败被伪装成了成功

如何确认:

Select-String -Path "C:\work\decompile\output\app-wxss.js" -Pattern 'gwx_XC_\d+' | Measure-Object

如果匹配到几百上千处,说明用的就是变体格式。

解决:

换 unveilr。它对各种 wcc 版本和 $gwx 变体的兼容性好得多。同一批包的实测对比:

工具wxml 结果
KillWxapkg v2.4.1210 空 + 137 占位符 = 0 个真实
unveilr v2.0.0366 个全部真实,平均 3.5 KB

教训:

工具跑通 ≠ 还原成功。永远打开文件看内容,不要只看文件数量和日志。


坑 #2:wxml 编译报 unexpected \ 或字符串跨行

现象:

[WXML 文件编译错误] ./components/actionTips/infoTips.wxml
Bad value with message: unexpected `\` at pos103.

查看报错处:

>{{type==1?self?'已通过认证 \n 会被推荐':'该用户已通过
      \n 认证,会被优先推荐':...}}

单引号字符串跨越了多行

原因:

unveilr 的 format: true 会格式化代码,把长表达式按行宽折行 —— 结果把字符串字面量也折断了。JS/WXML 的字符串不能跨行。

解决:

getConfigurator.ts,把 format 设为 false,重新解包:

format: false,

不要试图在坏文件上做文本替换修补 —— 实测不但治标不治本,还会引入编码问题(见坑 #7)。

教训:

格式化是给人读的,不是给机器跑的。反编译先求正确,再求可读。

想要可读的代码?先跑通,之后单独对 .js 跑 prettier,别碰 .wxml


坑 #3:npm install 报 ERESOLVE 依赖冲突

现象:

npm error code ERESOLVE
npm error Found: rollup@3.20.5
npm error peer rollup@"^2.0.0" from rollup-plugin-terser@7.0.2

原因:

老项目的构建工具版本对不上。这些只是构建期依赖,不影响工具运行。

解决:

npm install --legacy-peer-deps --registry=https://registry.npmmirror.com

坑 #4:unveilr 的 --help 报错,命令行参数无效

现象:

npm run run -- --help
# ExtractorError: File C:\...\unveilr\files cannot be extracted!

莫名其妙去解一个叫 files 的路径。

原因:

读一下 src/utils/isDev.ts

export function isDevelopment() {
  return PathController.make(process.argv[1]).suffixWithout === 'ts'
}

只要入口文件后缀是 .ts,就判定为开发模式。而开发模式下,getConfigurator.ts 直接返回硬编码配置(其中 packages: ['files']),完全跳过命令行解析

用 ts-node 跑源码,入口正好是 src/index.ts → 命令行参数全部作废。

解决:

两条路:

  1. 改配置(推荐):把参数写进 getConfigurator.ts 的 dev 分支,不加任何命令行参数直接跑
  2. 构建后跑npm run build 生成 dist/index.js,再用 node dist/index.js wx -i <appid> -o <输出> <包目录>

教训:

读工具源码比试参数快。 三分钟读懂 getConfigurator.ts 就能定位,盲试参数可能试一整天。


坑 #5:app.json: componentFramework 字段需为 string

现象:

[app.json 文件内容错误] app.json: componentFramework 字段需为 exparser,glass-easel

实际值是个对象:

"componentFramework": { "allUsed": ["exparser"], "default": "exparser" }

原因:

小程序有两套 json 格式

格式用途
源码 app.json"componentFramework": "exparser"开发者手写
编译产物 app-config.json{ "allUsed": [...], "default": "..." }运行时用

反编译是从编译产物逆推源码,这个字段没做格式转换。同类问题还有 plugins.subpackage

解决:

"componentFramework": "exparser",

plugins 里删掉 "subpackage": "__APP__" 这一行:

"plugins": {
  "livePlayerPlugin": {
    "version": "1.3.5",
    "provider": "wx2b03c6e691cd7370"
  }
}

坑 #6:插件组件找不到 / __plugin__ 报错

现象:

["usingComponents"]["page-live-player"]: 在 .../wxlive-components/page-live-player/ 路径下未找到组件

进去一看,该目录只有 .json,没有 .wxml/.js/.wxss

原因:

第三方插件(如微信官方直播插件 wx2b03c6e691cd7370)的代码不会明文缓存在本地 —— 微信按需从服务器下发。你反编译只能拿到配置骨架。

存在形式能反编译吗
你的业务代码明文在 wxapkg 里
第三方插件服务器按需下发

插件本来就不该作为源码存在于工程里 —— app.jsonplugins 字段是依赖声明,类似 package.json 写依赖名而不把 node_modules 塞进仓库。

解决:

  1. 确认 app.json 里有 plugins 声明(通常反编译已带出来)
  2. pages 数组删掉所有 __plugin__/... 开头的页面
  3. 移走 __plugin__wxlive-components 目录

判断某个目录是不是"死代码"的方法:

Select-String -Path "C:\work\decompile\output\**\*.json" -Pattern "wxlive-components" | Select-Object -First 5

无输出 = 没人用 = 可以安全移走。

这个方法比闷头修语法高效得多。遇到某文件编译不过,先问"它到底有没有人用"


坑 #7:PowerShell 读写中文文件变乱码 ⭐ 高频

现象 1 — 明明正常的 json 报格式错误:

ConvertFrom-Json : 传入的对象无效
"desc": "浣犵殑浣嶇疆淇℃伅灏嗙敤浜庡皬绋嬪簭...绀?

现象 2 — 用脚本改过文件后,编译报乱码字符 ?(U+FFFD)。

原因:

Windows PowerShell 5.1 的 Get-Content 默认按系统 ANSI(GBK)解码,而反编译产物是 UTF-8。UTF-8 中文按 GBK 解码会产生"半个字符",甚至把后面的引号一起吞掉 → json 看起来像是断了。

写入同理:Set-Content -Encoding UTF8 会写入带 BOM 的 UTF-8。

解决:

读取必须显式指定编码:

Get-Content "app.json" -Raw -Encoding UTF8 | ConvertFrom-Json

写入用 .NET 方法(无 BOM,最保险):

$utf8 = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($path, $content, $utf8)

读文件同样推荐 .NET 方法:

$content = [System.IO.File]::ReadAllText($path)   # 默认按 UTF-8

记死这条:PowerShell 5.1 处理反编译产物,读加 -Encoding UTF8,写用 .NET 方法。


坑 #8:module 'npm/xxx' is not defined ⭐ 最隐蔽

现象:

Error: module 'npm/@tarojs/async-await/index.js' is not defined,
require args is './npm/@tarojs/async-await/index.js'
Page "pages/home/index" has not been registered yet.

文件明明存在,内容也是合法 JS:

Test-Path "C:\work\decompile\output\npm\@tarojs\async-await\index.js"   # True

原因:

微信开发者工具有个默认开启的设置 ignoreUploadUnusedFiles(默认 true),会做依赖分析、剔除"未被引用"的文件。

反编译出的 js 是压缩后的:

!function(e){...}(require)

静态分析识别不出这种动态 require,于是判定"没人用这个文件" → 编译时直接剔除 → 运行时 not defined

app.js 在第一行 require 就挂掉,后面所有页面注册全都没执行,所以还会连带报 Page has not been registered yet

解决:

project.config.jsonsetting 里加两个字段:

"ignoreUploadUnusedFiles": false,
"ignoreDevUnusedFiles": false

改完重新打开项目

教训:

这类"工具的善意优化反而破坏还原产物"的坑最花时间 —— 因为文件就躺在磁盘上,内容也没问题,一切看起来都对。


附:其他可能遇到的小问题

报错原因解决
WXS 编译错误 Unexpected token }WXS 语法比 JS 严格,不支持尾随逗号等去掉 ,} 里的逗号;或确认该组件是否根本没人用,直接移走
npm i -g unveilrENOVERSIONS原 npm 包已下架从 GitHub fork 克隆源码构建
--help 参数无效npm run 吞参数 + 开发模式忽略参数见坑 #4
分包解出来不完整主包分包版本不一致确保同一版本目录下的包全部凑齐
找不到任何 wxapkg小程序没在电脑版微信打开过先打开并浏览几个页面
__route__ is not definedapp.js 挂掉导致页面未注册通常是坑 #8 的连带现象,修好 #8 即可

八、方法论总结

完整流程

① 定位缓存   微信4.x: %APPDATA%\Tencent\xwechat\radium\users\<hash>\applet\packages\<AppId>\<版本>\
                      ↓
② 判断加密   文件头 V1MMWX = 加密(AppId 是密钥来源) / 0xBE = 未加密
                      ↓
③ 凑齐一套   同版本目录下 __APP__ + 全部 _pages_* 必须完整
                      ↓
④ 解密解包   unveilr(format: false)/ KillWxapkg(-noClean)
                      ↓
⑤ 验收内容   ★ 打开文件看内容,别只看文件数量 ★
                      ↓
⑥ 修复配置   运行时格式 → 源码格式,关掉一切二次编译
                      ↓
⑦ 导入验证   微信开发者工具能编译运行 = 真正成功

三条核心经验

1. 工具跑通 ≠ 还原成功

日志满屏 Saved file、文件数量对得上,内容却可能全是占位符。工具的兜底逻辑会把失败伪装成成功。 唯一可靠的验收方式是打开文件看内容。

2. 关掉一切"聪明"功能

你手里的是编译后的成品,不是源码。任何二次加工都是破坏:

功能后果
format: true(反编译工具)折断 wxml 字符串
es6: true(开发者工具)重新 babel 编译,打乱模块 ID
minified: true二次压缩,可能破坏结构
ignoreUploadUnusedFiles: true剔除静态分析识别不出的文件

统一原则:不转译、不压缩、不美化、不剔除。

3. 读源码比试参数快

--help 失灵时,直接读 getConfigurator.tsisDev.ts,三分钟定位到"开发模式忽略命令行参数"。

同理,遇到某文件编译不过,先 grep 一下有没有人引用它 —— 没人用就直接移走,比修语法快得多。

保留中间产物

解包时务必加 -noClean(KillWxapkg)或保留 page-frame.js / app-wxss.js。这些是 wxml 的原料,一旦出问题,有它们才能定位是"解析器不认变体名"还是别的原因。删了就只能重来。


九、工具清单

工具地址用途
unveilr原仓库 github.com/r3x5ur/unve… (已失效)
fork: github.com/hzzheyang/u…
解密解包,wxml 还原成功率最高
KillWxapkggithub.com/Ackites/Kil…开箱即用,自动化程度高
wxapkg GUIgithub.com/wux1an/wxap…图形界面,适合快速尝试
wxappUnpackergithub.com/Ryan-Miao/w…经典 Node.js 脚本,适合学原理
微信开发者工具developers.weixin.qq.com/miniprogram…验证还原结果

附录:完整命令速查

# ===== 1. 找包 =====
Get-ChildItem -Path "C:\" -Recurse -Filter "*.wxapkg" -ErrorAction SilentlyContinue |
    Select-Object FullName, Length, LastWriteTime | Sort-Object LastWriteTime -Descending

# ===== 2. 验加密 =====
$pkg = "路径\__APP__.wxapkg"
$bytes = [System.IO.File]::ReadAllBytes($pkg)[0..15]
"HEX: " + (($bytes | ForEach-Object { $_.ToString('X2') }) -join ' ')

# ===== 3. 拷贝到工作区 =====
Copy-Item -Path "缓存路径\<AppId>" -Destination "C:\work\decompile\<AppId>" -Recurse -Force

# ===== 4. 安装 unveilr =====
git clone https://github.com/hzzheyang/unveilr-v2.0.0.git unveilr
cd unveilr
npm install --legacy-peer-deps --registry=https://registry.npmmirror.com
# 编辑 src\utils\getConfigurator.ts 填参数(format: false!)
npm run run

# ===== 5. 验收 wxml =====
$all = Get-ChildItem "C:\work\decompile\output" -Recurse -Filter *.wxml -File
"总数: " + $all.Count + "  空的: " + ($all | Where-Object Length -eq 0).Count
Get-Content "C:\work\decompile\output\app.json" -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null

# ===== 6. 修复配置(见 6.1 的完整脚本) =====

# ===== 7. 导入开发者工具,关闭所有编译优化 =====

结语

反编译没有"一条命令跑通"的神话。这次实操里,从第一次解包到最终跑起来,一共踩了 8 个坑 —— 工具版本、编译器变体、依赖冲突、开发模式陷阱、编码问题、配置格式、插件残留、静态分析剔除。

每个坑的排查方法都比坑本身更值钱:

  • 打开文件看内容,而不是相信日志
  • 保留中间产物,给排查留后路
  • 读工具源码,而不是盲试参数
  • grep 引用关系,而不是闷头修语法

学会这些,下次遇到新工具、新版本、新报错,你也能自己走通。


本文档基于真实实操整理,仅用于学习研究与自有项目的源码找回。请遵守法律法规。