Table of contents
Request packet analysis
Use Charles software to capture packets, the detailed process will not go into details, just paste the packet capture results directly
headers = {
'Host': 'miniapp.fang.com',
'pseusign': 'p10120230411162220f9a1195df01c47a6065f53b22d1c4100',
'openid': 'oxBn60ADRc6eURhDDkYReN4MjFik',
'content-type': 'application/json',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.33(0x18002129) NetType/WIFI Language/zh_CN',
'Referer': 'https://servicewechat.com/wxffbb41ec9b99a969/758/page-frame.html',
}
params = {
'appname': 'fangx',
'v': '1.3.4',
'miniplat': 'weixin',
'scene': '1027',
}
response = requests.get('https://miniapp.fang.com/pingyin/cityList', params=params, headers=headers, verify=False)
After deleting and restoring parameters back and forth, it is concluded that the pseudosign parameter is particularly important, and the applet code is directly analyzed.
Obtain the applet code (for some reason, the author of unveilr deleted the warehouse, you need to find it online)
I wrote an article [Mini Program Reverse] PC-side VX Mini Program Code Extraction to get the code of the mini program, but later when I used wxappUnpacker to receive the package, there was a problem, and the following error would be prompted
TypeError: subPackage.pages is not iterable
However, I found a new tool, unveilr , and the process of using it is also very simple. Let me demonstrate it briefly
The premise is still to use the PC WeChat mini-program package decryption tool to decrypt the mini-program file obtained on the PC side, just refer to the above article.
Install directly using NPM (first, ensure that NodeJS is installed on the computer)
# npm
npm i unveilr -g
# yarn
yarn global add unveilr
unveilr --help
# 简称
uvr -h
# 当 'unveilr' 不是内部或外部命令,也不是可运行的程序或批处理文件。
# 尝试在命令前面加一个 npx, 例如:
npx unveilr --help
After decrypting the applet file, use it directly unveilr /path/to/wxapkg/dir/
to get the source file.
There are many usage examples refer to the unveilr documentation for details.
run applet
Use the WeChat applet development tool to import the unpacked code, compile and run it directly, and you will get the following error message
[插件 wx1db9e5ab1149ea03] provider:wx1db9e5ab1149ea03, version:2.1.1, INVALID_LOGIN,access_token expired
插件文档: https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx1db9e5ab1149ea03&token=&lang=zh_CN(env: macOS,mp,1.06.2303060; lib: 2.30.3)
You can roughly understand the meaning of the plug-in expiration, then just app.json
find plugins
and delete it, just like the following
::: code-group
"plugins": {
"echarts": {
"version": "2.1.1",
"provider": "wx1db9e5ab1149ea03",
"subpackage": "__APP__"
}
},
"plugins": {
},
:::
Continue to compile and allow, continue to report errors, the content is as follows
WAServiceMainContext.js?t=wechat&s=1681204008944&v=2.30.3:1 TypeError: _typeof3 is not a function
The solution is to find @babel/runtime/helpers/typeof.js
and replace the following content into the file
function _typeof2(o) {
"@babel/helpers - typeof";
return (_typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
return typeof o;
} : function(o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
})(o);
}
function _typeof(o) {
return "function" == typeof Symbol && "symbol" === _typeof2(Symbol.iterator) ? module.exports = _typeof = function(o) {
return _typeof2(o);
} : module.exports = _typeof = function(o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : _typeof2(o);
}, _typeof(o);
}
module.exports = _typeof;
Continue to compile and run, and pass.
Analysis code
Direct global search pseusign
to find the following code
v.pseusign = h(e.url, i), o || (o = l("openid") || ""), v.openid = o, wx.request({
url: e.url,
data: "POST" === e.method ? e.data : i,
header: v,
method: e.method || "GET",
dataType: e.dataType || "json",
responseType: e.responseType || "text",
You can see pseusign
that is h
generated by the function, follow up h
to see
function h(e, n) {
var o = "",
a = getApp();
if (a) {
var i = a.vars.headerSgin;
try {
if (!n) return "";
var c = {
};
if ("string" == typeof n) n.split("&").forEach((function (e) {
var t = e.split("=");
2 === t.length && (c[t[0]] = t[1])
}));
else c = n;
if (e.includes("?")) e.substr(e.indexOf("?") + 1).split("&").forEach((function (e) {
var t = e.split("=");
2 === t.length && (c[t[0]] = t[1])
}));
var u = i.encodeNumber,
s = i.password,
g = function (e, t) {
var n = new Date(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate(), e.getUTCHours(), e.getUTCMinutes(), e.getUTCSeconds()),
r = new Date(n.setHours(e.getUTCHours() + 8)),
o = {
"M+": r.getMonth() + 1,
"d+": r.getDate(),
"h+": r.getHours(),
"m+": r.getMinutes(),
"s+": r.getSeconds(),
"q+": Math.floor((r.getMonth() + 3) / 3),
S: r.getMilliseconds()
};
for (var a in /(y+)/.test(t) && (t = t.replace(RegExp.$1, String(r.getFullYear()).substr(4 - RegExp.$1.length))), o) new RegExp("(" + a + ")").test(t) && (t = t.replace(RegExp.$1, 1 === RegExp.$1.length ? o[a] : ("00" + o[a]).substr(String(o[a]).length)));
return t
}(new Date, "yyyyMMddhhmmss"),
l = "p".concat(u).concat(g),
f = t(t({
}, c), {
}, {
micalltime: g,
miversion: i.miversgin,
misource: u
}),
h = Object.keys(f);
h.sort((function (e, t) {
return e.toLowerCase().localeCompare(t.toLowerCase())
}));
var d = "";
h.forEach((function (e) {
d += "&".concat(e, "=").concat(f[e])
})), d = d.substr(1);
var p = r.hex_md5("".concat(d).concat(s));
o = "".concat(l).concat(p)
} catch (e) {
o = ""
}
}
return o
}
Mark the debugger for debugging
var i = a.vars.headerSgin;
a.vars
Then declare that app.js
is a constant object, then headerSgin
isheaderSgin: {encodeNumber: "101",password: "4f0c6725d917f8fe4e2791921187fd7d",miversgin: "p"}
c = n;
n
It is the second parameter of the function, which is an object. After several times of debugging, you will know that it is the request parameter of the interface.
if (e.includes("?")) e.substr(e.indexOf("?") + 1).split("&").forEach((function (e) {
var t = e.split("=");
2 === t.length && (c[t[0]] = t[1])
}));
e
It is the first parameter of the function, which is a URL address
var u = i.encodeNumber,
u
= 101
s = i.password,
s
= 4f0c6725d917f8fe4e2791921187fd7d
g = function (e, t) {
var n = new Date(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate(), e.getUTCHours(), e.getUTCMinutes(), e.getUTCSeconds()),
r = new Date(n.setHours(e.getUTCHours() + 8)),
o = {
"M+": r.getMonth() + 1,
"d+": r.getDate(),
"h+": r.getHours(),
"m+": r.getMinutes(),
"s+": r.getSeconds(),
"q+": Math.floor((r.getMonth() + 3) / 3),
S: r.getMilliseconds()
};
for (var a in /(y+)/.test(t) && (t = t.replace(RegExp.$1, String(r.getFullYear()).substr(4 - RegExp.$1.length))), o) new RegExp("(" + a + ")").test(t) && (t = t.replace(RegExp.$1, 1 === RegExp.$1.length ? o[a] : ("00" + o[a]).substr(String(o[a]).length)));
return t
}(new Date, "yyyyMMddhhmmss"),
This code is to take the current time and then convert the format, g
= 20230411175542
l = "p".concat(u).concat(g),
l
= “p” + 101 + 20230411175542 = “p10120230411175542”
f = t(t({
}, c), {
}, {
micalltime: g,
miversion: i.miversgin,
misource: u
}),
f
It is to merge the above g, i.miversgin, u into one object
h = Object.keys(f);
h
an array of all for f
objectskey
h.sort((function (e, t) {
return e.toLowerCase().localeCompare(t.toLowerCase())
}));
h
Convert all elements in the array to lowercase
var d = "";
h.forEach((function (e) {
d += "&".concat(e, "=").concat(f[e])
})), d = d.substr(1);
Declare an empty string d
, and h
then &
splice all the elements in
var p = r.hex_md5("".concat(d).concat(s));
o = "".concat(l).concat(p)
d
After splicing with the previous s
and encrypting with md5, and l
splicing with , the result will come out
o
= “p10120230411175542fab22e3db8c2d5a523289a398d920647”
part final code
function getSign(e, n) {
var i = {
encodeNumber: "101",
password: "4f0c6725d917f8fe4e2791921187fd7d",
miversgin: "p"
};
s = i.password;
var c = {
};
if ("string" == typeof n) {
n.split("&").forEach((function (e) {
var t = e.split("=");
2 === t.length && (c[t[0]] = t[1])
}));
} else {
c = n;
if (e.includes("?")) e.substr(e.indexOf("?") + 1).split("&").forEach((function (e) {
var t = e.split("=");
2 === t.length && (c[t[0]] = t[1])
}));
}
var u = i.encodeNumber;
g = function (e, t) {
var n = new Date(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate(), e.getUTCHours(), e.getUTCMinutes(), e.getUTCSeconds()),
r = new Date(n.setHours(e.getUTCHours() + 8)),
o = {
"M+": r.getMonth() + 1,
"d+": r.getDate(),
"h+": r.getHours(),
"m+": r.getMinutes(),
"s+": r.getSeconds(),
"q+": Math.floor((r.getMonth() + 3) / 3),
S: r.getMilliseconds()
};
for (var a in /(y+)/.test(t) && (t = t.replace(RegExp.$1, String(r.getFullYear()).substr(4 - RegExp.$1.length))), o) new RegExp("(" + a + ")").test(t) && (t = t.replace(RegExp.$1, 1 === RegExp.$1.length ? o[a] : ("00" + o[a]).substr(String(o[a]).length)));
return t
}(new Date(), "yyyyMMddhhmmss");
l = "p".concat(u).concat(g);
f = _objectSpread2(_objectSpread2({
}, c), {
}, {
micalltime: g,
miversion: i.miversgin,
misource: u
});
h = Object.keys(f);
h.sort((function (e, t) {
return e.toLowerCase().localeCompare(t.toLowerCase())
}));
var d = "";
h.forEach((function (e) {
d += "&".concat(e, "=").concat(f[e])
})), d = d.substr(1);
var p = hs.hex_md5("".concat(d).concat(s));
o = "".concat(l).concat(p)
return o;
}
// let url = 'https://miniapp.fang.com/pingyin/cityList';
// let params = {
// 'appname': 'fangx',
// 'v': '1.3.4',
// 'miniplat': 'weixin',
// 'scene': '1027',
// }
// console.log(getSign(url, params));
full code
Follow the official account [ 趣码周
] and reply ftx
to get it.
Follow us
WeChat public account: 【趣码周
】