逆向工程实战手册 第 108 章

第 108 章 验证码专项深度(图像处理与轨迹解法)

第 106/107 章讲了验证码的定位与原理;本章是验证码求解的完整实战手册:滑块/点选/旋转/拼图/无感型逐类展开,重点是图像处理代码(缺口定位、旋转角度、目标识别)与轨迹的配合。核心认知:解法正确只占一半,轨迹像人才是另一半。

📍 知识点地图 | 主题:验证码专项深度 | 前置:第106/107章 | 后续:— | 核心概念:滑块缺口定位、点选识别、旋转角度、轨迹配合

108.1 验证码分类学(先识别类型)

交互型(有界面交互):
  □ 滑块(拖动缺口到目标)—— 最常见
  □ 点选(选出指定物体/文字)
  □ 旋转(旋转图片到正位)
  □ 拼图(滑动拼块到缺口)
  □ 文字输入(OCR 型,越来越少)
  □ 算术/逻辑(简单题,罕见)

无感型(无交互,后台判定):
  □ reCAPTCHA v3(评分制)
  □ Turnstile(无感挑战)
  □ hCaptcha enterprise(有时无感)

本质: 验证码 = 判定"是不是人"的挑战
  → 交互型: 解法正确 + 行为像人
  → 无感型: 行为+指纹真实(第 107 章)

108.2 滑块验证码(最常实战)

流程

1. 定位: 滑块按钮坐标 + 目标缺口坐标
2. 计算距离: 缺口中心 - 滑块中心 = 拖动距离
3. 生成轨迹: 贝塞尔曲线(第 107.5 算法)+ 抖动
4. 执行: CDP 原生事件拖拽(第 107.5)
5. 验证: 是否通过(失败重试)

滑块缺口定位(图像处理)

# 用 OpenCV 找滑块缺口(经典方法)
import cv2
import numpy as np

def find_gap(bg_img, slider_img):
    """
    背景图 + 滑块小图 → 返回缺口 x 坐标
    原理: 缺口区域的边缘特征与背景不同(亮度差异)
    """
    # 转灰度
    bg_gray = cv2.cvtColor(bg_img, cv2.COLOR_BGR2GRAY)
    slider_gray = cv2.cvtColor(slider_img, cv2.COLOR_BGR2GRAY)

    # 边缘检测(缺口有明显的边缘响应)
    bg_edge = cv2.Canny(bg_gray, 100, 200)
    slider_edge = cv2.Canny(slider_gray, 100, 200)

    # 模板匹配(滑块轮廓在背景中找最相似位置)
    result = cv2.matchTemplate(bg_edge, slider_edge, cv2.TM_CCOEFF_NORMED)
    _, max_val, _, max_loc = cv2.minMaxLoc(result)

    # max_loc[0] = 缺口左边缘 x 坐标
    return max_loc[0] + slider_img.shape[1] // 2  # 返回缺口中心
注意: 真实滑块背景的缺口"无滑块轮廓"(滑块已经被移走)
  → 常见做法: 对比"有缺口背景" vs "无缺口背景"(两张图差异)
  → 或: 缺口区域 = 背景中"最不像背景纹理"的矩形

更稳的方法(两张图对比):
  diff = abs(bg_with_gap - bg_without_gap)   # 差异图
  → 差异最大的列 = 缺口位置

距离校准(关键细节)

坑: 页面坐标 vs 图片坐标(缩放/偏移)
  □ 截图比例(devicePixelRatio 缩放)
  □ 滑块初始位置偏移(不是 0)
  □ 缺口位置在"可拖动范围"内的偏移
校准: 用已知滑动距离 vs 实际效果反推比例(试一次)

108.3 点选验证码(选中指定物体)

流程

1. 识别任务: OCR/目标检测解析"请选出所有 X"(中文/英文提示)
2. 定位目标: 在图片中找目标物体(每格坐标)
3. 点击: 人类化点击(第 107.5 轨迹)
4. 多选: 逐一点击 + 提交

目标识别(两种路线)

# 路线 1: 视觉模板(简单场景)
#   提示词在图片里(如"包含汽车")→ 模板匹配/颜色分割找目标

