在这篇文章中,我们将介绍如何使用JavaScript来逆向工程gcaptcha4.js文件,找到并破解w参数的加密算法。这个过程包括观察verify请求,定位加密位置,分析加密算法,最终还原w参数的明文。
一. 观察verify请求
首先,我们需要观察verify请求的发起者。在网络请求中,我们可以看到所有verify请求都来自于gcaptcha4.js文件。因此,这个文件成为我们分析的重点。
我们使用JavaScript的fetch API来捕获和分析这些请求。
javascript
fetch('https://example.com/verify')
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
通过分析响应内容,可以发现所有的verify请求都指向了gcaptcha4.js文件。
二. 定位w参数加密位置
为了找到w参数的加密位置,我们需要解析gcaptcha4.js文件。我们可以下载并格式化这个文件,然后使用正则表达式搜索关键词:w、.w、'w'或"w"。
javascript
const fs = require('fs');
fs.readFile('gcaptcha4.js', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
const matches = data.match(/[\.\'\"]w[\.\'\"]/g);
console.log(matches);
});
通过搜索"w",我们找到了相关代码。在第2527行,我们发现了w的值r在第2525行被定义。
三. 分析w参数加密算法
接下来,我们需要简化代码中w的定义。假设我们已经提取了相关的JavaScript代码,我们可以使用JavaScript来模拟这个过程。
javascript
const crypto = require('crypto');
function stringify(data) {
return JSON.stringify(data);
}
function defaultEncrypt(data, key) {
const hash = crypto.createHash('md5');
hash.update(data + key);
return hash.digest('hex');
}
// 示例数据
const e = { /* your data */ };
const a = 'your_key';
const r = defaultEncrypt(stringify(e), a);
console.log(r);
首先,stringify(e)相当于对e进行JSON序列化操作。然后,我们分析defaultEncrypt(stringify(e), a)的含义。
javascript
function encrypt(e, t) {
const n = generateGuid();
let a = encryptor.encrypt(n);
while (!a || a.length !== 256) {
n = generateGuid();
a = encryptor.encrypt(n);
}
const o = encryptor.encrypt(e, n);
return arrayToHex(o) + a;
}
function generateGuid() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function arrayToHex(input) {
return input.reduce((acc, val) => acc + val.toString(16), '');
}
// 示例数据
const e = 'example_data';
const t = 'example_key';
const result = encrypt(e, t);
console.log(result);
从这里可以确定参数t未被使用,参数e就是w的明文。接着我们分析e的组成部分:
javascript
const e = {
'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
function generateGuid() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function md5(input) {
return crypto.createHash('md5').update(input).digest('hex');
}
const n = 'example_n';
const a = 'example_a';
const s = 'example_s';
const o = 'example_o';
const t = 'example_t';
const e = 'example_e';
const r = 'example_r';
const u = `${n}|${a}|${s}|${o}|${t}|${e}|${r}|`;
const p = generateGuid();
const g = u + p;
const pow_msg = u + p;
const pow_sign = md5(g);
console.log('pow_msg:', pow_msg);
console.log('pow_sign:', pow_sign);
4.2 分析set_left、track、passtime、userresponse
set_left:滑块移动距离的整数值
track:移动轨迹,从第二步开始,是相对上一步的相对移动距离(x, y, t)
passtime:总移动时间
userresponse:计算公式为set_left / (0.8876 * 340 / 300)
javascript
const set_left = 88;
const track = [[38, 18, 0], [1, 0, 33]......];
const passtime = 166;
const userresponse = set_left / (0.8876 * 340 / 300);
console.log('set_left:', set_left);
console.log('track:', track);
console.log('passtime:', passtime);
console.log('userresponse:', userresponse);
至此,我们已经完成了w参数的明文解析。