【验证码逆向专栏】最新某度旋转验证码 v2 逆向分析
字数 1493 2025-08-10 16:34:31
某度旋转验证码v2逆向分析教学文档
1. 验证码概述
某度旋转验证码v2是一种基于用户交互行为的验证系统,主要包含以下特点:
- 采用旋转滑块形式
- 使用AI生成的底图(非旧版风景图)
- 包含多重加密机制
- 加入了浏览器指纹验证
- 验证流程分为多个接口交互
2. 验证码流程分析
2.1 接口交互流程
-
init接口 - 初始化验证码
- 请求参数:
ver: 固定值"1"_: 13位时间戳refer: 网站URLak: 固定值(盾ID,不同网站不同)
- 响应参数:
as: 后续加密使用tk: 后续请求使用
- 请求参数:
-
style接口 - 获取验证码样式
- 请求参数:
tk: init接口返回_: 时间戳
- 响应参数:
path: 旋转验证码底图URLbackstr: 生成fs参数的关键部分
- 请求参数:
-
log接口 - 提交验证结果
- 请求参数:
ak: 盾IDtk: init接口返回as: init接口返回fs: 加密参数(需逆向)fuid: 浏览器指纹
- 请求参数:
-
urlsubmit接口 - 最终提交
- 请求参数:
ds: log接口返回tk: log接口返回url: 提交的链接地址
- 请求参数:
2.2 验证结果判断
- 验证码未转正:响应
op值为3 - 验证码转正:响应
op值为1 - 提交成功:
{"over":0, "status":0} - 验证码信息有误:
{"status":101} - 未输入提交链接:
{"status":2} - 未添加cookies:
not allowed
3. 关键参数逆向分析
3.1 fs参数生成流程
fs参数生成分为两个阶段:
第一阶段fs生成
n.fs = (0, f.Li)(JSON.stringify(this.rzData), this.secondHandle)
this.rzData: 包含backstr及鼠标轨迹等参数this.secondHandle: 主要包含as参数
关键参数ac_c(旋转比例)计算:
Number((this.distance / (e - 52)).toFixed(2))
this.distance: 滑动的距离e: 固定值290(滑动条最大长度)
第二阶段fs生成(最终fs)
n.fs = (0, f.Li)(JSON.stringify({
common_en: n.fs, // 第一阶段生成的fs
backstr: this.cfg.backstr
}), {
key: this.newKey,
as: this.cfg.as,
method: "aes-ecb"
})
3.2 加密密钥生成
密钥生成函数getNewKey(as):
function getNewKey(as) {
var encryptedStr = as + "appsapi2";
var r = as.substr(as.length - 1, 1);
var encryptedValue;
switch(true) {
case ['A','B','C','D','E','F','G','a','b','c','d','e','f','g'].includes(r):
encryptedValue = CryptoJS.MD5(encryptedStr).toString();
break;
case ['H','I','J','K','L','M','N','h','i','j','k','l','m','n'].includes(r):
encryptedValue = CryptoJS.SHA1(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['O','P','Q','R','S','T','o','p','q','r','s','t'].includes(r):
encryptedValue = CryptoJS.SHA256(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['U','V','W','X','Y','Z','u','v','w','x','y','z'].includes(r):
encryptedValue = CryptoJS.SHA512(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['0','1','2','3','4'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, {outputLength: 256}).toString(CryptoJS.enc.Hex);
break;
case ['5','6','7','8','9'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, {outputLength: 512}).toString(CryptoJS.enc.Hex);
break;
default:
encryptedValue = encryptedStr;
}
var key = encryptedValue.slice(0, 16);
return key;
}
3.3 加密函数分析
加密函数encrypt采用AES加密,与旧版区别:
- 密钥生成方式不同:
- 旧版:
key = as + "appsapi2" - 新版:
key = getNewKey(as)(as+"appsapi2"加密后取前16位)
- 旧版:
- 填充方式不同:
- 旧版:PKCS7
- 新版:ZeroPadding
加密模式:ECB(电子码本模式)
4. 浏览器指纹fuid分析
fuid特点:
- 不同浏览器值不一致
- 相同浏览器短时间内不变
- 过一段时间会变化
- 由user-agent、canvas、plugins等浏览器属性构成
生成位置:fingerprint.js文件
F方法:encodeURIComponent()U方法:AES加密(ECB模式,PKCS7填充,固定key)
5. 模拟注意事项
-
轨迹模拟:
- 不能直接固定,需模拟构造
- 校验相对严格
- 轨迹中有重要参数
-
识别模型:
- 底图已改为AI生成(非旧版风景图)
- 需要重新训练识别模型
-
加密实现:
- 需完整实现两阶段fs生成
- 注意密钥生成规则
- 使用正确的加密模式和填充方式
6. JavaScript实现示例
const CryptoJS = require('crypto-js');
// 密钥生成函数
function getNewKey(as) {
var encryptedStr = as + "appsapi2";
var r = as.substr(as.length - 1, 1);
var encryptedValue;
switch(true) {
case ['A','B','C','D','E','F','G','a','b','c','d','e','f','g'].includes(r):
encryptedValue = CryptoJS.MD5(encryptedStr).toString();
break;
case ['H','I','J','K','L','M','N','h','i','j','k','l','m','n'].includes(r):
encryptedValue = CryptoJS.SHA1(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['O','P','Q','R','S','T','o','p','q','r','s','t'].includes(r):
encryptedValue = CryptoJS.SHA256(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['U','V','W','X','Y','Z','u','v','w','x','y','z'].includes(r):
encryptedValue = CryptoJS.SHA512(encryptedStr).toString(CryptoJS.enc.Hex);
break;
case ['0','1','2','3','4'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, {outputLength: 256}).toString(CryptoJS.enc.Hex);
break;
case ['5','6','7','8','9'].includes(r):
encryptedValue = CryptoJS.SHA3(encryptedStr, {outputLength: 512}).toString(CryptoJS.enc.Hex);
break;
default:
encryptedValue = encryptedStr;
}
return encryptedValue.slice(0, 16);
}
// AES加密函数(ZeroPadding)
function encrypt(key, word, isZeroPadding = true) {
// 实现ZeroPadding
if(isZeroPadding) {
const blockSize = 16;
const pad = blockSize - (word.length % blockSize);
word += String.fromCharCode(0).repeat(pad);
}
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse(word),
CryptoJS.enc.Utf8.parse(key),
{
mode: CryptoJS.mode.ECB,
padding: isZeroPadding ? CryptoJS.pad.ZeroPadding : CryptoJS.pad.Pkcs7
}
);
return encrypted.toString();
}
// 生成fs参数
function generateFs(rzData, secondHandle, backstr, as) {
// 第一阶段fs
const key1 = getNewKey(as);
const fs1 = encrypt(key1, JSON.stringify(rzData), true);
// 第二阶段fs(最终fs)
const payload = {
common_en: fs1,
backstr: backstr
};
const key2 = getNewKey(as);
const fsFinal = encrypt(key2, JSON.stringify(payload), true);
return fsFinal;
}
7. 总结
某度旋转验证码v2的主要防护点:
- 双重加密的fs参数
- 动态的密钥生成算法
- 严格的轨迹验证
- 浏览器指纹检测
- 多阶段的验证流程
逆向关键点:
- 完整跟踪两阶段fs生成过程
- 正确实现密钥生成算法
- 模拟真实的用户交互轨迹
- 处理浏览器指纹参数
- 注意加密细节(模式、填充方式等)