使用工具wxappUnpacker对该小程序解包时会抛出错误: SyntaxError: Unexpected token '}'
异常发生在wuWxml.js
, 这个文件的功能是将js文件逆向成wxml文件. 使用命令node wuWxapkg.js -o xxx.wxpkg
只解包不反编译, 可跳过执行该文件以避免发生该错误.
发生错误的原因应该是微信小程序的编译方法变化了, 使得原来的解包工具的反编译程序无法正常工作了. 接下来就对照wxappUnpacker
的源码和一个可以正常逆向解包的小程序, 尝试修改wuWxml.js的代码以使其能够顺利反编译新的小程序包.
TL;DR
- 生成
z
数组的gz$gwx
函数的命名变了。以前是gz$gwx1
,现在是gz$gwx1_XC_1_0_1
这样子 - 处理
z
数组的m
函数的命名和组织形式变了。以前是所有的m
函数都放在了一起、依次声明为m0, m1, m2
这样子;现在是每一个Z
数组后面都紧跟着一个m0
函数。
简单修改一下 wuWxml.js 和 wuRestoreZ.js 文件,即可成功反编译出相关的Page代码。
为了更好地阅读wxappUnpacker
的源码和了解小程序的反编译原理, 可对照这篇文章: 将微信小程序(.wxapkg)解包及将包内内容还原为"编译"前的内容的"反编译"器-Android安全-看雪论坛-安全社区
处理Z数组
根据上述文章中所述, 微信将所有要动态计算的变量放在了一个由函数构造的z
数组中. 对照一个可以正常解包的小程序可以发现, 两个版本在生成z
数组时的函数基本上是不变的, 但是函数名发生了变化:
// 新版的小程序
function gz$gwx1_XC_5_1(){
if( __WXML_GLOBAL__.ops_cached.$gwx1_XC_5_1)return __WXML_GLOBAL__.ops_cached.$gwx1_XC_5_1
__WXML_GLOBAL__.ops_cached.$gwx1_XC_5_1=[];
(function(z){var a=11;function Z(ops){z.push(ops)}
Z([3,'list'])
// 旧版的小程序
function gz$gwx_15(){
if( __WXML_GLOBAL__.ops_cached.$gwx_15)return __WXML_GLOBAL__.ops_cached.$gwx_15
__WXML_GLOBAL__.ops_cached.$gwx_15=[];
(function(z){var a=11;function Z(ops){z.push(ops)}
Z([a,[3,'custom-class '],[[12],[[6],[[7],[3,'utils']],[3,'bem']],[[5],[[5],[1,'loading']],[[8],
修改wuRestoreZ.js
以提取z
数组:
-
找到函数
catchZ
, 修改其中匹配函数名的正则表达式为:/function gz\$gwx(\d*[\_XC]*[\d\_]*)\(\)\{\s*if\( __WXML_GLOBAL__\.ops_cached\.\$gwx\d*[\_\dXC]+\)/
- 找到函数
catchZGroup
, 修改其中提取函数编号的正则表达式为:/function gz\$gwx(\d*[\_XC]*[\d\_]+)/
如图中第19/60行所示. (图中catchM0Group
是我自己新增的, 原版程序里没有这个函数)
之后便可以正常的提取z
数组了.
处理M函数
在提取z
数组后, 小程序使用了一系列的m
函数来处理这些z
数组, 以实现动态的渲染. 文章中提到了一种将每种可能的 js 语句拆分成"指令"的分析方法. 相应的程序在文件wuWxml.js
的analyze
函数中. 对照文章中列举的部分"指令"和代码, 可以看出新版小程序的编译方法与旧版几乎无差异. 因此analyze
函数大概率是依然可以使用的.
继续分析wuWxml.js
源码. 在getZ
函数的回调中, 执行了以下程序以获取m
系列函数:
getZ(code, z => {
const before = "\nvar nv_require=function(){var nnm=";
code = code.slice(code.lastIndexOf(before) + before.length, code.lastIndexOf("if(path&&e_[path]){"));
json = code.slice(0, code.indexOf("};") + 1);
let endOfRequire = code.indexOf("()\r\n") + 4;
if (endOfRequire == 4 - 1) endOfRequire = code.indexOf("()\n") + 3;
code = code.slice(endOfRequire);
let rD = {}, rE = {}, rF = {}, requireInfo = {}, x, vm = new VM({
sandbox: {
d_: rD, e_: rE, f_: rF, _vmRev_(data) {
[x, requireInfo] = data;
}, nv_require(path) {
return () => path;
}
}
});
let vmCode = code + "\n_vmRev_([x," + json + "])";
vm.run(vmCode);
上述代码也是文章开头提到的抛出异常的代码. 对照旧版小程序, 在代码var nv_require=function(){var nnm=
和if(path&&e_[path]){
之间是所有的m
系列函数.
此外旧版小程序声明了一个x
数组, 用以保存所有的.wxml文件位置.
但是在新的小程序中, m
系列函数不再具有连贯的编号, 而且其位置也不再集中放置, x
数组也不再具有保存所有.wxml的作用:
m0
函数会紧跟每一个生成Z
数组的gw$gwx
函数之后, x
数组仅保存当前z
数组对应的文件路径. 此外, 后面还会紧跟该页面的wxss样式代码
经过上述分析, 需要编写一个catchM0
函数, 用以将所有的m0
函数提取出来.
/**
* 获取所有的m0函数
* @param {String} code 代码
* @param {Object} z z数组
* @param {Function} cb 回调函数
* @returns {name: {code: string}}
*/
function catchM0(code, z, cb) {
let groupTest = code.match(/var x=\['.*\.wxml'];d_\[x\[0\]\]=\{\}/g);
if (groupTest !== null) return catchM0Group(code, groupTest, z, cb);
}
function catchM0Group(code, groupPreStr, z, cb) {
let m0Arr = {};
let i = 0;
for (let preStr of groupPreStr) {
i++;
let name = preStr.replace(/.*var x=\['(.*\.wxml)'\].*/g, "$1");
let content = code.slice(code.indexOf(preStr));
content = content.slice(0, content.indexOf("if(path&&e_[path]){"));
let zFuncName = ""
content.replace(/var z=gz\$gwx([_XC\d]+)\(\)/g, (match, p1, p2, p3, offset, string) => {
zFuncName = p1;
});
// TODO: d_数组不知道是什么意思, 暂时不考虑了
let rD = {}, rF = {}, rZ = z[zFuncName] || [], x = [];
let vm = new VM({
sandbox: {
x: x, z: rZ, d_: rD, e_: m0Arr, f_: rF, debugInfo: []
}
});
vm.run(content);
}
cb(m0Arr);
}
虽然x
数组不再存储所有的.wxml文件清单, 上述函数的执行结果m0Arr
的Keys
实际上就等价于x
数组. 再之后将m0Arr
数组/z
数组/x
数组交给analyze
函数执行:
catchM0(code, z, m0 => {
for (let name in m0) tryWxml(dir, name, m0[name].f.toString(), z, Object.keys(m0), [], [], moreInfo);
cb({[name]: 4});
});
可以正确地反编译出wxml
文件了.
修改后的文件:
😄,感谢大牛,解决我多年来的困惑
大佬,wuWxss.js也同样有如上报错,应该如何解决呢
wxss文件就是变体的css文件, 应该不需要反编译, 直接复制出来改改就能用了吧
大佬wuWxss.js、wuLib.js也有报错,怎么解决呀
我也不清楚哈, 具体情况得具体分析.
我贴一个仓库的地址, 本来这个仓库是我提了issue后作者重构了的开源项目, 现在好像半商业化运营了, 无意推广. 如介意请忽略.
https://github.com/r3x5ur/unveilr
https://u.openal.lat/#document
哦, 我补一个之前下载的unveilr的源码
下载链接: https://cloud.chaol.top/s/Z6id
密码: j19b0z