验深知 V2 业务风控逆向分析
字数 1352 2025-08-11 08:36:24
某验深知V2业务风控逆向分析教学文档
一、目标概述
逆向分析某验深知V2业务风控系统,主要目标是理解其验证流程并实现自动化请求。深知V2通过无感采集客户端数据,对用户环境、标识和行为操作进行智能化分析,识别潜在风险用户。
二、完整通讯流程
- 访问首页,引入
v2.sense.js并提取其中的id - 发起
gettype请求,获取动态变化的gct.xxx.js资源路径 - 自动发起
judge请求,生成并发送加密的Request Payload - 获取
session_id后请求业务接口verify-dk-v2
三、关键请求分析
1. gettype请求
- 返回包含
gct.xxx.js路径的JSON gct.xxx.js会生成动态键值对(如{'xnbw': '1158444372'})- 该JS名称和内容会定期变化,导致键值对变化
2. judge请求
- Query String Parameters中包含
app_id(即首页获取的id) - Request Payload为一长串加密字符串
- 成功验证后返回
session_id
四、逆向核心参数
Request Payload由以下代码生成:
var h = o[AUJ_(1156)]();
e = CoUE[ymDv(24)](NFeB);
l = EbF_[ymDv(409)](e, h[ymDv(1194)]);
e = DWYi[ymDv(1137)](l);
1. 获取h值
h值由方法生成一个包含aeskey和rsa的对象:
{
"aeskey": RwyT(), // 16位随机值
"rsa": BPqG(RwyT()) // RSA加密后的结果
}
RSA加密算法需要完整扣取BPqG()方法。
2. 获取e值
e值是对NFeB对象的处理结果,NFeB结构如下:
NFeB = {
"id": a["id"],
"page_id": a["page_id"], // 时间戳
"lang": a["lang"] || "zh-cn",
"data": {
"insights": u || null, // 浏览器环境信息
"track_key": c["value"] ? c["key"] : null,
"track": c["value"] || null,
"ep": o["KZrg"]("client"), // 处理后的客户端信息
"eco": window["GEERANDOMTOKEN"]
}
}
关键子参数:
insights(u值):
- 由浏览器环境值以"!!"连接生成
- 包含屏幕高宽、canvas、UA、插件、时间、时区、语言等
ep值:
- 传入字符串"client"处理得到
- 关键部分为
window.performance.timing的值,需伪造时间戳
eco值(window.GEERANDOMTOKEN):
- 可通过Hook获取生成逻辑:
(function() {
var token = "";
Object.defineProperty(window, 'GEERANDOMTOKEN', {
set: function(val) {
console.log('GEERANDOMTOKEN->', val);
debugger;
token = val;
return val;
},
get: function() {
return token;
}
});
})();
生成方法:
- 生成32位随机字符串
- 加上时间戳
- 进行MD5加密
实现代码:
var MD5 = require("md5")
function getToken(){
var t = MD5(function(e) {
for (var t = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"], n = "", r = 0; r < e; r++) n += t[parseInt(61 * Math.random(), 10)];
return n;
}(32) + new Date().getTime());
return t;
}
动态键值对(r值):
- 通过
gct.xxx.js生成,如{"xnbw":"1158444372"} - 需要动态获取最新JS文件并提取键值对
动态获取示例:
import re
import time
import json
import execjs
import requests
def get_gct():
url = "https://dkapi.脱敏处理.com/deepknow/v2/gettype"
params = {
"callback": "脱敏处理_" + str(int(time.time() * 1000))
}
response = requests.get(url, headers=headers, params=params).text
response = json.loads(re.findall(r"geetest_\d+$(.+)$", response)[0])
# 获取gct.xxx.js地址
gct_path = "https://static.脱敏处理.com" + response["gct_path"]
gct_js = requests.get(gct_path, headers=headers).text
# 提取方法名并全局导出
function_name = re.findall(r"\)\)\{return (.+?)$t$", gct_js)[0]
break_position = gct_js.find("return function(t){")
gct_js_new = gct_js[:break_position] + "window.gct=" + function_name + ";" + gct_js[break_position:]
# 添加调用代码
gct_js_new = "window = global;" + gct_js_new + """
function getGct(){
var e = {"lang": "zh", "ep": "test data"};
window.gct(e);
delete e["lang"];
delete e["ep"];
return e;
}"""
gct = execjs.compile(gct_js_new).call("getGct")
return gct
3. 获取l值
使用AES加密算法,以h["aeskey"]为key,e值为待加密字符串:
var CryptoJS = require("crypto-js")
function aesEncrypt(e, i) {
var key = CryptoJS.enc.Utf8.parse(i),
iv = CryptoJS.enc.Utf8.parse("0000000000000000"),
srcs = CryptoJS.enc.Utf8.parse(e),
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. 最终处理
l值经过tc_t方法处理,返回e["res"] + e["end"]格式的结果。注意该方法与某验其他产品中的实现可能有差异,需确保使用正确的常量值。
五、完整流程总结
- 获取首页
id和gct.xxx.js路径 - 动态获取并执行
gct.xxx.js,提取键值对 - 生成
GEERANDOMTOKEN - 构建NFeB对象
- 生成h值(包含aeskey和rsa)
- 将NFeB转为字符串并进行AES加密
- 对加密结果进行最终处理
- 拼接最终Request Payload:
e + h[AUJ_(1173)]
六、注意事项
- 不同业务场景下某些参数可能可以固定,但建议保持动态获取以确保稳定性
- 浏览器环境信息需要合理伪造,特别是
window.performance.timing gct.xxx.js会定期变化,必须实现动态获取机制- 某验不同产品线的方法实现可能有细微差异,不可直接复用