某验3代分析
字数 1241 2025-08-18 11:35:59

某验3代滑块验证码逆向分析教程

声明

本教程所有内容仅供学习交流使用,严禁用于商业用途和非法用途!文中涉及的所有敏感信息均已做脱敏处理。

前言

某验3代滑块验证码近期加强了校验机制,现在需要逆向三个相互关联的w参数才能通过验证。本文将详细分析每个w参数的生成逻辑。

验证流程分析

接口调用顺序

  1. register-slide接口:返回challenge和gt值
  2. get.php接口:返回c和s参数(新版w参数不能为空)
  3. ajax.php接口:返回验证码类型(必须请求且w不能为空)
  4. 第二次get.php接口:返回滑块图片和底图
  5. 滑动完成后生成validate参数,作为后续登录令牌

第一个w参数逆向分析

定位方法

搜索"\u0077"可以定位到w的生成位置:w=i+r

关键代码

var r = t[$_CEFDY(1196)]()
o = $_BEH()[$_CEFCV(1127)](fe[$_CEFDY(431)](t[$_CEFCV(370)]), t[$_CEFDY(1143)]())
i = R[$_CEFDY(1197)](o)

r值分析

  • this[$_CGHDO(1143)](e)生成16位随机字符串
  • new G()[$_CGHDO(93)]是RSA加密函数
  • 需要扣下G函数或获取RSA公钥和模值
  • r值为RSA加密后的16位随机字符串

o值分析

  • 加密方法:$_BEH()[$_CEFCV(1127)]
  • 参数1:t[$_CEFDY(1143)]()(RSA加密的key)
  • 参数2:fe[$_CEFDY(431)](t[$_CEFCV(370)])
  • 实际是AES加密,初始向量为0000000000000000

AES加密实现

