欢迎光临
我们一直在努力

极验GT4的w参数怎么生成的?AES+RSA双层加密与PoW机制分析

免责声明: 本文所有分析均基于公开可访问的前端 JS 代码及极验官网演示页(https://www.geetest.com/adaptive-captcha),仅用于安全研究、学习与了解验证码防护机制。文中所有敏感参数(RSA 公钥模数、captcha_id 等)均来自极验官方公开演示环境,不涉及任何第三方业务系统。请勿将本文技术用于任何未授权的系统,违者后果自负。

一、背景介绍

极验 GT4(第四代行为验证)是国内应用最广泛的人机验证方案之一,支持滑动拼图、文字点选、一点即过、消消乐等多种验证形式。本文以滑动拼图验证(slide) 为研究对象,分析其前端安全机制。

基本交互逻辑:

  • 前端携带 captcha_id + 动态 challenge 请求 load 接口,获取背景图(bg)、滑块图(slice)、会话标识(lot_number)及工作量证明参数(pow_detail)
  • 用计算机视觉识别滑块应移动到的缺口位置,得到 distance
  • 前端将滑动距离、耗时、设备信息等核心字段组装为 w_data,经 AES-CBC + RSA 双层加密后构造参数 w
  • 携带 w 及会话参数提交 verify 接口,服务端返回 result: success/fail
  • 研究核心难点:

    • 识别缺口距离(计算机视觉)
    • 还原 w 参数的加密体系(AES-CBC 对称加密 + RSA 非对称加密密钥)
    • 工作量证明(PoW)参数计算(动态 bits/hashfunc 适配)

    二、整体流程图

    本地生成 challenge(UUID v4)+ callback(时间戳)

    load 接口(GET /load)
    ↓ 返回 lot_number、bg、slice、pow_detail、payload、process_token
    下载背景图 + 滑块图

    ddddocr 识别缺口距离 distance

    计算工作量证明 pow_msg / pow_sign

    组装 w_data(distance、passtime、userresponse 等字段)

    AES-CBC 加密 w_data → aes_data
    RSA 加密 AES 密钥 → rsa_data
    w = aes_data + rsa_data

    verify 接口(GET /verify)→ result: success/fail


    三、抓包分析

    3.1 load 接口

    请求:

    GET https://gcaptcha4.geetest.com/load

    关键请求参数:

    参数来源说明
    callback 本地生成 'geetest_' + Date.now(),用于 JSONP 回调
    captcha_id 业务方配置 绑定某个具体业务,从前端 JS 中提取,固定值
    challenge 本地生成 UUID v4 格式,每次请求动态生成(源码:config.challenge || uuid())
    client_type 固定 web
    risk_type 固定 slide 表示滑动拼图,不同验证形式对应不同值
    lang 固定 zh

    challenge 在 GT4 的 gt4.js 源码中明确写为 config.challenge || uuid(),即业务方不传时自动生成 UUID,因此可本地用 uuid.uuid4() 替代。

    响应示例(JSONP 格式):

    geetest_xxxxxxxxxx({
    "status": "success",
    "data": {
    "lot_number": "614c7e56806748cfad5d2eeca241293f",
    "captcha_type": "slide",
    "bg": "captcha_v4/xxx/bg/xxx.jpg",
    "slice": "captcha_v4/xxx/slice/xxx.png",
    "ypos": 14,
    "pow_detail": {
    "version": "1",
    "bits": 0,
    "datetime": "2026-04-15T21:20:34.369357+08:00",
    "hashfunc": "md5"
    },
    "payload": "AgFD8g…",
    "process_token": "ee684e49…",
    "payload_protocol": 1
    }
    })

    响应为 JSONP 格式,需动态定位括号位置解析,不能硬编码偏移量(callback 名长度随时间戳变化)。

    3.2 verify 接口

    请求:

    GET https://gcaptcha4.geetest.com/verify

    关键请求参数:

    参数来源说明
    captcha_id 同 load 业务方固定值
    lot_number load 响应 本次会话唯一标识
    risk_type 固定 slide
    payload load 响应 服务端加密载荷,原样透传
    process_token load 响应 会话凭证,原样透传
    payload_protocol load 响应 协议版本,原样透传
    pt load 响应 原样透传
    w 本地加密生成 核心参数,见第四节

    四、JS 逆向——w 参数加密体系

    w 是 verify 接口的核心参数,其生成逻辑可直接在极验 CDN 分发的 gt4.js(未混淆)中阅读。

    4.1 w 参数结构

    w = AES_CBC_Encrypt(JSON.stringify(w_data), aes_key) [hex]
    + RSA_Encrypt(aes_key) [hex]

    即:AES 加密后的数据 hex 字符串 拼接 RSA 加密密钥 hex 字符串,服务端先用私钥解出 AES 密钥,再解密出 w_data。

    4.2 w_data 核心字段

    字段含义说明
    setLeft 滑块移动像素 ddddocr 识别结果
    passtime 滑动总耗时(ms) 随机范围 1300~2000
    userresponse 实际响应距离 distance / 1.0059466… + 2,固定换算公式
    lot_number 会话标识 来自 load 响应
    pow_msg PoW 消息 见第五节
    pow_sign PoW 签名 见第五节
    gee_guard 环境检测结果 固定结构,包含多项 bot 检测标志
    em 环境检测补充 固定结构

    4.3 AES 加密

    算法:AES-128-CBC,PKCS7 填充,输出 hex

    密钥处理规则(来自 gt4.js 逆向):

    • key_str 长度 < 16:取其 MD5 digest(16 字节)作为密钥
    • key_str 长度 ≥ 16:截取前 16 字节
    • IV 处理规则相同,默认 IV 为 "0000000000000000"

    def AES_Encrypt(word: str, key_str: str, iv_str: str = "0000000000000000") > str:
    """AES-128-CBC 加密,PKCS7 填充,输出 hex"""
    key_bytes = (hashlib.md5(key_str.encode()).digest()
    if len(key_str) < 16
    else key_str.encode()[:16])
    iv_bytes = (hashlib.md5(iv_str.encode()).digest()
    if len(iv_str) < 16
    else iv_str.encode()[:16])
    cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
    return cipher.encrypt(pad(word.encode(), AES.block_size)).hex()

    4.4 RSA 加密

    算法:RSA PKCS1 v1.5,公钥通过模数(hex)+ 固定指数(65537)构造,输出 hex

    RSA 公钥的模数(modulus)虽以 hex 字符串形式存在于 gt4.js 中,但实际使用时需通过调试断点或 Hook 加密函数的方式动态确认其与当前 SDK 版本的对应关系,不建议直接静态读取硬编码。加密的内容是随机生成的 AES 密钥字符串,服务端持有私钥还原后用于解密 w_data。

    def RSA_Encrypt(plaintext: str) > str:
    """RSA PKCS1 v1.5 加密,modulus 需通过调试从 gt4.js 中获取,输出 hex"""
    modulus = int("<调试获取的 modulus hex 字符串>", 16)
    key = RSA.construct((modulus, 65537))
    return PKCS1_v1_5.new(key).encrypt(plaintext.encode()).hex()

    4.5 AES 密钥生成

    每次请求随机生成 16 字节(4 × 4 位随机 hex),保证每次 w 不重复:

    def get_random_key() > str:
    """生成 16 字节随机 hex 字符串,作为 AES 会话密钥"""
    return ''.join(format(random.getrandbits(16), '04x') for _ in range(4))


    五、工作量证明(PoW)机制

    GT4 在 load 响应中下发 pow_detail,要求客户端提交满足条件的哈希值,用于防刷。

    5.1 pow_detail 结构

    字段说明
    version 协议版本,固定 "1"
    bits 要求哈希结果前导零 bit 数;bits=0 表示无要求,任意值即可
    hashfunc 哈希算法,md5 或 sha256
    datetime 服务端当前时间戳,计入消息体防重放

    5.2 pow_msg 格式

    pow_msg = f"1|{bits}|{hashfunc}|{datetime}|{captcha_id}|{lot_number}||{random_key}"
    pow_sign = hashfunc(pow_msg)

    当 bits > 0 时,需循环碰撞直到 pow_sign 满足前导零条件:

    def get_pow_info(pow_detail: dict, captcha_id: str, lot_number: str):
    """
    动态计算 PoW,适配 bits=0(直接返回)和 bits>0(碰撞循环)两种场景。
    """

    bits = pow_detail.get('bits', 0)
    datetime = pow_detail.get('datetime', '')
    hashfunc = pow_detail.get('hashfunc', 'md5')
    hash_fn = hashlib.sha256 if hashfunc == 'sha256' else hashlib.md5
    prefix = '0' * (bits // 4) # 需要多少个前导零 hex 字符

    while True:
    key = get_random_key()
    pow_msg = f'1|{bits}|{hashfunc}|{datetime}|{captcha_id}|{lot_number}||{key}'
    pow_sign = hash_fn(pow_msg.encode()).hexdigest()
    if bits == 0 or pow_sign.startswith(prefix):
    return pow_msg, pow_sign


    六、图像识别——计算缺口距离

    使用开源库 ddddocr 进行滑块缺口匹配,支持直接传入图片二进制,无需写入磁盘:

    from ddddocr import DdddOcr

    def ddddocr_get_distance(bg: bytes, tp: bytes) > int:
    """
    利用 ddddocr 识别滑块缺口位置,返回滑动距离(像素)。
    :param bg: 背景图二进制(带缺口的完整背景)
    :param tp: 滑块图二进制(带透明通道的拼图块)
    :return: 缺口 x 坐标(像素)
    """

    det = DdddOcr(det=False, ocr=False, show_ad=False)
    res = det.slide_match(tp, bg, simple_target=True)
    return int(res['target'][0])

    res['target'] 返回 [left, top, right, bottom],left 即缺口的 x 坐标。

    极验 GT4 图片为标准分辨率(非 Retina 2x),无需额外 /2 校正,与数美等 SDK 不同。

    备选方案:OpenCV 模板匹配

    若 ddddocr 识别不准,可改用 Canny 边缘检测 + 模板匹配:

    import cv2

    def opencv_get_distance(bg_path: str, slice_path: str) > int:
    """OpenCV Canny 边缘检测 + 模板匹配识别缺口距离"""
    bg = cv2.imread(bg_path)
    tp = cv2.imread(slice_path, cv2.IMREAD_UNCHANGED)
    # 统一灰度化
    bg_gray = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
    tp_gray = (cv2.cvtColor(tp, cv2.COLOR_BGRA2GRAY)
    if tp.shape[2] == 4
    else cv2.cvtColor(tp, cv2.COLOR_BGR2GRAY))
    # 边缘检测后模板匹配
    result = cv2.matchTemplate(
    cv2.Canny(bg_gray, 50, 150),
    cv2.Canny(tp_gray, 50, 150),
    cv2.TM_CCOEFF_NORMED
    )
    return cv2.minMaxLoc(result)[3][0] # max_loc[0] 即 x


    七、JSONP 响应解析

    GT4 的 load/verify 接口均返回 JSONP 格式,回调函数名包含毫秒级时间戳,长度不固定,不能用固定偏移量(如 [22:-1])截取,应动态定位括号:

    def parse_jsonp(text: str) > dict:
    """动态解析 JSONP 响应,提取 JSON 部分"""
    json_str = text[text.index('(') + 1: text.rindex(')')]
    return json.loads(json_str)


    八、完整请求流程概述

    步骤动作说明
    1 生成会话标识 本地生成 challenge(uuid.uuid4())和 callback(geetest_ + 时间戳)
    2 调用 load 接口 获取 lot_number、背景图/滑块图路径、pow_detail、payload、process_token
    3 下载图片 从 https://static.geetest.com/ 下载 bg 和 slice
    4 识别缺口距离 ddddocr slide_match 得到 distance
    5 计算 PoW 根据 pow_detail.bits 和 hashfunc 动态碰撞,得到 pow_msg/pow_sign
    6 组装 w_data 填入 distance、passtime、userresponse、pow 等字段
    7 加密构造 w AES_CBC(w_data, aes_key) + RSA(aes_key) 拼接
    8 调用 verify 接口 携带 w 及会话参数,解析返回 result: success/fail

    九、关键知识点总结

    知识点详情
    challenge 来源 本地 uuid.uuid4() 生成,GT4 源码明确支持(config.challenge || uuid())
    captcha_id 业务方配置的固定值,从前端 JS 提取,不会每次变化
    图像识别库 ddddocr 滑块匹配,GT4 无 Retina 缩放,无需 /2
    AES 加密 AES-128-CBC,PKCS7 填充,输出 hex;密钥短于 16 字节时取 MD5
    RSA 加密 PKCS1 v1.5,公钥参数可从 gt4.js 明文读取,输出 hex
    w 结构 AES(w_data) hex + RSA(aes_key) hex 直接拼接
    PoW 机制 动态适配 bits/hashfunc,bits=0 时直接返回,bits>0 时循环碰撞
    JSONP 解析 动态定位 ( ) 位置,不能硬编码偏移量
    Session 管理 使用 requests.Session 自动携带服务端下发的 Cookie(如 captcha_v4_user)

    十、与数美 SDK(protocol=206)的横向对比

    对比维度极验 GT4数美 SDK(新版)
    加密算法 AES-CBC + RSA,输出 hex DES-ECB,输出 Base64
    加密参数数量 合并为 1 个 w 参数 分散为 12 个独立字段
    密钥来源 本地随机生成 AES Key + 固定公钥 硬编码 Key 逆向 + 动态派生 __key
    工作量证明 有 PoW 机制(pow_detail 动态下发) 无 PoW
    图片分辨率 标准分辨率,无缩放 Retina 2x,需 /2 校正
    序列化 标准 json.dumps 自定义 smStringify(差异处理 undefined)
    JSONP 解析 需动态解析 需动态解析
    JS 混淆程度 gt4.js 基本可读 obfuscator.io 重度混淆,需 webcrack 反混淆

    十一、依赖安装

    pip install requests pycryptodome ddddocr opencv-python


    本文技术仅供安全研究与学习,切勿用于任何未授权系统,违者后果自负。

    赞(0)
    未经允许不得转载:171主机测评 » 极验GT4的w参数怎么生成的?AES+RSA双层加密与PoW机制分析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址