【验证码逆向专栏】某盾 v2 滑动验证码逆向分析
字数 1939 2025-08-29 08:30:19

某盾v2滑动验证码逆向分析教学文档

1. 验证码概述

某盾v2滑动验证码是一种常见的反爬机制,通过滑块验证来区分人类用户和自动化程序。该验证码系统包含以下主要接口:

  • 图片接口:获取验证码图片(背景图和滑块图)
  • 验证接口:提交滑块位置进行验证

两个接口实际上是同一个接口,只是请求参数不同。

2. 关键参数分析

验证码涉及9个关键参数(P1-P9),下面详细分析每个参数的生成方式:

2.1 P1参数生成

p1 = oQO0Q0
  • oOoQQ0为固定值:"b37uCyfyme4S7TF/MVDRqSRxP4CB2BjsnDxr4bSxz0vSL/~hXNGID9Tr7vzaBm~F"
  • window._fmOpt.token:由window._fmOpt.partner、时间戳和随机数拼接而成
  • window._fmOpt.partnerwindow._fmOpt.appName:不同网站的标识
  • oO0QQo.mfaId:通常为undefined,可忽略

2.2 P2参数生成

p2 = OoOQ0O
  • QQoooQ.blackBox + ^^1^^1^^1生成(图片接口)
  • 验证接口时使用^^3^^1^^1代替^^1^^1^^1

2.3 P3参数生成

p3 = oQOoO0(ooQQ0Q, QQQOQO)
  • ooQQ0Q = QOOO0O(p1 + p2) + QOOO0O(oOOoQO)
    • oOOoQO:由固定值161155拼接时间戳构成,如"161155^^|^^|^^1739762660066"
    • QOOO0O函数:标准MD5哈希算法
  • QQQOQO
    var OOOQOQ = window._fmOpt.token.split('-');
    var QoQQo0 = OOOQOQ[OOOQOQ.length - 2] + OOOQOQ[OOOQOQ.length - 1];
    var QQQOQO = QQ00QO('stq67pv9') + QoQQo0.substring(10, 18);
    
    • QQ00QO('stq67pv9')生成固定值rsp67ou9
  • oQOoO0:AES加密函数,参数:
    • IV:Moa14C2uXpe8AUJ5
    • 需要字符替换处理(q↔p,I↔J)

Python实现示例:

from Crypto.Cipher import AES
import base64

def swap_characters(input_str):
    return input_str.replace('q', 'tem1').replace('p', 'q').replace('tem1', 'p').replace('I', 'tem2').replace('J', 'I').replace('tem2', 'J')

def encrypt_aes_cbc(data, key):
    iv = 'Mnz14C2tXod8AUJ5'
    block_size = AES.block_size
    pad = lambda s: s + (block_size - len(s) % block_size) * chr(block_size - len(s) % block_size)
    data = pad(data)
    cipher = AES.new(key.encode('latin-1'), AES.MODE_CBC, iv.encode('latin-1'))
    encrypted = cipher.encrypt(data.encode('latin-1'))
    return swap_characters(base64.b64encode(encrypted).decode('latin-1').swapcase())

2.4 P4参数生成

p4 = oQOoO0(QOo0Oo, QQQOQO)
  • oQOoO0:同上AES加密
  • QOo0Oo:图片接口直接为"|"(固定值)
  • QQQOQO:同上

2.5 P5参数生成

p5 = QoOO00
  • QQ00QO('xfc'):固定值"web"

2.6 P6参数生成

p6 = Qoo0Q0
  • QOQQO0 = o0QoQQ(8):取8位随机数
  • Qoo0Q0 = oQOoO0(QOQQO0 + window.location.href, QQQOQO)
    • oQOoO0:同上AES加密
    • window.location.href:当前页面URL(可写死)
    • QQQOQO:同上

2.7 P7参数生成

p7 = QOQ0QQ + o0QoQQ(32)
  • o0QoQQ(32):取32位随机数
  • QOQ0QQ = QOOO0O(Qoo0Q0) + QOOO0O(oOOoQO)
    • QOOO0O:标准MD5加密
    • Qoo0Q0:P6值
    • oOOoQO:同上

2.8 P8参数生成

p8 = QOQQO0
  • QOQQO0 = o0QoQQ(8):取8位随机数(与P6生成中的一致)

2.9 P9参数生成

p9 = oOOoQO
  • oOOoQO = oQOoO0(oOOoQO, QQQOQO)
    • oQOoO0:同上AES加密
    • oOOoQO:同上
    • QQQOQO:同上

3. 图片还原算法