function Aes_encrypt(text, key_value) {
    var key = CryptoJS.enc.Utf8.parse(key_value);
    var iv = CryptoJS.enc.Utf8.parse("0000000000000000");
    var srcs = CryptoJS.enc.Utf8.parse(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;
}

i值分析

  • i值为两个value相加
  • 传入参数为AES加密后的值
  • 需要扣下m模块的函数

明文参数

  • gt和challenge:来自/register-slider接口
  • offline和static_servers:固定值

第二个w参数逆向分析

定位方法

无法通过搜索"\u0077"定位,需要通过跟栈方式找到生成位置

生成方式

w=R["encrypt"](r,key),与第一个w中的i生成方式相同

nr参数结构

{
    "lang": "zh-cn",
    "type": "fullpage",
    "tt": "M3*8Pjp8Pj9HbUp8PN9U),,:A(,(5(,(m-BJBFB:bgfA9/1O6*:I:JkNjRj31RkK**2KDRjE1S0OMM9*)-2.k6h)).E-:-)-9-:(:5b9-:1Mal2UK1RjY1I****)*:F3)pM0/JBBBA(((((,((iB9(((((,(5bn)BBBo95(,(qcjc*)R)fM2*QWU3cUA.N9?-G5N(:(?-N6,B1-2OUS_M9b?M:(A-)19d_cUS/BTF@AfC*Mf5?M95U-)1E1*OE(mj@.NQJ2@(g5@Acb?T)0N5u9khbE6,:CX)*E/B5-*Mb*)ME-((((M(((((((Lqqqp(Df((((((bb55,55(5((,((n-.(--88e(qR@).?2WE-Q(c19M9-)M919/)MM/)(P-U-(/)M*/.M*-)4)M@-N9d5Y-,-d(?b9/,M1AB9*nF)2(J*Df*M9/)MfN9*)(UU(0)(N1I-*b9/)(0qqM)qqp(-n",
    "s": "c7c3e21112fe4f741921cb3e4ff9f7cb",
    "h": "321f9af1e098233dbd03f250fd2b5e21",
    "hh": "39bd9cad9e425c3a8f51610fd506e3b3",
    "hi": "09eb21b3ae9542a9bc1e8b63b3d9a467",
    "vip_order": -1,
    "ct": -1,
    "ep": {...},
    "passtime": 5365,
    "rp": "0d51406b2c658811294a91e9ea533bed",
    "captcha_token": "541381339",
    "gdyf": "kqy8o0w7"
}

rp参数生成

const CryptoJS = require("crypto-js");
let gtt = '019924a82c70bb123aae90d483087f94';
let challenge = '7d59427b8c64734df3d8aa8585311fac';
let rp = CryptoJS.MD5(gtt + challenge + 1986).toString();

第三个w参数逆向分析

生成方式

与第一个w类似:w = h + u

关键参数

  • userresponse:滑动距离 + challenge的值
  • passtime:滑块滑动时间
  • imgload:图片加载时间
  • aa:轨迹加密
  • h9s9:每天变化的key/value(可固定)
  • rp:gt + 32位challenge + passtime的MD5加密

rp参数生成

var CryptoJS = require("crypto-js");
gt = '019924a82c70bb123aae90d483087f94'
challenge = '7d59427b8c64734df3d8aa8585311fac'
var rp = CryptoJS.MD5(gtt + challenge + 476).toString()

userresponse生成

// t为滑动距离,i[$_CAHJS(134)]为最新的challenge
var userresponse = H(t, i[$_CAHJS(134)])

aa参数生成

  • 由轨迹数组加密生成
  • 需要扣下轨迹加密函数
  • 关键参数:
    • 轨迹加密结果
    • c值
    • s值

轨迹加密函数示例

function ct(t) {
    // 省略具体实现
    W['prototype'] = {
        "\u0024\u005f\u0046\u0044\u004c": function (trace) {
            // 轨迹加密逻辑
        }
    }
    aa = W['prototype']['\u0024\u005f\u0042\u0042\u0045\u0049'](W['prototype']['\u0024\u005f\u0046\u0044\u004c'](trace), C, S);
    return aa;
}

辅助功能实现

底图还原

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")

缺口识别(方案A)

import io
from PIL import Image
import cv2
import numpy as np

def pilImgToCv2(img: Image.Image, flag=cv2.COLOR_RGB2BGR):
    return cv2.cvtColor(np.asarray(img), flag)

def getDistance(img: Image.Image, slice: Image.Image):
    grayImg = pilImgToCv2(img, cv2.COLOR_BGR2GRAY)
    graySlice = pilImgToCv2(slice, cv2.COLOR_BGR2GRAY)
    grayImg = cv2.Canny(grayImg, 255, 255)
    graySlice = cv2.Canny(graySlice, 255, 255)
    result = cv2.matchTemplate(grayImg, graySlice, cv2.TM_CCOEFF_NORMED)
    maxLoc = cv2.minMaxLoc(result)[3]
    distance = maxLoc[0]
    return distance

缺口识别(方案B)

import ddddocr

slide = ddddocr.DdddOcr(det=False, ocr=False)
with open('bg.jpg', 'rb') as f:
    target_bytes = f.read()
with open('fullpage.jpg', 'rb') as f:
    background_bytes = f.read()
res = slide.slide_comparison(target_bytes, background_bytes)
print(res)

轨迹模拟

import random

def __ease_out_expo(sep):
    if sep == 1:
        return 1
    else:
        return 1 - pow(2, -10 * sep)

def get_slide_track(distance):
    slide_track = [
        [random.randint(-50, -10), random.randint(-50, -10), 0],
        [0, 0, 0],
    ]
    count = 40 + int(distance / 2)
    t = random.randint(50, 100)
    _x = 0
    _y = 0
    for i in range(count):
        x = round(__ease_out_expo(i / count) * distance)
        t += random.randint(10, 50)
        if x == _x:
            continue
        slide_track.append([x, _y, t])
        _x = x
    slide_track.append(slide_track[-1])
    return slide_track

总结

  1. 三个w参数相互关联,缺一不可
  2. 第一个w:RSA+AES加密组合
  3. 第二个w:与无感验证类似,需要生成复杂的nr参数
  4. 第三个w:包含轨迹加密和多种参数组合
  5. 需要配合底图还原、缺口识别和轨迹模拟等辅助功能

通过完整实现上述所有环节,即可成功逆向某验3代滑块验证码。

某验3代滑块验证码逆向分析教程 声明 本教程所有内容仅供学习交流使用,严禁用于商业用途和非法用途!文中涉及的所有敏感信息均已做脱敏处理。 前言 某验3代滑块验证码近期加强了校验机制,现在需要逆向三个相互关联的w参数才能通过验证。本文将详细分析每个w参数的生成逻辑。 验证流程分析 接口调用顺序 register-slide接口 :返回challenge和gt值 get.php接口 :返回c和s参数(新版w参数不能为空) ajax.php接口 :返回验证码类型(必须请求且w不能为空) 第二次get.php接口 :返回滑块图片和底图 滑动完成后生成validate参数,作为后续登录令牌 第一个w参数逆向分析 定位方法 搜索 "\u0077" 可以定位到w的生成位置: w=i+r 关键代码 r值分析 this[$_CGHDO(1143)](e) 生成16位随机字符串 new G()[$_CGHDO(93)] 是RSA加密函数 需要扣下G函数或获取RSA公钥和模值 r值为RSA加密后的16位随机字符串 o值分析 加密方法: $_BEH()[$_CEFCV(1127)] 参数1: t[$_CEFDY(1143)]() (RSA加密的key) 参数2: fe[$_CEFDY(431)](t[$_CEFCV(370)]) 实际是AES加密,初始向量为 0000000000000000 AES加密实现 i值分析 i值为两个value相加 传入参数为AES加密后的值 需要扣下m模块的函数 明文参数 gt和challenge:来自/register-slider接口 offline和static_ servers:固定值 第二个w参数逆向分析 定位方法 无法通过搜索 "\u0077" 定位,需要通过跟栈方式找到生成位置 生成方式 w=R["encrypt"](r,key) ,与第一个w中的i生成方式相同 nr参数结构 rp参数生成 第三个w参数逆向分析 生成方式 与第一个w类似: w = h + u 关键参数 userresponse:滑动距离 + challenge的值 passtime:滑块滑动时间 imgload:图片加载时间 aa:轨迹加密 h9s9:每天变化的key/value(可固定) rp:gt + 32位challenge + passtime的MD5加密 rp参数生成 userresponse生成 aa参数生成 由轨迹数组加密生成 需要扣下轨迹加密函数 关键参数: 轨迹加密结果 c值 s值 轨迹加密函数示例 辅助功能实现 底图还原 缺口识别(方案A) 缺口识别(方案B) 轨迹模拟 总结 三个w参数相互关联,缺一不可 第一个w:RSA+AES加密组合 第二个w:与无感验证类似,需要生成复杂的nr参数 第三个w:包含轨迹加密和多种参数组合 需要配合底图还原、缺口识别和轨迹模拟等辅助功能 通过完整实现上述所有环节,即可成功逆向某验3代滑块验证码。