【验证码逆向专栏】某验三代滑块验证码逆向分析
字数 1688 2025-08-11 17:40:05
某验三代滑块验证码逆向分析教学文档
一、验证码概述
某验三代滑块验证码是一种常见的反爬机制,主要包含以下特点:
- 采用RSA、AES、MD5等多种加密算法
- 包含乱序背景图还原机制
- 使用复杂的w参数加密验证
二、验证码流程分析
1. 初始请求阶段
register-slide接口:
- 请求参数:
t=时间戳 - 响应关键字段:
gt:固定值,不同网页对应不同的gt值challenge:每次刷新页面都会变化的值
gettype.php接口:
- 请求参数:
gt:register-slide返回的gt值callback:geetest_+ 时间戳
- 响应返回js文件及版本号
2. 验证阶段
第一个get.php接口:
- 请求参数:
gt:register-slide返回的gt值challenge:register-slide返回的challenge值w:首次可为空callback:geetest_+ 时间戳
ajax.php接口:
- 请求参数同上
- 响应返回验证码模式(slide或click)
第二个get.php接口:
- 响应关键内容:
bg:乱序带缺口背景图fullbg:乱序完整背景图slice:滑块图片c:关键参数,与aa参数相关s:关键参数,与aa参数相关
第二个ajax.php接口:
- 请求参数:
gt:register-slide返回的gt值challenge:register-slide返回的challenge值+两位字符串w:关键加密参数(需逆向)callback:geetest_+ 时间戳
三、w参数逆向分析
w参数由h和u相加组成:w = h + u
1. u参数生成
u参数生成流程:
- 生成16位随机字符串:
function random() { var random_str = ""; for (var index = 0; index < 4; index++) { random_str += (65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1); } return random_str; } - 使用RSA加密:
- 公钥值:
00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81 - 公钥模数:
10001
- 公钥值:
2. h参数生成
h参数由l参数经过加密处理得到:h = m[$_CAIAt(782)](l)
l参数生成:
l = V["encrypt"](gt["stringify"](o), r["$_CCEc"]())
其中:
r["$_CCEc"]():16位随机字符串(同u参数生成)gt["stringify"](o):JSON格式数据,包含以下关键字段:
o参数结构分析
var o = {
"lang": "zh-cn",
"userresponse": H(t, i["challenge"]), // 滑动距离+challenge
"passtime": n, // 滑块滑动时间
"imgload": r["$_CAIAt(750)"], // 图片加载时间
"aa": e, // 轨迹加密结果
"ep": r["$_CAHJd(714)]() // 性能相关
};
userresponse:
function H(t, e) {
return t + e
}
passtime:
滑动结束时间 - 开始时间
aa参数:
由三部分加密得到:
- 轨迹加密结果
- c值(从第二个get.php响应中获取)
- s值(从第二个get.php响应中获取)
轨迹加密算法:
function getAA(t, e, n) {
if (!e || !n) return t;
var r, i = 0, o = t, s = e[0], a = e[2], _ = e[4];
while (r = n.substr(i, 2)) {
i += 2;
var c = parseInt(r, 16),
u = String.fromCharCode(c),
l = (s * c * c + a * c + _) % t.length;
o = o.substr(0, l) + u + o.substr(l);
}
return o;
}
rp参数:
o["rp"] = X(i["gt"] + i["challenge"].slice(0, 32) + o["passtime"]);
其中X为MD5加密函数
ep参数:
包含每天变化的键值对,从gct.xxxxxxxx.js文件中获取
3. l参数加密
使用AES加密:
- 密钥:16位随机字符串
- 初始向量iv:
"0000000000000000" - 加密模式:CBC
- 填充方式:Pkcs7
JavaScript实现:
function aesV(o_text, random_str) {
var key = CryptoJS.enc.Utf8.parse(random_str);
var iv = CryptoJS.enc.Utf8.parse("0000000000000000");
var srcs = CryptoJS.enc.Utf8.parse(o_text);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
for (var r = encrypted, o = r.ciphertext.words, i = r.ciphertext.sigBytes, s = [], a = 0; a < i; a++) {
var c = o[a >>> 2] >>> 24 - a % 4 * 8 & 255;
s.push(c);
}
return s;
}
4. h参数加密
将l参数加密后得到:
function encryptH(l) {
var e = [];
for (var t = 0; t < l.length; t++) {
var n = l[t].toString(16);
1 == n.length && (n = "0" + n), e.push(n);
}
return e.join("");
}
四、底图还原算法
乱序背景图还原顺序:
[39,38,48,49,41,40,46,47,35,34,50,51,33,32,28,29,27,26,36,37,31,30,44,45,43,42,
12,13,23,22,14,15,21,20,8,9,25,24,6,7,3,2,0,1,11,10,4,5,19,18,16,17]
Python还原实现:
def restore_picture():
img_list = ["./乱序缺口背景图.png", "./乱序背景图.png"]
for index, img in enumerate(img_list):
image = Image.open(img)
s = Image.new("RGBA", (260, 160))
ut = [39,38,48,49,41,40,46,47,35,34,50,51,33,32,28,29,27,26,36,37,31,30,44,45,43,42,
12,13,23,22,14,15,21,20,8,9,25,24,6,7,3,2,0,1,11,10,4,5,19,18,16,17]
height_half = 80
for inx in range(52):
c = ut[inx] % 26 * 12 + 1
u = height_half if ut[inx] > 25 else 0
l_ = image.crop(box=(c, u, c + 10, u + 80))
s.paste(l_, box=(inx % 26 * 10, 80 if inx > 25 else 0))
if index == 0:
s.save("./缺口背景图片.png")
else:
s.save("./背景图片.png")
五、常见错误
- challenge不正确:
{"status": "error", "error": "illegal challenge", "user_error": "网络不给力", "error_code": "error_23"}
- w参数不正确:
{"status": "error", "error": "param decrypt error", "user_error": "网络不给力", "error_code": "error_03"}
- 无轨迹数据:
{"status": "error", "error": "not proof", "user_error": "网络不给力", "error_code": "error_21"}
- 轨迹、缺口距离或参数问题:
{"success": 0, "message": "fail"}
{"success": 0, "message": "forbidden"}
六、总结
-
验证码破解成功率约95%
-
关键点:
- w参数的正确生成
- 轨迹算法的准确实现
- 背景图的正确还原
- 各加密算法的准确实现
-
注意事项:
- 部分参数每天会变化(如mocq)
- 注意challenge值的变化(第二个ajax.php比注册请求时多两位)
- 各加密环节的顺序和参数传递要准确