验证码JS逆向(上)—— 前置知识与简单验证码逆向

目录

  • 一、什么是验证码?为什么需要它?
  • 二、验证码的验证流程(核心理论)
    • [2.1 客户端-服务端交互模型](#2.1 客户端-服务端交互模型)
    • [2.2 会话绑定机制](#2.2 会话绑定机制)
    • [2.3 服务端验证 vs 客户端验证](#2.3 服务端验证 vs 客户端验证)
    • [2.4 常见的安全防护机制](#2.4 常见的安全防护机制)
  • 三、验证码类型:分类与概览
    • [3.1 第一层:简单/传统型验证码](#3.1 第一层:简单/传统型验证码)
      • [3.1.1 数英验证码(字母数字混合)](#3.1.1 数英验证码(字母数字混合))
      • [3.1.2 计算型验证码](#3.1.2 计算型验证码)
    • [3.2 第二层:行为型验证码](#3.2 第二层:行为型验证码)
      • [3.2.1 滑块验证码](#3.2.1 滑块验证码)
      • [3.2.2 点选验证码](#3.2.2 点选验证码)
      • [3.2.3 旋转验证码](#3.2.3 旋转验证码)
    • [3.3 第三层:智能/无感验证码](#3.3 第三层:智能/无感验证码)
      • [3.3.1 工作原理](#3.3.1 工作原理)
      • [3.3.2 代表产品](#3.3.2 代表产品)
      • [3.3.3 Token 获取与使用](#3.3.3 Token 获取与使用)
    • [3.4 分类汇总表](#3.4 分类汇总表)
    • [3.5 国内主流验证码厂商概览](#3.5 国内主流验证码厂商概览)
  • 四、验证码识别工具与方案
    • [4.1 第三方打码平台](#4.1 第三方打码平台)
    • [4.2 大模型识别](#4.2 大模型识别)
    • [4.3 图像处理辅助](#4.3 图像处理辅助)
    • [4.4 训练自定义模型](#4.4 训练自定义模型)
  • 五、简单验证码JS逆向:通用方法论
    • [5.1 逆向流程总览](#5.1 逆向流程总览)
    • [5.2 从网络请求判断验证码类型](#5.2 从网络请求判断验证码类型)
    • [5.3 常见加密模式](#5.3 常见加密模式)
    • [5.4 处理 Base64 编码的验证码图片](#5.4 处理 Base64 编码的验证码图片)
    • [5.5 实战案例导航](#5.5 实战案例导航)
  • 六、总结与过渡
    • [6.1 本篇内容回顾](#6.1 本篇内容回顾)
    • [6.2 核心要点](#6.2 核心要点)
    • [6.3 下篇预告](#6.3 下篇预告)

本篇是验证码 JS 逆向系列的上篇,主要讲解验证码的验证原理与流程、常见验证码类型分类、识别工具介绍,以及简单验证码(数英、计算型等)的 JS 逆向通用方法论。滑块验证码、点选验证码等行为型验证码的实战将在下篇展开。

一、什么是验证码?为什么需要它?

定义CAPTCHA ,全称 C ompletely A utomated P ublic T uring test to tell C omputers and H umans A part(全自动区分计算机与人类的公开图灵测试),是一种 挑战-响应机制,用于判断当前与系统交互的是真人还是机器程序(Bot)。

该术语由卡内基梅隆大学的研究人员在 2003 年正式提出,不过利用视觉谜题区分人机的思路更早就已存在。核心理念很简单:给出一个人类容易完成、但机器难以解决的任务

验证码的作用,验证码在 Web 安全中承担着多项关键功能:

  1. 防止自动化操作:阻止脚本批量注册账号、撞库攻击、抢票等自动化行为。
  2. 反爬虫:保护网站内容和 API 不被爬虫大规模采集。当服务器检测到可疑的请求模式时,触发验证码来验证请求者是否为真人。
  3. 限流与防滥用:即使攻击者知道正确的 API 端点,验证码也会增加一道验证关卡,减缓或阻止自动化滥用。
  4. 防垃圾信息:阻止 Bot 批量提交表单、发布评论、发送消息等。

验证码技术经历了多代演进,每一代的升级都源自上一代被攻破:

复制代码
第一代: 简单文字验证码(2000年代初)
    - 带噪点背景的扭曲字母数字
    - 现代 OCR 引擎轻松击破

第二代: 增强型图片验证码(2000年代中)
    - 加入旋转、重叠、色彩渐变、干扰线等
    - 计算型验证码(如 "3 + 7 = ?" )作为变体出现
    - 仍然容易被训练过的 ML 模型破解

第三代: 行为型验证码(2010年代)
    - 滑块拼图(拖动滑块填补缺口)
    - 点选验证码(按顺序点击指定文字/图标)
    - 旋转验证码(将图片旋转至正确角度)
    - 不仅验证答案,还验证用户的交互方式(轨迹、时序)

第四代: 智能/无感验证码(2010年代末至今)
    - Google reCAPTCHA v3、hCaptcha、Cloudflare Turnstile
    - 基于风险评分,大多数情况下用户无需任何操作
    - 后台静默分析浏览器指纹、鼠标移动、点击模式等
    - 只有风险评分偏高时才会弹出显式验证

每一代验证码的诞生,都是因为上一代被攻破。OCR 越来越强,文字验证码变得不再安全;图片验证码被攻破后,行为验证随之出现。如今许多网站将第三代和第四代验证码结合使用,以获得最大的防护效果。

从 JS 逆向视角看,验证码不只是一个 "答题页面",更重要的是它和会话状态、请求参数、后续业务接口之间的绑定关系。后面分析时,关注点不能只停留在 "题目长什么样",还要看它是如何发起、如何提交、如何校验以及如何发放通过凭证的。

二、验证码的验证流程(核心理论)

理解验证码的验证流程是逆向验证码 最重要的前置知识。无论验证码类型是文字、滑块还是点选,底层的验证流程都遵循相同的基本模式。掌握了这个模式,就可以将任何验证码系统的具体实现映射到这个通用框架上进行分析。

2.1 客户端-服务端交互模型

验证码 不仅仅是一张图片 ------它是客户端(浏览器)与服务端之间的一次 有状态的交互过程。服务端必须跟踪它发送了哪个挑战、正确答案是什么、以及提交的答案是否有效。完整流程如下:

复制代码
 ┌───────────────────────────────────────────────────────────────────────┐
 │                      验证码验证流程                                    │
 │                                                                      │
 │  ┌──────────┐                                    ┌──────────┐        │
 │  │  客户端    │                                    │  服务端    │        │
 │  │ (浏览器)   │                                    │  (API)    │        │
 │  └─────┬─────┘                                    └─────┬─────┘        │
 │        │                                                │              │
 │  第1步 │  ──── 请求验证码 ──────────────────────────►   │              │
 │        │       GET /captcha/init                        │              │
 │        │                                                │              │
 │        │                              ┌─────────────────┤              │
 │        │                              │ 生成验证题目     │              │
 │        │                              │ 存储正确答案     │              │
 │        │                              │ 创建会话标识     │              │
 │        │                              └─────────────────┤              │
 │        │                                                │              │
 │  第2步 │  ◄──── 返回挑战数据 ───────────────────────── │              │
 │        │       {captcha_id, image, ...}                 │              │
 │        │                                                │              │
 │        │  ┌──────────────────┐                          │              │
 │        │  │ 用户/脚本         │                          │              │
 │        │  │ 解答验证码        │                          │              │
 │        │  └──────────────────┘                          │              │
 │        │                                                │              │
 │  第3步 │  ──── 提交答案 ────────────────────────────►   │              │
 │        │       POST /captcha/verify                     │              │
 │        │       {captcha_id, answer}                     │              │
 │        │                                                │              │
 │        │                              ┌─────────────────┤              │
 │        │                              │ 查找会话         │              │
 │        │                              │ 对比答案         │              │
 │        │                              │ 标记已使用       │              │
 │        │                              └─────────────────┤              │
 │        │                                                │              │
 │  第4步 │  ◄──── 返回验证结果 ──────────────────────── │              │
 │        │       {success: true, token: "abc123"}         │              │
 │        │                                                │              │
 │  第5步 │  ──── 携带凭证发起业务请求 ────────────────►   │              │
 │        │       POST /login {token: "abc123", ...}       │              │
 │        │                                                │              │
 │        ▼                                                ▼              │
 └───────────────────────────────────────────────────────────────────────┘

下面我们逐步拆解每个环节:

① 第1步:客户端请求验证码。客户端向服务端的验证码初始化接口发送请求。触发场景包括:

  1. 用户点击 "登录""注册" 按钮
  2. 服务端检测到可疑行为(请求过多、异常模式等)
  3. 页面加载时强制要求验证码验证。请求本身通常很简单------一个 GETPOST,参数不多。部分站点会附带 timestampnonce 防止缓存。

② 第2步:服务端返回挑战数据。服务端执行以下操作:

  1. 生成验证码题目------可能是渲染一张文字图片、创建一个带缺口的滑块拼图、或者选定若干点选目标字符。
  2. 存储正确答案 ------服务端记住答案是什么(比如文本 "A3xK"、缺口位置 x=156、需要点击的字符顺序等)。答案存储在服务端,通常以唯一的会话标识作为键。
  3. 创建会话标识 ------一个唯一 ID(通常是 UUID、token 或 challenge 字符串),将本次验证码实例与其存储的答案绑定在一起。服务端返回给客户端的数据包括:
    • 验证码挑战数据(图片链接、Base64 编码的图片、拼图碎片等)

    • 会话标识captcha_idchallengetokensession_idcheckId 等)

    • 有时还包括后续请求所需的附加参数(keysign 等)

      python 复制代码
      # 服务端响应示例
      {
          "captcha_id": "a836aaee-02fe-487d-87f2-577de6d798dc",
          "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
          "type": "text",       # 或 "slide"、"select" 等
          "expire": 120          # 该验证码的过期时间(秒)
      }

③ 第3步:客户端提交答案。用户(或我们的脚本)解出验证码后,客户端将答案连同会话标识一起提交到验证接口。不同类型验证码的答案格式不同:

验证码类型 答案格式
文字/数英 "answer": "A3xK"
计算型 "answer": "15"(8+7 的结果)
滑块 "distance": 156 + 轨迹数据
点选 "points": [[120,45], [230,180], [50,90]]

会话标识至关重要------它告诉服务端这个答案对应的是哪一次验证码。没有它,服务端就无法查找到正确答案进行比对。

④ 第4步:服务端验证并响应。服务端执行以下操作:

  1. 根据会话标识查找存储的正确答案
  2. 将提交的答案与存储的答案进行比对(滑块类型允许一定的容差)
  3. 检查验证码是否已过期或已被使用
  4. 对于行为型验证码:验证轨迹/时序数据是否符合真人操作的特征
  5. 返回验证成功或失败的结果,如果成功,还会返回一个 验证通过凭证 (也叫 validateseccodeticket 等)

⑤ 第5步:验证凭证用于后续业务请求 。验证通过凭证用于证明用户已通过验证码。后续的 "真正" 请求(登录、数据获取、表单提交等)中需要携带这个凭证。业务 API 服务端在处理请求前会先校验该凭证。这也是为什么仅仅绕过验证码 UI 是不够的------你还需要服务端签发的凭证才能继续后续操作。

2.2 会话绑定机制

会话标识 (常见的叫法有 captcha_idchallengetokenverify_idcheckId 等)是整个验证流程的核心纽带。它的作用:

需要注意的是,会话信息不一定总是直接以 captcha_id 这类字段出现在响应体中;很多站点会在响应体里返回 captcha_iduuidtoken 等验证码实例标识,同时通过 Set-Cookie 下发 sessionId 等会话标识,后续请求再由浏览器自动携带,甚至还会要求把这些字段与自定义 Header 里的参数组合起来共同完成校验。因此,逆向时不能只看响应 JSON,还要把响应体、响应头、请求头和 Cookie 放在同一条链路里一起分析。

  1. 将挑战与答案绑定 :服务端存储 {captcha_id: "abc" → answer: "A3xK"}。当客户端提交 {captcha_id: "abc", answer: "A3xK"} 时,服务端就能查找并验证。
  2. 防止重放攻击 :每个 captcha_id 只能使用一次。一旦验证过(或过期),就不能再次使用。
  3. 防止跨会话混淆:如果没有唯一 ID,服务端就无法判断哪张验证码图片对应哪个答案------尤其是有多个用户同时在做验证的时候。

常见的会话标识格式:

复制代码
UUID:       a836aaee-02fe-487d-87f2-577de6d798dc
Token:      f19fec2a83cbf7933bd498e02222cc7b
Challenge:  c21a2d1b7bf9ad4be7098f7b79147eac
自定义:      captcha_20231029_8a3f2b

2.3 服务端验证 vs 客户端验证

设计良好的验证码 一定是在服务端进行验证的。但是,有些实现较差的验证码会在客户端进行验证------意味着正确答案就嵌在页面的 JavaScript 中,或者直接在 API 响应中返回了。这是严重的安全缺陷,使得验证码形同虚设。

如何判断 :仔细检查验证码初始化接口的响应数据。如果你在响应中看到 answerresultcorrectPosition 之类的字段,或者返回了可疑的详细坐标数据,那么这个验证码很可能是客户端验证。这种情况下,你甚至不需要 OCR------直接从响应中解析答案即可。

python 复制代码
# 实现较差的验证码示例(客户端验证)
# 服务端响应中直接包含了答案!
{
    "captcha_id": "xxx",
    "image": "base64...",
    "answer": "A3xK"           # 答案就在这里
}

# 实现良好的验证码示例(服务端验证)
# 服务端响应中不包含答案
{
    "captcha_id": "xxx",
    "image": "base64..."
    # 没有 answer 字段------验证在服务端完成
}

2.4 常见的安全防护机制

除了基本的挑战-响应模式外,验证码系统还采用了多种安全措施:

  1. 过期机制:每个验证码都有有效期(TTL),通常为 60~180 秒。过期后无论答案是否正确都会验证失败。
  2. 一次性使用 :每个 captcha_id 只能验证一次。即使提交了正确答案,第二次使用同一个 captcha_id 也会失败。
  3. 请求签名 :部分验证码要求附带 sign 参数------用请求参数计算出的哈希值(通常是 MD5 或 HMAC)。这可以防止参数被篡改。
  4. 加密传输:提交的答案可能经过加密(AES、RSA 等)后再发送,服务端收到后先解密再验证。
  5. 行为分析:对于滑块/点选验证码,服务端不仅检查最终位置,还会分析整个操作轨迹(时间间隔、加速度、抖动)来判断是否为真人操作。
  6. IP/指纹绑定captcha_id 可能绑定了客户端的 IP 地址或浏览器指纹。从一个 IP 解出验证码,在另一个 IP 使用凭证,会导致验证失败。

这些机制说明,验证码验证并不是单次提交答案这么简单。服务端校验的往往是一整条状态链路:挑战是否存在、会话是否一致、是否过期、是否已使用、请求参数是否完整、签名或加密结果是否正确,以及验证通过后的凭证是否被正确带入后续业务接口。

三、验证码类型:分类与概览

按交互方式和校验逻辑,验证码从简单到复杂可以分为三层。判断一个验证码属于哪一层,最直接的方式是看它让你做什么操作、最终提交的答案长什么样。

3.1 第一层:简单/传统型验证码

第一层验证码的核心特征是 答案直接印在图片里 ------只要认出图中的字符或算出算式的结果,就能得到正确答案。这类验证码只校验答案本身,不涉及鼠标轨迹或行为分析,逆向链路最单纯:看清题目 → 得出答案 → 提交结果

这一层的逆向链路最单纯:看清题目 → 得出答案 → 提交结果,不涉及轨迹分析和行为模拟。

3.1.1 数英验证码(字母数字混合)

最经典的验证码类型:一张包含扭曲字母和/或数字的图片,用户识别后输入其中的字符。

实际中可能遇到的数英验证码外观差异很大,从简单的噪点背景到严重的字符扭曲粘连都可能出现:

典型特征

  1. 随机 4~6 位字符(纯数字、纯字母、字母数字混合),部分验证码 区分大小写
  2. 视觉干扰:旋转、缩放、扭曲、重叠、空心字体、透叠
  3. 噪点元素:背景杂点、交叉干扰线、颜色渐变、网格底纹
  4. 常见变种:字符带边框、阴影、空心描边(增加二值化预处理难度)

逆向时的答案格式 :提交的通常是一个字符串,如 "answer": "A3xK"。注意部分网站前端会自动 toUpperCase(),但服务端可能区分大小写------应以实际测试为准。

逆向切入点 :简单背景的可以用 OCR 库(ddddocr 等)试试手,但实测稍微带噪点或干扰线准确率就断崖式下降。实际项目中建议直接走打码平台或大模型识别,稳定性和效率都更高。具体方案对比见第四节。

3.1.2 计算型验证码

用户不需要识别具体字符,而是解答一道渲染在图片中的算术题。计算型验证码示例:

典型特征

  1. 简单四则运算:加、减、乘,除法较少见
  2. 以图片形式渲染,通常带有轻微扭曲或噪点
  3. 答案是 数字(可能为负数),不是字符串
  4. 常见变种:
    • 运算符用中文表示(如 "叁加柒""3加7"
    • 多步运算(如 "5 - 4 + 2 = ?"
    • 运算符和数字分离,需要先识别再拼接

逆向时的答案格式 :提交的是计算结果,如 "answer": "15""captcha": "-2"

逆向切入点 :用 OCR 识别算式 → 正则提取操作数和运算符 → eval() 或手动计算。如果算式以 HTML 文本渲染而非图片,直接从 DOM 取值即可,不需要 OCR。

3.2 第二层:行为型验证码

第二层与第一层的区别,表面上是从 "打字输入" 变成了 "拖拽/点击/旋转",但 核心差异在服务端的校验逻辑 :第一层只验答案对不对,第二层还会验你得出答案的 过程数据(操作轨迹、时间间隔、加速度变化)是否像真人。

这意味着逆向时你至少需要做两件事:① 算出正确答案,② 生成一套 "看起来像人" 的行为数据。

3.2.1 滑块验证码

目前国内最常见的验证码类型。用户拖动一个悬浮的滑块,使拼图碎片对准背景图中的缺口位置。

滑块验证码示例 --- 极验风格,背景图 + 缺口 + 悬浮滑块

滑块验证码示例 --- 网易易盾(外观相似,差异在接口加密和轨迹校验策略)

典型特征

  1. 验证码接口通常返回两张图:带缺口的 背景图 + 滑块碎片图
  2. 服务端验证两个核心参数:滑动距离 (缺口位置的 X 轴偏移量)和 滑动轨迹(鼠标/触摸移动过程的完整记录)
  3. 距离有一定的容差范围(通常是 ±3~5px),但轨迹的校验远更严格
  4. 常见变种:单滑块(最常见的水平滑块)、旋转滑块(网易易盾特有,滑块本身需要旋转到正确角度后才能滑动)、刮刮卡式滑块(以刮开涂层的交互形式)

轨迹校验到底验什么? 这是第二层和第一层最关键的技术差异。服务端拿到轨迹数组后,通常会检查以下维度:

校验维度 机器特征 真人特征
速度变化 匀速或接近匀速,速度曲线平滑 有快有慢,存在明显的加速段和减速段
停顿点 无停顿,或停顿间隔均匀(像 timer) 可能在中途有短暂停顿(犹豫、调整),时机随机
Y 轴抖动 Y 轴无波动或波动极小 手指/鼠标在水平滑动时 Y 轴有微小无规律的偏移
加速度 加速度曲线规律(匀速→突然减速→停) 加速度曲线不规则,存在微小的加速尖峰和减速谷底
起始响应 从滑块出现到开始拖动的延迟过于固定 延迟有随机性(人在看到滑块后需要反应时间)

一个简化的轨迹示例(Python 模拟):

python 复制代码
# 一条简化的真人轨迹示例(示意图,非完整代码)
# 轨迹数组格式: [[x, y, time_ms], [x, y, time_ms], ...]
track = [
    [-37, -19, 0],    # 起始位置(相对滑块中心)
    [-35, -18, 80],   # 开始缓慢移动
    [-30, -17, 160],
    [-20, -16, 240],  # 加速段
    [0, -15, 320],
    [20, -15, 400],   # 匀速段
    [50, -14, 480],
    [100, -14, 560],
    [140, -13, 640],  # 接近目标,开始减速
    [155, -13, 720],
    [158, -12, 800],  # 微调阶段(超过一点再拉回来,真人常见)
    [156, -12, 880],  # 最终停在目标位置
]

不同厂商的轨迹校验强度差异很大。极验的轨迹校验非常严格(加密+多维分析),一些自研滑块可能只看距离不看轨迹。

逆向时的答案格式

python 复制代码
# 典型的滑块验证提交参数
{
    "captcha_id": "xxx",
    "distance": 156,          # 滑动距离(像素)
    "track": [[x, y, t], ...] 
    # 部分厂商还会要求 sign / encrypt 等加密参数
}


json_data = {
    'queryData': {
        'entryId': '',
        'billNo': 'HSK0218741A',
        'trspmcName': '',
        'voyageNo': '',
        'transferMode': '1',
    },
    'captchaData': {
        'id': '6a27ff0f1aac4fd5a37265fdad4fda0d',
        'ogImageWidth': 270,
        'ogImageHeight': 165,
        'sliderImageWidth': 50,
        'sliderImageHeight': 165,
        'startSlidingTime': '2026-06-25 06:24:44',
        'endSlidingTime': '2026-06-25 06:24:55',
        'trackList': [ # 轨迹数据
            {
                'x': 0,
                'y': 0,
                'type': 'down',
                't': 9772,
            },
            {
                'x': 1,
                'y': 0,
                'type': 'move',
                't': 9808,
            },
            # .... 省略
            {
                'x': 171,
                'y': -1,
                'type': 'up',
                't': 10863,
            },
        ],
    },
}

逆向切入点 :① 用 OpenCV 模板匹配或 ddddocr.slide_match() 找到缺口位置,计算滑动距离;② 用算法生成一条带有加速段、匀速段、减速段、微调段和 Y 轴噪声的轨迹数组;③ 如果轨迹数据经过加密(极验等),需要逆向加密逻辑。

3.2.2 点选验证码

用户需要按提示顺序,在图片中依次点击指定目标。提示可能是文字指令,也可能是图标示意。

点选验证码示例 --- 文字点选,如 "请依次点击:侈 斥 舱"

点选验证码示例 --- 图标点选

典型特征

  1. 背景图中散布着多个候选目标(通常 6~12 个汉字或图标)
  2. 提示栏给出需要依次点击的目标
  3. 服务端验证点击坐标是否落在对应目标的范围内(有容差),以及 点击顺序是否正确
  4. 部分点选验证码还会记录每次点击的时间间隔,用于行为分析

三种常见变体

变体 判断方式 典型场景
文字点选 按顺序点击图片中的指定汉字 国内政务网站、网易易盾、极验
图标点选 点击指定图标/物体 登录保护、敏感操作二次验证
语义点选 点击所有包含红绿灯的图片 Google reCAPTCHA v2 图片选择

逆向时的答案格式

python 复制代码
# 点选验证码提交参数示例
{
    "captcha_id": "xxx",
    "points": [[120, 45], [230, 180], [50, 90]],  # 每个目标的点击坐标
    "click_order": [2, 5, 1]                      # 或目标序号
}

逆向切入点:① 用目标检测模型(YOLO、CNN 分类器)定位每个候选目标的位置;② 匹配提示文字与目标位置,确定点击坐标和顺序;③ 对于图标/语义点选,通常需要更复杂的视觉模型或多模态大模型来理解图像内容。

3.2.3 旋转验证码

用户需要将一张被故意旋转的图片(或图片的一部分)拖回正确的朝向。

旋转验证码示例 --- 一张被随机旋转的图片,用户拖动旋转到正确角度

典型特征

  1. 图片被随机旋转某个角度(通常 30°~330° 之间,步长随机)
  2. 用户通过拖拽滑块或直接旋转图片来调整角度
  3. 服务端验证最终角度是否在容差范围内(通常 ±5°~10°)
  4. 部分实现会同时记录旋转过程的角速度变化(轨迹分析的一种变体)

逆向时的答案格式

python 复制代码
{
    "captcha_id": "xxx",
    "angle": 45,              # 需要旋转的角度
    "direction": "clockwise"  # 旋转方向(部分厂商)
}

逆向切入点 :用图像处理判断图片的 "朝向正确性"。常见思路:① 训练一个 CNN 模型判断图片是否摆正;② 利用图片中文字的 OCR 置信度来判断(文字越正 → 置信度越高 → 角度越接近正确);③ 利用图片的 Hough 直线检测判断水平线方向。

3.3 第三层:智能/无感验证码

第三层验证码的设计理念与前两层完全不同:不先出题,先判断你值不值得出题。它的核心任务是在后台静默采集浏览器环境和用户行为数据,对当前会话给出一个风险评分,只有当风险偏高时才弹出显式验证。

这一层的逆向难点不再集中在 "识别图片""模拟轨迹",而是在 浏览器指纹模拟、环境一致性维护和 token 生成链路的还原 上。

3.3.1 工作原理

复制代码
用户访问页面
        │
        ▼
┌─────────────────────┐
│  JS SDK 静默加载     │  ← 加载验证码厂商的 JS SDK(如极验 gt.js)
│  采集以下数据:        │
│  - 浏览器指纹        │
│  - 鼠标/触摸行为     │
│  - 滚轮/按键事件     │
│  - 页面停留时间      │
│  - 设备/屏幕信息     │
│  - 网络/TLS 特征     │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│  计算风险评分        │  ← 0.0(肯定是机器人) ~ 1.0(肯定是真人)
│  (通常在厂商服务端   │
│   完成,不在前端)    │
└─────────┬───────────┘
          │
    ┌─────┴─────┐
    │           │
    ▼           ▼
 评分高        评分低
    │           │
    ▼           ▼
 放行/发token  弹出显式验证
              (可能是滑块、
               点选等降级方案)

3.3.2 代表产品

国际厂商

厂商 核心产品 Token 名称 特点
Google reCAPTCHA v3 无感评分 g-recaptcha-response 每月100万次免费,返回 0.0~1.0 分数
hCaptcha 显式验证为主,支持无感模式 h-captcha-response reCAPTCHA 替代品,注重数据隐私
Cloudflare Turnstile 无感 + 自动挑战 cf-turnstile-response Cloudflare 生态,免费额度慷慨

国内厂商(3.5 节会展开介绍):

厂商 主要验证码类型
极验 GeeTest 滑块(三代)、点选、无感(四代)
网易易盾 滑块、点选、文字、无感
腾讯天御 滑块、无感、风险评分
阿里云验证码 滑块、无感
顶象 滑块、点选、无感、设备指纹
数美 无感/风控(偏向设备指纹和黑产识别)

3.3.3 Token 获取与使用

第三层验证码的 关键产物 是一个 验证 token------在 JS SDK 完成风险判断后,前端通过回调拿到这个 token,然后在业务请求中携带它。服务端收到业务请求后,会用这个 token 向验证码厂商的后台发起一次校验(verify),确认 token 有效且 risk 评分在可接受范围内。

python 复制代码
# reCAPTCHA v3 的典型使用方式
# 前端(JS SDK 自动注入):
grecaptcha.execute("site_key", {action: "login"}).then(token => {
    // 把 token 塞到表单里一起提交
    document.getElementById("recaptcha-token").value = token;
});

# 后端(业务服务器校验):
POST https://www.google.com/recaptcha/api/siteverify
{
    "secret": "your_secret_key",
    "response": "token_from_frontend"
}
# 响应:
{
    "success": true,
    "score": 0.9,       # 风险评分
    "action": "login"
}

不同厂商的 token 名称(逆向时定位接口的关键线索):

  1. g-recaptcha-response --- Google reCAPTCHA
  2. h-captcha-response --- hCaptcha
  3. cf-turnstile-response --- Cloudflare Turnstile
  4. geetest_challenge / geetest_validate / geetest_seccode --- 极验
  5. NECaptchaValidate --- 网易易盾

逆向思路:第三层验证码最难自动化绕过,通常有以下几种路线:

  1. 浏览器自动化 + 反检测:用 Playwright/Selenium 配合 stealth.js 等反检测补丁,在真实浏览器中触发验证并获取 token。适合 token 有效期长、同一 token 可多次使用的场景。
  2. 第三方打码服务:2Captcha、CapSolver 等平台支持自动获取 reCAPTCHA/hCaptcha token,按次付费。
  3. 逆向 JS SDK:分析 SDK 的 token 生成逻辑和环境检测点,补环境后直接调用------这是难度最高的路线,只在确实需要全自动化且 token 消耗量大的场景下才值得投入。

3.4 分类汇总表

层级 类型 难度 逆向核心挑战 提交的答案 主要工具/方案
第一层 数英验证码 字符识别 "A3xK" 打码平台 / 大模型
第一层 计算型验证码 算式识别 + 计算 "15" 打码平台 / 大模型 + eval
第二层 滑块验证码 缺口定位 + 轨迹模拟 + 加密 distance=156 + track=[...] 打码平台 / 大模型 + 轨迹算法
第二层 点选验证码 中~高 目标检测 + 坐标映射 + 顺序 [[x1,y1],[x2,y2],[x3,y3]] 打码平台 / 大模型
第二层 旋转验证码 角度检测 angle=45 打码平台 / 大模型
第三层 无感验证码(各类厂商) 浏览器指纹 + 环境 + 行为 g-recaptcha-response 等 token 浏览器自动化 / 打码服务

3.5 国内主流验证码厂商概览

国内的验证码市场经过多年竞争,已经形成了相对清晰的梯队格局。了解这些厂商的产品形态,可以让你在遇到一个验证码时快速判断这是自研的还是接入了哪家厂商,从而决定逆向策略------接入第三方厂商的验证码,通常意味着你需要面对更规范的接口协议、更完善的加密体系和更严格的行为校验

厂商 官网 产品线 技术特点 逆向难度
极验 GeeTest geetest.com 三代(滑块+点选)、四代(无感)、OnePass(一键登录) 国内滑块验证码的行业标准,轨迹加密和校验非常严格,参数涉及多层 AES + RSA 加密 ★★★★☆
网易易盾 dun.163.com 文字验证码、滑块、点选、无感 产品线覆盖全面,政务和教育类网站中出现频率较高 ★★★★☆
腾讯天御 cloud.tencent.com 滑块、无感、VTT(动态语义验证)、TDC(设备指纹) 依托腾讯安全大数据,风险判断模型强,滑块精度要求相对宽松 ★★★☆☆
阿里云验证码 help.aliyun.com/product/28310 滑块、无感、智能验证 阿里云生态集成度高,接口规范,SDK 文档完善 ★★★☆☆
顶象 dingxiang-inc.com 滑块、点选、无感、设备指纹(X5S)、风控决策引擎 偏向金融和政务行业,设备指纹(JS 采集上百维度的浏览器特征)是其核心技术壁垒 ★★★★☆
数美 ishumei.com 无感验证(天网)、设备指纹(SM-Detect) 不强调出题,以黑产设备识别和风险评分为核心,字节跳动等大型互联网公司是其重要客户 ★★★★★
瑞数 riversecurity.com 动态 Cookie 验证(非传统验证码)、设备指纹 本质上是动态安全而非验证码------通过动态变换的 Cookie 和前端挑战脚本区分人机。逆向难度极高,通常需要补环境或 iv8 通杀方案 ★★★★★
Vaptcha(手势验证) vaptcha.com 手势轨迹验证(在图上画一个指定形状) 交互形式独特,在一段时间内较流行,现在出现频率下降 ★★★☆☆

如何快速判断当前网站用的是哪家厂商:打开 F12 → Network 面板,看验证码相关请求的域名和参数关键词:

厂商 域名/URL 关键词 请求参数关键词
极验 geetestgt.jsapi.geetest.com gtchallengevalidateseccode
网易易盾 c.dun.163.comnecaptcha NECaptchaValidatecaptchaId
腾讯天御 captcha.qq.comtcaptcha aidticketrandstr
阿里云 cf.aliyun.comaliyun-captcha sessionidsigtoken
顶象 dingxiang-inc.comdxcaptcha tokenconstId
数美 ishumei.comfp.js deviceIdriskLevel
Cloudflare challenges.cloudflare.com cf-turnstile-response
reCAPTCHA recaptchagoogle.com/recaptcha g-recaptcha-response

当你拿到一个站点后,先走完第二节的五步流程(定位初始化接口 → 分析请求参数 → 识别会话标识),再对照上面的域名和关键词快速锁定厂商,然后就能有针对性地查找该厂商的逆向资料和已知方案。

四、验证码识别工具与方案

OCR 库(ddddocr、Tesseract、PaddleOCR)上手简单,但实战中一旦验证码带噪点、干扰线或字符粘连,识别率就会断崖式下降。目前最可靠的两条路径是 第三方打码平台大模型识别 ------前者省心、准确率高,后者灵活、可控性强。本节以这两者为主,最后简要提及图像处理辅助和自训练模型的方向。

4.1 第三方打码平台

打码平台背后通常结合了训练好的 AI 模型和人工审核,准确率能做到 95% 以上,是处理日常验证码最省心的方案。工作流程很简单:上传图片 → 平台识别 → 返回结果,按次付费,通常几分钱一次。

常见平台:

平台 网址 特点
超级鹰(chaojiying) chaojiying.com 国内老牌,API 稳定,类型全
图鉴(ttshitu) ttshitu.com 速度快,支持滑块坐标返回
云码(jfbym) jfbym.com 类型码丰富,支持人工打码兜底
2Captcha 2captcha.com 国际平台,支持 reCAPTCHA/hCaptcha

以超级鹰为例,完整调用代码:

python 复制代码
import requests
from hashlib import md5


class Chaojiying_Client:
    def __init__(self, username, password, soft_id):
        self.username = username
        self.password = md5(password.encode('utf8')).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型,参考 https://www.chaojiying.com/price.html
        """
        params = {'codetype': codetype}
        params.update(self.base_params)
        files = {'userfile': ('captcha.jpg', im)}
        r = requests.post(
            'http://upload.chaojiying.net/Upload/Processing.php',
            data=params, files=files, headers=self.headers
        )
        return r.json()


# 使用示例
# 注意还要将你的ip地址放入到白名单中
# 软件ID的生成: 超级鹰首页>用户中心>软件ID
chaojiying = Chaojiying_Client('账号', '密码', '软件ID')

with open('test.png', 'rb') as f:
    image_bytes = f.read()
result = chaojiying.PostPic(image_bytes, 1902)
print(result)
print(result['pic_str'])  # 识别结果

with open('test2.png', 'rb') as f:
    image_bytes = f.read()
result = chaojiying.PostPic(image_bytes, 1902)
print(result)
print(result['pic_str'])  # 识别结果

实测结果:

4.2 大模型识别

多模态大模型(Claude、GPT-4o 等)对验证码识别有天然优势------它们在训练时就处理过大量文字图片,扭曲、噪点、粘连这些对它们来说几乎不是问题。实测对 SpiderDemo 的数英验证码,Claude Sonnet 每次都能稳定返回 4 位结果,远超 ddddocr 的表现。

使用方式通常是通过中转站(API 代理)调用,兼容 OpenAI 接口格式,换任何中转站或模型只改三个变量:

python 复制代码
# -*- coding: utf-8 -*-
"""
@File    : claude_sonnet.py
@Author  : XAMO Lab
@Date    : 2026/6/25 15:04
@Blog    : https://blog.csdn.net/xw1680
@Tool    : PyCharm
@Desc    : 
"""
import time

import requests
import base64
import json

# ========== 配置区(换服务商只改这里) ==========

# 方式一: OpenAI 官方(推荐,稳定)
# API_KEY  = "sk-你的OpenAI密钥"
# BASE_URL = "https://api.openai.com"
# MODEL    = "gpt-4o"

# 方式二:中转站(模型选择多,选支持视觉的模型即可)
# 必须选支持图片输入(vision)的模型,DeepSeek 等纯文本模型不可用
API_KEY = "sk-你的中转站密钥"
BASE_URL = "https://api.你的中转站.com"
MODEL = "claude-sonnet-4-6"  # 或 claude-sonnet-4-5 等,确保支持图片


# 方式三:Anthropic 官方(需用 anthropic SDK,本脚本不适用)

# 常见中转站的配置示例:
# ┌────────────┬──────────────────────────────┬───────────────────────┐
# │ 中转站      │ BASE_URL                     │ 常见模型名             │
# ├────────────┼──────────────────────────────┼───────────────────────┤
# │ 某中转A     │ https://api.xxx.site         │ claude-sonnet-4-6     │
# │ 某中转B     │ https://api.yyy.com          │ claude-3-5-sonnet     │
# │ 某中转C     │ https://api.zzz.top          │ gpt-4o                │
# │ OpenAI官方  │ https://api.openai.com       │ gpt-4o                │
# │ Anthropic  │ https://api.anthropic.com     │ (需用Anthropic SDK)    │
# └────────────┴──────────────────────────────┴───────────────────────┘


def recognize_by_llm(image_bytes):
    """
    用大模型识别验证码
    - 通过中转站调用,兼容 OpenAI 接口格式
    - 自动处理流式/非流式响应
    """
    b64_image = base64.standard_b64encode(image_bytes).decode("utf-8")

    resp = requests.post(
        f"{BASE_URL}/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "model": MODEL,
            "max_tokens": 20,
            "stream": False,
            "messages": [{
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/png;base64,{b64_image}"}
                    },
                    {
                        "type": "text",
                        # {"type": "text", "text": prompt}      # ← 提示词通过这里传入
                        "text": "这是一张4位英文数字验证码图片,直接返回4个字符,不要解释。"
                        # "text": "这是一张计算题验证码图片,包含两个三位数的加减乘运算(如 123+456、200-150、111*222)。"
                        #        "请按以下格式回答:\n算式:xxx\n答案:xxx\n"
                        #        "注意:1.仔细辨认每个数字,不要看错 2.减法结果可能是负数 3.只输出上面的格式,不要多余文字"
                    }
                ]
            }]
        },
        timeout=30
    )

    # 有些中转站会强制返回流式(SSE)响应,需要手动解析
    if resp.text.startswith("data: "):
        # 流式响应:逐行解析 SSE
        text = ""
        for line in resp.text.strip().split("\n"):
            line = line.strip()
            if line.startswith("data: ") and line != "data: [DONE]":
                try:
                    chunk = json.loads(line[6:])
                    choices = chunk.get("choices", [])
                    if choices:
                        content = choices[0].get("delta", {}).get("content", "")
                        if content:
                            text += content
                except json.JSONDecodeError:
                    pass
        result = text.strip()
    else:
        # 标准 JSON 响应
        data = resp.json()
        print(data)
        result = data["choices"][0]["message"]["content"].strip()

    # 清理:有些模型会返回带反引号的结果如 `xocA`
    result = result.strip("`").strip("'").strip('"').strip()

    # 如果返回了 "算式:xxx\n答案:xxx" 格式,提取答案部分
    # import re
    # match = re.search(r'答案[::]\s*(-?\d+)', result)
    # if match:
    #     result = match.group(1)
    # else:
    #     # 兜底:尝试提取最后一个数字(可能是负数)
    #     nums = re.findall(r'-?\d+', result)
    #     if nums:
    #         result = nums[-1]

    return result


if __name__ == '__main__':
    # ========== 使用示例 ==========
    with open("./realworld/test2.png", "rb") as f:
        result = recognize_by_llm(f.read())
        print(f"大模型识别: {result}")  # "8KfD"
    time.sleep(2)
    with open("./realworld/test.png", "rb") as f:
        result = recognize_by_llm(f.read())
        print(f"大模型识别: {result}")  # "FyFU"

实测结果:

Prompt 技巧:明确告诉模型验证码的位数、字符集和格式要求,可以大幅提升返回规范性:

python 复制代码
# 数英验证码
prompt = "这是一张4位字母数字验证码图片,直接返回4个字符,不要解释。"

# 计算型验证码
prompt = "这是一张计算题验证码图片,包含简单的加减乘运算。先识别算式再计算结果,只返回最终数字(可能是负数),不要解释。"

注意:中转站的响应速度不稳定,部分线路可能较慢(5~15 秒甚至超时),但识别准确率通常不错。如果追求稳定和效率,打码平台是更务实的选择------下面是对比:

打码平台 vs 大模型怎么选

维度 打码平台 大模型
准确率 95%+(含人工兜底) 80%~95%(取决于模型)
速度 1~3 秒,稳定 快则 2 秒,慢则超时
成本 ~1 分/次 15 分/次
接入门槛 需注册充值 有中转站 API 即可
适用场景 大批量、要求高准确率 小批量、追求灵活性

个人经验:实际项目中我更倾向打码平台------稳定省心,不用担心中转站挂掉或限速。大模型识别适合快速验证、小批量场景,或者手头没有打码平台账号时的备选方案。

4.3 图像处理辅助

无论用打码平台还是大模型,有时需要先对验证码图片做预处理,或从接口返回的特殊格式中提取图片:

python 复制代码
import base64
from io import BytesIO
from PIL import Image

# 1. 解码 Base64 图片(最常见场景)
b64_str = response.json()["image"]
if b64_str.startswith("data:image"):
    b64_str = b64_str.split(",")[1]
image_bytes = base64.b64decode(b64_str)

# 2. 拼接切碎的分片(如 SpiderDemo 案例2 的 50 条竖条)
pieces = response.json()["data"]  # base64 字符串数组
images = [Image.open(BytesIO(base64.b64decode(p))) for p in pieces]
stitched = Image.new('RGB', (sum(i.width for i in images), images[0].height))
x = 0
for img in images:
    stitched.paste(img, (x, 0))
    x += img.width
buf = BytesIO()
stitched.save(buf, format='PNG')
image_bytes = buf.getvalue()

实际逆向中,滑块缺口距离、点选坐标等参数通常直接交给打码平台或大模型返回,大多数场景不需要自己写图像处理逻辑。

4.4 训练自定义模型

对于同一网站需要长期反复破解、且验证码样式固定的场景,收集样本训练自己的 CNN / YOLO 模型是一劳永逸的方案。训练完成后推理速度极快,无需调用外部 API。但前期投入较大------需要收集数千张标注样本、搭建训练环境、调参验证。

这部分属于进阶专题,后续会单独写一篇验证码模型训练的实践文章。

五、简单验证码JS逆向:通用方法论

有了前面验证流程和工具的基础,这里把简单验证码的逆向过程总结为一张可复用的流程图。每一步的核心关注点已经在第二节详述,此处不再展开------实际分析时按这个顺序走,不容易漏关键环节。

5.1 逆向流程总览

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                验证码逆向工程流程                                      │
│                                                                      │
│  第1步: 识别与观察                                                    │
│  ├── 在浏览器中触发验证码                                             │
│  ├── 打开 F12 → Network 选项卡                                       │
│  ├── 判断验证码类型(文字?滑块?点选?)                                │
│  └── 观察验证码的视觉复杂程度                                         │
│                                                                      │
│  第2步: 定位接口                                                      │
│  ├── 清空 Network 面板,重新触发验证码                                 │
│  ├── 找到初始化接口(返回图片/挑战数据的接口)                         │
│  ├── 手动完成验证,找到验证接口                                       │
│  └── 检查是否存在辅助/中间接口                                        │
│                                                                      │
│  第3步: 分析请求与响应                                                │
│  ├── 对比多次请求: 哪些参数是动态变化的?                              │
│  ├── 识别会话标识(captcha_id、token、challenge 等)                   │
│  ├── 检查答案是否在响应中(是否为客户端验证?)                         │
│  └── 检查请求头中是否有特殊字段                                       │
│                                                                      │
│  第4步: 逆向动态参数                                                  │
│  ├── 设置 XHR 断点,回溯调用栈定位参数生成代码                         │
│  ├── 识别加密/哈希算法(MD5、AES、RSA 等)                             │
│  └── 提取并改写 JS 逻辑(扣代码 / PyExecJS / Python 复现)             │
│                                                                      │
│  第5步: 解题                                                          │
│  ├── 下载/解码验证码图片                                              │
│  ├── 交给打码平台或大模型识别答案                                     │
│  └── 对于计算型验证码: 解析算式并计算结果                              │
│                                                                      │
│  第6步: 提交验证                                                      │
│  ├── 构造包含所有参数的验证请求                                       │
│  ├── 提交并检查响应结果                                               │
│  ├── 从成功响应中提取验证通过凭证                                     │
│  └── 在后续业务请求中携带该凭证                                       │
│                                                                      │
│  第7步: 迭代与加固                                                    │
│  ├── 多次测试,检查准确率                                             │
│  ├── 处理边界情况(识别错误、会话过期等)                               │
│  └── 添加重试逻辑,提升成功率                                         │
└──────────────────────────────────────────────────────────────────────┘

5.2 从网络请求判断验证码类型

首次遇到一个验证码时,网络请求中的线索可以帮助你快速判断类型:

响应中的线索 可能的验证码类型
返回单张图片链接或 Base64 图片,无滑块/背景分离 文字 / 计算型验证码
返回两张图片(背景图 + 滑块图) 滑块验证码
返回图片且带有 pointX/pointY 或坐标数据 点选验证码
返回 type: "slide"type: "select" 等显式声明 接口直接标明了类型
从极验(GeeTest)/ hCaptcha 域名返回 challenge 第三方验证码供应商

5.3 常见加密模式

在分析验证码的验证请求时,你会频繁遇到以下几种加密模式:

模式一:明文传输(无加密)。最简单的情况------答案直接作为明文发送:

python 复制代码
data = {
    "captcha_id": "xxx",
    "answer": "A3xK"
}
requests.post("/verify", json=data)

模式二:MD5 签名 。请求中附加了 sign 参数,由多个参数拼接后做 MD5 哈希生成:

python 复制代码
import hashlib

params = f"{captcha_id}{answer}{timestamp}"
sign = hashlib.md5(params.encode()).hexdigest()
data = {
    "captcha_id": "xxx",
    "answer": "A3xK",
    "timestamp": 1761719202955,
    "sign": sign
}

模式三:AES 加密。整个请求体经过 AES 加密后发送:

python 复制代码
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64, json

key = b"16-byte-aes-key!"
plaintext = json.dumps({"answer": "A3xK"}).encode()
cipher = AES.new(key, AES.MODE_ECB)
encrypted = base64.b64encode(cipher.encrypt(pad(plaintext, 16))).decode()
requests.post("/verify", data=encrypted)

模式四:RSA 加密。使用服务端的公钥对答案进行 RSA 加密(公钥通常在初始化接口的响应中提供):

python 复制代码
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

public_key = RSA.import_key(base64.b64decode(key_from_server))
cipher = PKCS1_v1_5.new(public_key)
encrypted = base64.b64encode(cipher.encrypt(b"A3xK")).decode()

模式五:自定义/混淆 JS。加密逻辑使用严重混淆的 JavaScript 实现。这种情况下,通常更省事的做法是直接提取整个函数,用 PyExecJS / Node.js 来执行,填入所需参数即可,而不是把混淆代码完全逆向重写成 Python。

5.4 处理 Base64 编码的验证码图片

许多验证码系统以 Base64 编码的字符串(而非 URL 链接)返回图片。处理方式如下:

python 复制代码
import base64

# 场景: 从 API 响应中拿到 Base64 字符串
b64_image = response.json()["image"]

# 如果带有 data URI 前缀,先去掉
if b64_image.startswith("data:image"):
    b64_image = b64_image.split(",")[1]

# 解码为字节
image_bytes = base64.b64decode(b64_image)

# 直接传给打码平台或大模型识别
# result = chaojiying.PostPic(image_bytes, 1902)
# 或
# result = recognize_by_llm(image_bytes)

# 如需调试,保存到本地查看
with open("captcha.png", "wb") as f:
    f.write(image_bytes)

5.5 实战案例导航

以上方法论在以下四个案例中逐一落地,建议按顺序阅读:

  1. 数英验证码 --- 图片验证码识别、Session/Cookie 绑定、打码平台/大模型/暴力重试四种方案对比
  2. 计算型验证码 --- 图片竖条拼接还原、算式 OCR 识别与自动计算
  3. 动画 GIF 验证码 --- 真假图片判断、GIF 帧提取(duration=300ms 特征)
  4. 中大网校登录 --- RSA 密码加密逆向、Cookie/Session 陷阱、execjs vs Python 复现

六、总结与过渡

6.1 本篇内容回顾

本篇围绕验证码逆向的前置知识和方法论,建立了从理论到工具的完整框架:

  1. 验证码是什么 以及为什么需要它------理解问题域
  2. 验证流程------所有验证码都遵循的 5 步客户端-服务端交互模型。这是最重要的概念:一旦理解了会话绑定、挑战-响应机制和服务端验证,所有验证码都只是同一模式的变体
  3. 验证码分类------从文字/计算型(第一层)到滑块/点选/旋转(第二层)再到无感验证码(第三层),按复杂度递进,附国内主流厂商对照表
  4. 识别方案------打码平台(稳定可靠)和大模型识别(灵活方便)两种主力方案,以及图像处理辅助和自训练模型的后续方向
  5. 通用方法论------从定位接口到处理加密的 7 步逆向流程 + 常见加密模式速查

6.2 核心要点

所有验证码都遵循相同的流程 :初始化 → 获取挑战 → 解题 → 验证 → 使用凭证。复杂度不同,结构不变。

会话绑定是核心captcha_id / challenge / token 将整个流程串联在一起,缺了它什么都做不了。

先检查是否存在客户端验证 :如果答案就在响应数据中,根本不需要识别。

识别方案选型 :日常批量走打码平台(稳定 95%+),小批量快速验证走大模型,长期固定站点可考虑自训练模型。

常见加密模式会反复出现:MD5 签名、AES-ECB 加密、RSA 公钥加密、Base64 编码------在不同站点你会反复遇到这些套路。

6.3 下篇预告

下篇将把本篇学到的知识应用到 行为型验证码 上。行为型验证码的挑战不仅是识别答案,还要模拟真人的操作行为:

  1. 滑块验证码:通过图像识别计算滑动距离、生成逼真的滑动轨迹、处理加密请求体
  2. 点选验证码:定位点击目标、计算坐标、处理点击顺序要求
  3. 进阶技术:背景图还原、轨迹分析、反检测策略