获取的验证码大图是乱序的,需要根据bgImageSplitSequence参数还原:

  1. 将图片上下平均分割成2层,每层水平分割成8张小图,共16张小图
  2. 根据bgImageSplitSequence参数计算新的顺序
  3. 重新排序拼接

Python实现:

from io import BytesIO
from PIL import Image

def reconstruct_image(segment_sequence, image_binary):
    # 加载图像
    img_io = BytesIO(image_binary)
    original_img = Image.open(img_io)
    
    # 定义图像尺寸和分割参数
    img_width, img_height = 320, 180
    segment_width, segment_height = img_width // 8, img_height // 2
    
    # 拆分图像
    image_layers = [[None]*8 for _ in range(2)]
    for layer in range(2):
        y_start = layer * segment_height
        for i in range(8):
            x_start = i * segment_width
            crop_box = (x_start, y_start, x_start + segment_width, y_start + segment_height)
            image_layers[layer][i] = original_img.crop(crop_box)
    
    # 创建新图像
    new_image = Image.new('RGB', (img_width, img_height))
    new_image_layers = [[None]*8 for _ in range(2)]
    
    # 重新排序
    for index, hex_value in enumerate(segment_sequence):
        position = int(hex_value, 16)
        layer, segment = divmod(position, 8)
        original_layer = 1 if index >= 8 else 0
        new_image_layers[layer][segment] = image_layers[original_layer][index % 8]
    
    # 拼接图像
    for layer in range(2):
        for i in range(8):
            new_image.paste(new_image_layers[layer][i], (segment_width * i, segment_height * layer))
    
    # 转换为二进制数据
    img_byte_arr = BytesIO()
    new_image.save(img_byte_arr, format='PNG')
    return img_byte_arr.getvalue()

4. 滑块识别算法

使用OpenCV进行模板匹配识别滑块位置:

Python实现:

import cv2
import numpy as np

def bytes_to_cv2(img):
    # 将二进制数据转换为OpenCV图像
    img_buffer_np = np.frombuffer(img, dtype=np.uint8)
    img_np = cv2.imdecode(img_buffer_np, cv2.IMREAD_COLOR)
    return img_np

