一. 观察verify请求
首先,我们需要观察verify请求的Initiator。通过查看可以发现,这些请求全都来自于gcaptcha4.js文件。因此,这个文件将是我们分析的重点。
格式化该文件后,在第13339行,可以看到代码被混淆得非常严重。面对这种情况,我们有两种选择:直接硬刚解析混淆代码,或者使用AST(抽象语法树)技术来还原代码。我选择了使用AST还原JavaScript文件,并利用Chrome的reres插件将混淆的代码替换掉。AST还原的代码可以在我的GitHub上找到,关于reres插件的安装和使用,请自行搜索。
二. 定位w参数加密位置
为了找到w参数的加密位置,我们可以搜索以下关键词:w、.w、'w'或"w"。通过搜索"w",我们发现了我们想要的结果。在第2527行打上断点,可以看到w的值r在第2525行被定义。我们在2525行也打上断点,然后刷新页面,成功断点后,就完成了w参数加密位置的定位。
三. 分析w参数加密算法
接下来,我们需要简化代码中w的定义:
var r = (0, d["default"])(l["default"]["stringify"](e), a);
这段代码等价于:
javascript
var r = d.default(l.default.stringify(e), a);
首先,l.default.stringify(e)是对e进行JSON序列化操作。
接下来,我们分析d.default(l.default.stringify(e), a)的含义。通过跟进d函数,我们可以看到如下代码:
function a(e, t) {
var s = t["options"];
if (!s["pt"] || "0" === s["pt"]) {
return r["default"]["urlsafe_encode"](e);
}
if ("1" === s["pt"]) {
var n = (0, c["guid"])(),
a = new _["default"]()["encrypt"](n);
while (!a || 256 !== a["length"]) {
n = (0, c["guid"])(),
a = new _["default"]()["encrypt"](n);
}
var o = i["default"]["encrypt"](e, n);
return (0, c["arrayToHex"])(o) + a;
}
}
简化后等价于:
javascript
function a(e, t) {
var n = c["guid"])(),
a = new _["default"]()["encrypt"](n);
o = i["default"]["encrypt"](e, n);
return c["arrayToHex"])(o) + a;
}
从这里可以确定参数t未被使用,参数e就是w的明文。我们继续分析e的组成部分:
javascript
{
'device_id': 'A8A0', // 固定值
'em': {
'cp': 0,
'ek': '11',
'nt': 0,
'ph': 0,
'sc': 0,
'si': 0,
'wd': 1,
}, // 固定值
'ep': '123', // 固定值
'geetest': 'captcha', // 固定值
'fq6a': '1925502591', // 固定值
'lang': 'zh', // 固定值
'lot_number': '7e22264d4f3e4dd8a6ffbf6e82e1122d', // load请求返回值
'passtime': 166, // 通过时间
'pow_msg': '1|0|md5|2022-03-25T14:23:36.364152+08:00|24f56dc13c40dc4a02fd0318567caef5|7e22264d4f3e4dd8a6ffbf6e82e1122d||29f07cebf938aa4e', // load请求返回加上某算法返回值
'pow_sign': '2b47a3a9425dd19dd5abf902c8bb0763', // pow_msg的md5值
'setLeft': 88, // 滑动距离
'track': [[38, 18, 0], [1, 0, 33]......], // 轨迹
'userresponse': 87.47978686742837 // 某算法返回值
}
4.1 分析pow_msg和pow_sign
通过搜索pow_msg,找到相关代码并打上断点,刷新页面,捕捉到以下代码:
javascript
u = n + "|" + a + "|" + s + "|" + o + "|" + t + "|" + e + "|" + r + "|"
p = (0, b["guid"])()
g = u + p更多内容联系1436423940
pow_msg = u + p
pow_sign = md5(g)
b.guid()的算法如下:
var a = function () {
function e() {
return (65536 * (1 + Math ;
}
return function () {
return e() + e() + e() + e();
};
}();
t["guid"] = a;
4.2 分析set_left、track、passtime、userresponse
set_left:滑块移动距离的整数值
track:移动轨迹,从第二步开始,是相对上一步的相对移动距离(x, y, t)
passtime:总移动时间
userresponse:计算公式为set_left / (0.8876 * 340 / 300)