WeChat Mini Program Decompilation on Windows: The 8 Pitfalls That Break Every Tutorial
Complete WeChat Mini Program Decompilation Tutorial (Windows Beginner's Edition)
This article is based on a real, complete hands-on operation, including all steps, 8 pitfalls encountered, and their solutions. Environment: Windows 11 + PowerShell 5.1 + Node.js v22 + WeChat 4.x + WeChat DevTools 1.06
⚠️ Must-Read Before Use: Legal Boundaries
Only use in the following scenarios:
- ✅ Decompiling a mini program you developed yourself (source code lost, retrieving historical versions)
- ✅ Security assessments and penetration testing with written authorization
- ✅ Learning and researching mini program packaging and compilation principles
Prohibited:
- ❌ Decompiling others' commercial mini programs and using/publishing their code — involves copyright infringement
- ❌ Cracking paid features, bypassing business checks
- ❌ Stealing others' business logic or data interfaces
The technical barrier for decompilation is not high, but the legal liability is real. This tutorial assumes you are operating under the first two legal scenarios.
Table of Contents
- Principle Overview
- Environment Setup
- Step 1: Locate the wxapkg Package
- Step 2: Determine Encryption Status
- Step 3: Decrypt and Unpack
- Step 4: Import into DevTools
- FAQ (8 Real Pitfalls)
- Methodology Summary
- Tool List
1. Principle Overview
1.1 How Mini Programs Exist on Your Computer
When you open a mini program in WeChat, WeChat downloads its code package to a local cache. The file suffix is .wxapkg. Decompilation is the process of restoring this package back into editable project source code.
Obtain .wxapkg → Decrypt → Unpack → Restore project structure → Import into DevTools for verification
1.2 Package Structure
| File | Meaning |
|---|---|
__APP__.wxapkg |
Main Package — Main program code |
_pages_xxx_.wxapkg |
Subpackage — Code split by page |
Key Point: The main package and subpackages must be from the same version and all collected to restore a complete project.
1.3 Where Did the Source Code Go
After compilation, the source code is broken down into these files:
| Compiled Product | Restores To |
|---|---|
app-service.js |
All .js logic code |
page-frame.js / app-wxss.js |
All .wxml views + .wxss styles |
app-config.json |
app.json and page .json configs |
Among these, wxml is the hardest to restore — it is compiled into a JS generation function named $gwx, requiring the view tree to be reverse-engineered. This is also the biggest pitfall later on.
2. Environment Setup
2.1 What Needs to Be Installed
node -v # Required, >= 16
git --version # Required
- Node.js: https://nodejs.org/ (LTS recommended)
- Git: https://git-scm.com/
- WeChat DevTools: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
- PC WeChat: Ensure you can log in normally
2.2 Create Working Directory
New-Item -ItemType Directory -Force -Path "C:\work\decompile" | Out-Null
cd C:\work\decompile
The following text uses C:\work\decompile as the working directory; you can replace it with your own path.
3. Step 1: Locate the wxapkg Package
3.1 First, Let the Mini Program Be Cached
Open PC WeChat → Search for and open the target mini program → Click through several pages (ensure both main package and subpackages are loaded) → Close it.
If you don't open it, there's no cache, and you won't find anything later.
3.2 Find the Cache Directory
Paths differ for different WeChat versions:
| WeChat Version | Cache Path |
|---|---|
| WeChat 4.x (Personal) | %APPDATA%\Tencent\xwechat\radium\users\<user hash>\applet\packages\<AppId>\<version>\ |
| WeChat 3.x (Old Version) | Documents\WeChat Files\Applet\<AppId>\ |
| WeCom | Documents\WXWork\<ID>\Applets\Applet\<AppId>\<version>\ |
The easiest way is a full disk search:
Get-ChildItem -Path "C:\" -Recurse -Filter "*.wxapkg" -ErrorAction SilentlyContinue |
Select-Object FullName, Length, LastWriteTime |
Sort-Object LastWriteTime -Descending
The search is slow; wait patiently for 1-3 minutes. You can also search only the WeChat directories:
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 Lock Down Your Mini Program's AppId
The cache directory contains a bunch of AppIds (all the mini programs you've used). Use this script to sort by recent use:
# Replace <user hash> with your actual string
$root = "$env:APPDATA\Tencent\xwechat\radium\users\<user hash>\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
PackageCount = $files.Count
LastModified = $latest.LastWriteTime
TotalSizeKB = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1KB)
}
} | Sort-Object LastModified -Descending | Format-Table -AutoSize
The one with the most recent modification time is the mini program you just opened. The AppId can also be found in the WeChat Official Accounts Platform backend.
3.4 Copy the Package to the Workspace
$src = "$env:APPDATA\Tencent\xwechat\radium\users\<user hash>\applet\packages\<Your AppId>"
Copy-Item -Path $src -Destination "C:\work\decompile\<Your AppId>" -Recurse -Force
Copy instead of operating directly on the original directory — avoids accidentally deleting WeChat's cache.
3.5 Confirm Package Integrity
Get-ChildItem "C:\work\decompile\<Your AppId>" -Recurse -Filter *.wxapkg |
Select-Object @{n='VersionDir';e={$_.Directory.Name}},
Name,
@{n='KB';e={[math]::Round($_.Length/1KB)}},
LastWriteTime |
Sort-Object VersionDir, Name | Format-Table -AutoSize
Checklist:
- Find the version directory that contains
__APP__.wxapkgand has the most files - The main package + all subpackages must be in the same version directory
- If scattered across multiple version directories (e.g.,
651,662), use the newest and most complete set
4. Step 2: Determine Encryption Status
$pkg = "C:\work\decompile\<Your AppId>\<version>\__APP__.wxapkg"
$bytes = [System.IO.File]::ReadAllBytes($pkg)[0..15]
"First 16 bytes (Hex): " + (($bytes | ForEach-Object { $_.ToString('X2') }) -join ' ')
"First 16 bytes (ASCII): " + ([System.Text.Encoding]::ASCII.GetString($bytes) -replace '[^\x20-\x7E]','.')
| File Header | Meaning | Handling Method |
|---|---|---|
56 31 4D 4D 57 58(V1MMWX) |
PC WeChat encrypted | Needs AppId to decrypt |
First byte BE |
Unencrypted standard | Can be unpacked directly |
Why AppId is the source of the decryption key: PC WeChat uses the AppId to participate in deriving the key. This also means — you must know the AppId to decrypt, naturally limiting it to "mini programs you can identify."
5. Step 3: Decrypt and Unpack
5.1 Tool Selection
| Tool | Pros | Cons |
|---|---|---|
| unveilr | Best compatibility with various wcc compiler versions, high wxml restoration success rate | npm package has been taken down, needs building from fork source |
| KillWxapkg | Ready-to-use exe, high degree of automation | Fails to restore wxml for older wcc versions (see Pitfall #1) |
Recommendation: Use unveilr directly. If you want to quickly verify if decryption is possible, you can run KillWxapkg first.
5.2 Option A: KillWxapkg (Quick Start)
Download: https://github.com/Ackites/KillWxapkg/releases → Download the Windows amd64 version → Rename to KillWxapkg.exe and place in the working directory.
⚠️ Third-party open-source binary; it's recommended to scan with antivirus first.
cd C:\work\decompile
.\KillWxapkg.exe -h # Check parameters first
.\KillWxapkg.exe -id <Your AppId> `
-in "C:\work\decompile\<Your AppId>\<version>" `
-out "C:\work\decompile\output" `
-restore -pretty -noClean
| Parameter | Effect |
|---|---|
-id |
AppId (source of decryption key) |
-in |
Input directory (the entire version directory, main package and subpackages together) |
-out |
Output directory |
-restore |
Restore project directory structure |
-pretty |
Beautify code |
-noClean |
Keep intermediate files (page-frame.js/app-wxss.js) — key for troubleshooting, strongly recommended |
5.3 Option B: unveilr (Recommended, Correct wxml Restoration)
Step 1: Clone Source Code
The original repository r3x5ur/unveilr is invalid, and the npm package has also been taken down (npm i -g unveilr reports ENOVERSIONS). You need to use a fork:
cd C:\work\decompile
git clone https://github.com/hzzheyang/unveilr-v2.0.0.git unveilr
Backup forks:
- https://github.com/junxiaqiao/unveilr-v2.0.0
- https://github.com/Lunction/unveilr
- https://github.com/HelloAndyZhang/unveilr
Step 2: Install Dependencies
cd C:\work\decompile\unveilr
npm install --legacy-peer-deps --registry=https://registry.npmmirror.com
--legacy-peer-depsis required — the project's rollup version has a peer conflict with rollup-plugin-terser (see Pitfall #3).
Step 3: Configure Parameters (Important!)
When running source code with ts-node, unveilr enters development mode and directly ignores command-line arguments (see Pitfall #4). So parameters must be written into the configuration file.
Edit src\utils\getConfigurator.ts, change the wx: { ... } section to:
wx: {
appid: 'Your AppId',
format: false,
clearDecompile: true,
clearSave: true,
parse: true,
depth: 1,
output: 'C:/work/decompile/output',
packages: ['C:/work/decompile/Your AppId/version'],
},
formatmust be set tofalse! Setting it totruewill format the code, breaking strings in wxml expressions across multiple lines and causing compilation failures (see Pitfall #2).Use forward slashes
/for paths to avoid escaping issues.
Step 4: Run
cd C:\work\decompile\unveilr
npm run run
Do not add any command-line arguments — parameters are hardcoded in the code; adding them will be misinterpreted as paths.
Step 5: Acceptance Check (The Most Important Step!)
Don't just look at the file count; you must check the content:
$all = Get-ChildItem "C:\work\decompile\output" -Recurse -Filter *.wxml -File
"Total: " + $all.Count
"Empty: " + ($all | Where-Object Length -eq 0).Count
"Avg KB: " + [math]::Round(($all | Measure-Object Length -Average).Average / 1KB, 1)
# Spot-check real content
Get-Content "C:\work\decompile\output\pages\home\index.wxml" -TotalCount 10 -Encoding UTF8
| Phenomenon | Judgment |
|---|---|
0 empty files, avg 2-5 KB, content is <view class="..."> tag tree |
✅ Success |
Many 0-byte files, or content is <text>page path</text> |
❌ Failed (placeholders, see Pitfall #1) |
6. Step 4: Import into WeChat DevTools
6.1 Necessary Fixes Before Importing
The decompilation product is a compiled artifact. Some configurations are in runtime format and need to be converted back to source format. Use this script to fix them all at once:
$root = "C:\work\decompile\output"
$utf8 = New-Object System.Text.UTF8Encoding $false
# --- 1. app.json three fixes ---
$appPath = Join-Path $root "app.json"
$app = Get-Content $appPath -Raw -Encoding UTF8 | ConvertFrom-Json
# 1a. componentFramework from object to string
if ($app.componentFramework -and $app.componentFramework -isnot [string]) {
$app.componentFramework = "exparser"
}
# 1b. Remove runtime field subpackage from plugins
if ($app.plugins) {
foreach ($p in $app.plugins.PSObject.Properties) {
$p.Value.PSObject.Properties.Remove('subpackage')
}
}
# 1c. Remove plugin pages from pages array
$app.pages = @($app.pages | Where-Object { $_ -notlike '__plugin__*' })
[System.IO.File]::WriteAllText($appPath, ($app | ConvertTo-Json -Depth 100), $utf8)
"app.json fixed"
# --- 2. project.config.json: disable all secondary compilation ---
$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
# Crucial! Prevent unused files from being stripped
$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 fixed"
}
# --- 3. Move away plugin residual directories ---
foreach ($dir in @("__plugin__", "wxlive-components")) {
if (Test-Path "$root\$dir") {
Move-Item "$root\$dir" "C:\work\decompile\${dir}_backup" -Force
"$dir moved to backup"
}
}
wxlive-componentsis the component directory for the live-streaming plugin. If your mini program doesn't use the live-streaming plugin, it might not exist; the script will automatically skip it.
6.2 Import
- Open WeChat DevTools → Import Project
- Directory:
C:\work\decompile\output - AppID: Enter your AppId
- Backend Service: Select 'Do not use cloud services'
- After importing, go to Details → Local Settings, check:
- ✅ Do not verify valid domain names, web-view, TLS version, and HTTPS certificates
- ✅ Disable ES6 to ES5 conversion
- ✅ Disable style auto-completion on code upload
- ✅ Disable automatic code compression on upload
After modifying
project.config.json, you must reopen the project for changes to take effect.
7. FAQ (8 Real Pitfalls)
The following 8 pitfalls all come from real hands-on experience, listed in the order encountered.
Pitfall #1: wxml Files Are All Empty or Placeholders ⭐ Most Critical
Symptom:
wxml total: 347 empty: 210
Opening a non-empty wxml, the content is:
<!--pages/test/settings.wxml--><text>pages/test/settings.wxml</text>
The log is full of Saved file: xxx.wxml, looking perfectly normal.
Cause:
The mini program's wxml is compiled into a JS generation function named $gwx, stored in page-frame.js (subpackages) or app-wxss.js (main package).
Some compiler versions add an obfuscation suffix to this function, turning it into variants like $gwx_XC_29, $gwx_XC_31:
// Real form inside app-wxss.js
var gf = $gwx_XC_29('./components/common/card1.wxml');
KillWxapkg only matches the standard $gwx. When it encounters variant names, it can't find them → no error is reported, but it falls back to writing placeholders. This is the most insidious part: failure is disguised as success.
How to Confirm:
Select-String -Path "C:\work\decompile\output\app-wxss.js" -Pattern 'gwx_XC_\d+' | Measure-Object
If hundreds or thousands of matches are found, it means the variant format is being used.
Solution:
Switch to unveilr. It has much better compatibility with various wcc versions and $gwx variants. A real comparison using the same set of packages:
| Tool | wxml Result |
|---|---|
| KillWxapkg v2.4.1 | 210 empty + 137 placeholders = 0 real |
| unveilr v2.0.0 | 366 all real, avg 3.5 KB |
Lesson:
Tool runs successfully ≠ Restoration succeeded. Always open files to check content, don't just look at file counts and logs.
Pitfall #2: wxml Compilation Error unexpected \ or String Spans Multiple Lines
Symptom:
[WXML file compilation error] ./components/actionTips/infoTips.wxml
Bad value with message: unexpected `\` at pos103.
Looking at the error location:
>{{type==1?self?'已通过认证 \n 会被推荐':'该用户已通过
\n 认证,会被优先推荐':...}}
Single-quoted string spans multiple lines.
Cause:
unveilr's format: true formats the code, wrapping long expressions at line width — resulting in string literals also being broken. JS/WXML strings cannot span lines.
Solution:
Modify getConfigurator.ts, set format to false, and re-unpack:
format: false,
Do not attempt to patch the broken files with text replacement — in practice, this not only fails to address the root cause but also introduces encoding issues (see Pitfall #7).
Lesson:
Formatting is for humans to read, not for machines to run. For decompilation, prioritize correctness first, readability second.
Want readable code? Get it running first, then run prettier on
.jsfiles separately; don't touch.wxml.
Pitfall #3: npm install Reports ERESOLVE Dependency Conflict
Symptom:
npm error code ERESOLVE
npm error Found: [email protected]
npm error peer rollup@"^2.0.0" from [email protected]
Cause:
Build tool versions in the old project don't match. These are only build-time dependencies and do not affect the tool's operation.
Solution:
npm install --legacy-peer-deps --registry=https://registry.npmmirror.com
Pitfall #4: unveilr's --help Errors Out, Command-Line Arguments Ineffective
Symptom:
npm run run -- --help
# ExtractorError: File C:\...\unveilr\files cannot be extracted!
Inexplicably tries to extract a path called files.
Cause:
Read src/utils/isDev.ts:
export function isDevelopment() {
return PathController.make(process.argv[1]).suffixWithout === 'ts'
}
As long as the entry file suffix is .ts, it's determined to be development mode. In development mode, getConfigurator.ts directly returns hardcoded configuration (where packages: ['files']), completely skipping command-line parsing.
Running source code with ts-node, the entry is exactly src/index.ts → all command-line arguments are voided.
Solution:
Two paths:
- Modify config (Recommended): Write parameters into the dev branch of
getConfigurator.ts, run directly without any command-line arguments - Run after building:
npm run buildgeneratesdist/index.js, then usenode dist/index.js wx -i <appid> -o <output> <package dir>
Lesson:
Reading tool source code is faster than blindly trying parameters. Three minutes reading
getConfigurator.tscan pinpoint the issue; blindly trying parameters might take all day.
Pitfall #5: app.json: componentFramework field must be string
Symptom:
[app.json file content error] app.json: componentFramework field must be exparser,glass-easel
The actual value is an object:
"componentFramework": { "allUsed": ["exparser"], "default": "exparser" }
Cause:
Mini programs have two sets of json formats:
| Format | Usage | |
|---|---|---|
Source app.json |
"componentFramework": "exparser" |
Developer writes |
Compiled product app-config.json |
{ "allUsed": [...], "default": "..." } |
Runtime use |
Decompilation infers source code from the compiled product; this field wasn't format-converted. A similar issue exists with plugins.subpackage.
Solution:
"componentFramework": "exparser",
In plugins, delete the line "subpackage": "__APP__":
"plugins": {
"livePlayerPlugin": {
"version": "1.3.5",
"provider": "wx2b03c6e691cd7370"
}
}
Pitfall #6: Plugin Component Not Found / __plugin__ Error
Symptom:
["usingComponents"]["page-live-player"]: Component not found at path .../wxlive-components/page-live-player/
Looking inside, that directory only has .json, no .wxml/.js/.wxss.
Cause:
Third-party plugin code (like WeChat's official live-streaming plugin wx2b03c6e691cd7370) is not cached locally in plaintext — WeChat delivers it from the server on demand. You can only decompile the configuration skeleton.
| Existence Form | Decompilable? | |
|---|---|---|
| Your business code | Plaintext in wxapkg | ✅ |
| Third-party plugin | Server delivers on demand | ❌ |
Plugins shouldn't exist as source code in the project anyway — the plugins field in app.json is a dependency declaration, similar to writing dependency names in package.json without stuffing node_modules into the repository.
Solution:
- Confirm
app.jsonhas thepluginsdeclaration (usually already brought out by decompilation) - Delete all pages starting with
__plugin__/...from thepagesarray - Move away the
__plugin__andwxlive-componentsdirectories
How to determine if a directory is "dead code":
Select-String -Path "C:\work\decompile\output\**\*.json" -Pattern "wxlive-components" | Select-Object -First 5
No output = nobody uses it = safe to move away.
This method is much more efficient than blindly fixing syntax. When a file fails to compile, first ask "does anyone even use it?".
Pitfall #7: PowerShell Reads/Writes Chinese Files as Garbled Text ⭐ High Frequency
Symptom 1 — A seemingly normal json reports a format error:
ConvertFrom-Json : Invalid object passed in
"desc": "浣犵殑浣嶇疆淇℃伅灏嗙敤浜庡皬绋嬪簭...绀?
Symptom 2 — After modifying a file with a script, compilation reports garbled character ? (U+FFFD).
Cause:
Windows PowerShell 5.1's Get-Content defaults to decoding with the system ANSI (GBK) code page, while decompilation products are UTF-8. Decoding UTF-8 Chinese as GBK produces "half characters", even swallowing the following quote → json looks broken.
Writing has the same issue: Set-Content -Encoding UTF8 writes UTF-8 with BOM.
Solution:
Reading must explicitly specify encoding:
Get-Content "app.json" -Raw -Encoding UTF8 | ConvertFrom-Json
Writing use .NET methods (no BOM, safest):
$utf8 = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($path, $content, $utf8)
Reading files also recommends .NET methods:
$content = [System.IO.File]::ReadAllText($path) # Defaults to UTF-8
Memorize this rule: PowerShell 5.1 handling decompilation products: read with
-Encoding UTF8, write with .NET methods.
Pitfall #8: module 'npm/xxx' is not defined ⭐ Most Subtle
Symptom:
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.
The file clearly exists, and the content is valid JS:
Test-Path "C:\work\decompile\output\npm\@tarojs\async-await\index.js" # True
Cause:
WeChat DevTools has a default setting ignoreUploadUnusedFiles (default true), which performs dependency analysis and strips "unreferenced" files.
Decompiled JS is minified:
!function(e){...}(require)
Static analysis cannot recognize this dynamic require, thus determining "nobody uses this file" → stripped during compilation → not defined at runtime.
app.js fails on the first line's require, so all subsequent page registrations never execute, leading to the accompanying Page has not been registered yet error.
Solution:
Add two fields in project.config.json's setting:
"ignoreUploadUnusedFiles": false,
"ignoreDevUnusedFiles": false
After modifying, reopen the project.
Lesson:
This type of pitfall — where "the tool's well-intentioned optimization destroys the restored product" — takes the most time because the file is right there on disk, the content is fine, everything looks correct.
Appendix: Other Minor Issues You Might Encounter
| Error | Cause | Solution |
|---|---|---|
WXS Compile Error Unexpected token } |
WXS syntax is stricter than JS, no trailing commas etc. | Remove comma in ,}; or confirm if the component is even used, just move it away |
npm i -g unveilr reports ENOVERSIONS |
Original npm package has been taken down | Clone source from GitHub fork and build |
--help parameter ineffective |
npm run swallows params + dev mode ignores params | See Pitfall #4 |
| Subpackage decompression incomplete | Main package and subpackage versions inconsistent | Ensure all packages from the same version directory are collected |
| Cannot find any wxapkg | Mini program hasn't been opened in PC WeChat | Open it first and browse several pages |
__route__ is not defined |
app.js failure causes pages not registered | Usually a side effect of Pitfall #8, fixing #8 resolves it |
8. Methodology Summary
Complete Workflow
① Locate Cache WeChat 4.x: %APPDATA%\Tencent\xwechat\radium\users\<hash>\applet\packages\<AppId>\<version>\
↓
② Check Encryption Header V1MMWX = Encrypted (AppId is key source) / 0xBE = Unencrypted
↓
③ Gather a Complete Set __APP__ + all _pages_* in the same version directory must be complete
↓
④ Decrypt & Unpack unveilr (format: false) / KillWxapkg (-noClean)
↓
⑤ Verify Content ★ Open files to check content, don't just look at file counts ★
↓
⑥ Fix Config Runtime format → Source format, disable all secondary compilation
↓
⑦ Import & Verify WeChat DevTools can compile and run = True success
Three Core Lessons
1. Tool Runs Successfully ≠ Restoration Succeeded
Logs full of Saved file, file counts match, but content might be all placeholders. The tool's fallback logic disguises failure as success. The only reliable verification method is to open files and check the content.
2. Disable All "Smart" Features
What you have is a compiled artifact, not source code. Any secondary processing is destructive:
| Feature | Consequence |
|---|---|
format: true (decompiler) |
Breaks wxml strings |
es6: true (DevTools) |
Re-runs babel, scrambles module IDs |
minified: true |
Secondary compression, may break structure |
ignoreUploadUnusedFiles: true |
Strips files static analysis can't recognize |
Unified Principle: No transpiling, no minifying, no beautifying, no stripping.
3. Reading Source Code Is Faster Than Trying Parameters
When --help fails, directly read getConfigurator.ts and isDev.ts; three minutes to pinpoint "development mode ignores command-line arguments."
Similarly, when a file fails to compile, grep first to see if anything references it — if nobody uses it, just move it away, much faster than fixing syntax.
Preserve Intermediate Artifacts
When unpacking, be sure to add -noClean (KillWxapkg) or keep page-frame.js / app-wxss.js. These are the raw materials for wxml. If problems arise, having them allows you to determine whether it's "parser doesn't recognize variant names" or another reason. Delete them and you have to start over.
9. Tool List
| Tool | Address | Purpose |
|---|---|---|
| unveilr | Original repo https://github.com/r3x5ur/unveilr (invalid) fork: https://github.com/hzzheyang/unveilr-v2.0.0 |
Decrypt & unpack, highest wxml restoration success rate |
| KillWxapkg | https://github.com/Ackites/KillWxapkg | Ready-to-use, high automation |
| wxapkg GUI | https://github.com/wux1an/wxapkg | GUI, suitable for quick attempts |
| wxappUnpacker | https://github.com/Ryan-Miao/wxappUnpacker | Classic Node.js script, good for learning principles |
| WeChat DevTools | https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html | Verify restoration results |
Appendix: Complete Command Quick Reference
# ===== 1. Find packages =====
Get-ChildItem -Path "C:\" -Recurse -Filter "*.wxapkg" -ErrorAction SilentlyContinue |
Select-Object FullName, Length, LastWriteTime | Sort-Object LastWriteTime -Descending
# ===== 2. Check encryption =====
$pkg = "path\__APP__.wxapkg"
$bytes = [System.IO.File]::ReadAllBytes($pkg)[0..15]
"HEX: " + (($bytes | ForEach-Object { $_.ToString('X2') }) -join ' ')
# ===== 3. Copy to workspace =====
Copy-Item -Path "cache path\<AppId>" -Destination "C:\work\decompile\<AppId>" -Recurse -Force
# ===== 4. Install 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
# Edit src\utils\getConfigurator.ts to fill in parameters (format: false!)
npm run run
# ===== 5. Verify wxml =====
$all = Get-ChildItem "C:\work\decompile\output" -Recurse -Filter *.wxml -File
"Total: " + $all.Count + " Empty: " + ($all | Where-Object Length -eq 0).Count
Get-Content "C:\work\decompile\output\app.json" -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
# ===== 6. Fix config (see complete script in 6.1) =====
# ===== 7. Import into DevTools, disable all compilation optimizations =====
Conclusion
There is no myth of "one command runs it all" in decompilation. In this hands-on operation, from the first unpacking to finally getting it running, a total of 8 pitfalls were encountered — tool versions, compiler variants, dependency conflicts, development mode traps, encoding issues, configuration formats, plugin residuals, static analysis stripping.
The troubleshooting method for each pitfall is more valuable than the pitfall itself:
- Open files to check content, don't trust logs
- Preserve intermediate artifacts, leave a trail for troubleshooting
- Read tool source code, don't blindly try parameters
- Grep reference relationships, don't bury your head in fixing syntax
Learn these, and next time you encounter new tools, new versions, new errors, you can walk through it yourself.
This document is based on real hands-on operations, intended only for learning research and source code retrieval of self-owned projects. Please comply with laws and regulations.