def get_distance(bg, tp, save_path=None):
    # 将二进制数据转换为OpenCV图像
    bg_img = bytes_to_cv2(bg)
    tp_img = bytes_to_cv2(tp)
    
    # 转换为灰度图并进行高斯模糊
    tp_gray = cv2.GaussianBlur(cv2.cvtColor(tp_img, cv2.COLOR_BGR2GRAY), (5, 5), 0)
    bg_gray = cv2.GaussianBlur(cv2.cvtColor(bg_img, cv2.COLOR_BGR2GRAY), (5, 5), 0)
    
    # 使用Canny边缘检测
    lower_threshold = 30
    high_threshold = 100
    tp_edge = cv2.Canny(tp_gray, lower_threshold, high_threshold)
    bg_edge = cv2.Canny(bg_gray, lower_threshold, high_threshold)
    
    # 使用模板匹配算法
    result = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCORR_NORMED)
    _, _, _, max_loc = cv2.minMaxLoc(result)
    
    # 寻找滑块图像的轮廓
    contours, _ = cv2.findContours(tp_edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if contours:
        # 选择面积最大的轮廓
        contour = max(contours, key=cv2.contourArea)
        x, y, width, height = cv2.boundingRect(contour)
        
        # 在背景图上绘制矩形标记滑块缺口位置
        cv2.rectangle(bg_img, (max_loc[0] + x, max_loc[1] + y), 
                     (max_loc[0] + x + width, max_loc[1] + y + height), (0, 255, 0), 2)
        
        # 保存标记后的图片
        if save_path:
            cv2.imwrite(save_path, bg_img)
        
        return {'x': max_loc[0] + x, 'y': max_loc[1] + y}
    else:
        return None

5. 验证接口特殊处理

验证接口的P1-P9参数生成与图片接口大部分相同,主要区别在于:

  1. P2参数:使用^^3^^1^^1代替^^1^^1^^1
  2. P3参数:加密的明文多了:
    • validateCodeObj:图片接口返回的对象
    • userAnswer
      userAnswer = Math.round(QoO0Oo / Oo0OOo) + QQ00QO('|10|') + new Date().getTime()
      
      • Math.round(QoO0Oq / Oo0OOo):滑块识别的距离
      • QQ00QO('|10|'):固定值"|10|"
  3. P4参数:加密的明文多了mouseInfo轨迹信息(可写死)

6. 验证结果处理

验证接口返回结果:

  • 失败:needValidateCode: true,返回图片接口信息
  • 成功:needValidateCode: false,返回validateToken

7. 总结

逆向某盾v2滑动验证码的关键点:

  1. 理解P1-P9参数的生成逻辑,特别是AES加密部分
  2. 掌握图片还原算法,正确处理bgImageSplitSequence
  3. 使用OpenCV准确识别滑块位置
  4. 验证接口与图片接口的参数差异处理
  5. 轨迹模拟需要合理生成

以上内容完整覆盖了某盾v2滑动验证码的逆向分析过程,包含了所有关键技术和实现细节。

某盾v2滑动验证码逆向分析教学文档 1. 验证码概述 某盾v2滑动验证码是一种常见的反爬机制,通过滑块验证来区分人类用户和自动化程序。该验证码系统包含以下主要接口: 图片接口 :获取验证码图片(背景图和滑块图) 验证接口 :提交滑块位置进行验证 两个接口实际上是同一个接口,只是请求参数不同。 2. 关键参数分析 验证码涉及9个关键参数(P1-P9),下面详细分析每个参数的生成方式: 2.1 P1参数生成 oOoQQ0 为固定值: "b37uCyfyme4S7TF/MVDRqSRxP4CB2BjsnDxr4bSxz0vSL/~hXNGID9Tr7vzaBm~F" window._fmOpt.token :由 window._fmOpt.partner 、时间戳和随机数拼接而成 window._fmOpt.partner 和 window._fmOpt.appName :不同网站的标识 oO0QQo.mfaId :通常为undefined,可忽略 2.2 P2参数生成 由 QQoooQ.blackBox + ^^1^^1^^1 生成(图片接口) 验证接口时使用 ^^3^^1^^1 代替 ^^1^^1^^1 2.3 P3参数生成 ooQQ0Q = QOOO0O(p1 + p2) + QOOO0O(oOOoQO) oOOoQO :由固定值 161155 拼接时间戳构成,如 "161155^^|^^|^^1739762660066" QOOO0O 函数:标准MD5哈希算法 QQQOQO : QQ00QO('stq67pv9') 生成固定值 rsp67ou9 oQOoO0 :AES加密函数,参数: IV: Moa14C2uXpe8AUJ5 需要字符替换处理(q↔p,I↔J) Python实现示例: 2.4 P4参数生成 oQOoO0 :同上AES加密 QOo0Oo :图片接口直接为 "|" (固定值) QQQOQO :同上 2.5 P5参数生成 QQ00QO('xfc') :固定值 "web" 2.6 P6参数生成 QOQQO0 = o0QoQQ(8) :取8位随机数 Qoo0Q0 = oQOoO0(QOQQO0 + window.location.href, QQQOQO) oQOoO0 :同上AES加密 window.location.href :当前页面URL(可写死) QQQOQO :同上 2.7 P7参数生成 o0QoQQ(32) :取32位随机数 QOQ0QQ = QOOO0O(Qoo0Q0) + QOOO0O(oOOoQO) QOOO0O :标准MD5加密 Qoo0Q0 :P6值 oOOoQO :同上 2.8 P8参数生成 QOQQO0 = o0QoQQ(8) :取8位随机数(与P6生成中的一致) 2.9 P9参数生成 oOOoQO = oQOoO0(oOOoQO, QQQOQO) oQOoO0 :同上AES加密 oOOoQO :同上 QQQOQO :同上 3. 图片还原算法 获取的验证码大图是乱序的,需要根据 bgImageSplitSequence 参数还原: 将图片上下平均分割成2层,每层水平分割成8张小图,共16张小图 根据 bgImageSplitSequence 参数计算新的顺序 重新排序拼接 Python实现: 4. 滑块识别算法 使用OpenCV进行模板匹配识别滑块位置: Python实现: 5. 验证接口特殊处理 验证接口的P1-P9参数生成与图片接口大部分相同,主要区别在于: P2参数 :使用 ^^3^^1^^1 代替 ^^1^^1^^1 P3参数 :加密的明文多了: validateCodeObj :图片接口返回的对象 userAnswer : Math.round(QoO0Oq / Oo0OOo) :滑块识别的距离 QQ00QO('|10|') :固定值 "|10|" P4参数 :加密的明文多了 mouseInfo 轨迹信息(可写死) 6. 验证结果处理 验证接口返回结果: 失败: needValidateCode: true ,返回图片接口信息 成功: needValidateCode: false ,返回 validateToken 7. 总结 逆向某盾v2滑动验证码的关键点: 理解P1-P9参数的生成逻辑,特别是AES加密部分 掌握图片还原算法,正确处理 bgImageSplitSequence 使用OpenCV准确识别滑块位置 验证接口与图片接口的参数差异处理 轨迹模拟需要合理生成 以上内容完整覆盖了某盾v2滑动验证码的逆向分析过程,包含了所有关键技术和实现细节。