# 路线 2: 深度学习目标检测(复杂场景)
#   YOLO 类模型识别"汽车/红绿灯/行人"等
#   部署: ultralytics YOLOv8 加载模型 → 预测 → 输出框坐标
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model(captcha_image)
for r in results:
    for box in r.boxes:
        cls = int(box.cls[0])
        if r.names[cls] in target_classes:   # 目标类别
            x1, y1, x2, y2 = map(int, box.xyxy[0])
            click_point = ((x1+x2)//2, (y1+y2)//2)
实战建议:
  简单验证码(图标固定)→ 模板匹配够用
  复杂验证码(真实照片)→ YOLO 预训练模型
  中文提示解析 → OCR(PaddleOCR)+ 语义映射

108.4 旋转验证码

原理

图片被旋转了角度,需要转回正位(拖动滑杆)
检测: 旋转角度计算 + 轨迹执行

角度计算(图像处理)

# 方法: 找"正位特征"(文字/对称结构)偏离角度
# 经典: 图像旋转不变性分析(方向梯度直方图 HOG / 傅里叶变换)

import cv2
import numpy as np

def find_rotation_angle(img):
    """用霍夫变换/边缘方向估计旋转角度(简化示例)"""
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150)
    # 检测直线 → 取主导角度
    lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100)
    angles = []
    for line in lines:
        x1, y1, x2, y2 = line[0]
        angle = np.degrees(np.arctan2(y2-y1, x2-x1))
        angles.append(angle)
    # 统计主导角度(中位数)
    return np.median(angles) if angles else 0

# 实际中: 多次小步旋转 + 评分(图像"正"的程度)更稳
#   旋转后检测文字/形状对齐度 → 找到对齐度最高的角度

108.5 拼图验证码

原理: 拼块 + 缺口(类似滑块但形状不规则)
解法: 缺口定位(同 108.2)+ 拼块拖动(同滑块)
差异: 距离 = 缺口位置 - 拼块初始位置(精确校准更重要)

108.6 无感验证码(reCAPTCHA v3 / Turnstile)

原理: 后台评分(不交互)→ 分数低于阈值才出交互挑战
  → 绕过核心 = 让评分高(指纹+行为真实,第 107 章)

关键点:
  □ 评分输入: 行为/指纹/上下文/历史
  □ 无"解法"可言 → 只能"优化分数"
  □ 失败表现: 从无感降级为交互(然后走 108.2-108.5)

Turnstile 特别说明:
  □ Cloudflare 的无感验证码
  □ token 与页面上下文绑定(同页多次/跨页失效)
  □ 方案: 路线 A(真实浏览器)成功率最高

108.7 轨迹-解法配合(成败关键)

错误: 答对了但轨迹像机器(直线/匀速/精确网格)
正确: 解法正确 + 人类化轨迹(第 107.5)

完整执行链:
  1. 计算目标位置(图像处理)
  2. 生成贝塞尔轨迹(弧线+抖动+加速减速)
  3. CDP 原生事件逐点执行
  4. 自然停顿(到达后停顿 100-300ms 再松手)
  5. 失败重试(轨迹随机化,不重放同一轨迹!)

重试注意:
  □ 每次重试生成新轨迹(旧轨迹可被关联检测)
  □ 重试间隔随机(不是固定 2s)

108.8 检查清单

□ 验证码类型识别
□ 图像处理(缺口/角度/目标定位)
□ 坐标校准(页面 vs 图片)
□ 轨迹生成(贝塞尔+抖动)
□ CDP 原生事件执行
□ 自然停顿
□ 重试(新轨迹+随机间隔)
□ 成功率记录(报告素材)

动手练习

  1. 用 OpenCV 实现滑块缺口定位(找练习验证码截图,标注缺口中心)。
  2. 写一个"两张图差异法"找缺口(对比有/无缺口背景)。
  3. 用 YOLO 对点选验证码做目标识别(下载预训练模型跑一次)。
  4. 组合练习:滑块 + 贝塞尔轨迹 + CDP 原生拖拽全流程(测试靶场)。

深入阅读

  • 本教材:第 106 章(验证码在 Bot Protection 中的定位)
  • 本教材:第 107 章(轨迹算法/CDP 原生事件)
  • 本教材:第 105 章(案例 1 的 CDP 拖拽实战)
  • 外部: OpenCV 文档、ultralytics YOLO、验证码攻防研究