某象「刮刮乐」验证码分析
本文为技术分析,探讨验证码的交互机制与数据编码方式。
一、这是什么验证码
常见的验证码无非是滑块、点选、旋转。但某象有一种比较少见的类型——刮刮乐(Scratch)。
交互过程:页面展示一张被"打乱"的图片,你需要在图片上反复刮擦,直到刮出下方隐藏的内容,系统判定你是否刮在了正确的位置。
整个交互数据流分三步:
Step 1: 请求题目 → 服务器返回 {sid, type:2, 混淆图URL} Step 2: 还原图片 → 找到目标位置 Step 3: 生成数据 → 提交 → 拿token看上去就三步,但真正有意思的藏在第二步和第三步里面。
二、请求参数概览
获取题目(GET /api/a)的关键参数:
| 参数 | 含义 | 备注 |
|---|---|---|
ak | 应用标识 | 固定值 |
c | 设备指纹 token | 会动态过期,不是固定值 |
jsv | SDK版本号 | 目前是 5.1.53 |
cid | 场景ID | 刮刮乐固定为 72556577 |
aid | 会话标识 | 每轮随机生成 |
提交答案(POST /api/v3)的关键参数:
| 参数 | 含义 |
|---|---|
ac | 核心参数 |
sid | 第一步返回的会话ID |
API 交互本身并不复杂。真正的难点是两个问题:图片里的目标在哪,以及ac 这个几百字符的字符串到底是什么。
三、图片是怎么被混淆的
3.1 混淆方式
某象返回的图被纵向切成 32 个竖条(strip),打乱了顺序。人眼完全无法辨认里面的内容。
有意思的是,还原密钥直接嵌在图片URL里:
https://static.xxx.com/picture/xxx/abc123def456.png └─── hash_data ───┘文件名去掉.png后缀,就是这个 hash 字符串。
3.2 还原思路
核心是一个哈希 → 排列映射的过程——URL 文件名作为种子,通过一个特定的映射算法,生成 strip 的排列顺序。然后按这个排列把 strip 从混淆图里取出来,按正确顺序拼回去。
整个还原算法并不长,但前提是你得从某象那 4000 多行混淆过的 JS 里把映射算法还原出来。这才是真正花时间的地方。
还原为如下:
还原代码:
fromPILimportImagedefrestore_img(file_path,save_file_path,hash_data):defget_order(hash_value):defget_unique_num(i,used):num=i%32ifnumnotinused:used.add(num)returnnumreturnget_unique_num(i+1,used)used_numbers=set()data=[]forcharinhash_value:i=ord(char)unique_num=get_unique_num(i,used_numbers)data.append(unique_num)returndata image=Image.open(file_path)width,height=image.size s=width//32new_image=Image.new('RGBA',(width,height))order=get_order(hash_data)+[32]k=0foridxinorder:c=idx*s piece=image.crop((c,0,c+16,height))new_image.paste(piece,(k,0))k+=s new_image.save(save_file_path,format="PNG")print(f"✅ 还原完成 →{save_file_path}")restore_img('img.png','output.png','hashdata ')img.png 是输入图片名称,output.png是还原后的图片名,第三个参数是前文所讲的hash data
四、怎么定位目标
图片还原后,需要在图里找到一个特定的目标区域。
传统方案各有各的问题:模板匹配泛化不了(目标的外观会变),边缘检测不稳定(还原图背景嘈杂),OCR 也不太适用(目标可能不是纯文字)。
这种场景下深度学习目标检测是一个比较自然的选择。用 YOLO 这类模型,标注几十张同类图训练一轮,模型就能学会识别目标。因为不管外观怎么变,目标的语义是不变的。
五、AC参数到底是什么
5.1 AC 不是一个简单的加密串
ac = encode([ 加密(刮擦行为轨迹), ← 完整的鼠标/触摸路径,不是几个坐标 网格覆盖编码, ← 刮擦动作覆盖了画布上哪些区域 会话令牌绑定 ← 与本次 sid 的关联 ])5.2 前端 SDK 内部在做什么
从某象前端混淆 JS 中还原出来的逻辑链路,大致有六个步骤:
- 生成刮擦轨迹:不是"画一个矩形"这么简单。需要模拟人手反复涂抹的效果——蛇形路径在矩形区域内来回扫,每行有缓入缓出曲线,Y 轴有微小抖动,行尾偶尔"冲出边界再收回",时间间隔随机波动
- 初始化行为追踪器:将本次验证会话的 sid 绑定进去
- 逐帧喂入轨迹点:每个点的
[x, y, t]被记录,时间戳在 JS 执行时实时计算 - 加密轨迹数据:JSON 序列化 → Base64(魔改字符表) → 自定义混淆 → 拼接
- 计算刮擦覆盖数据:把画布划分为网格,计算每个格子的笔刷覆盖面积,生成一个位图再编码为十六进制字符串。注意这个编码必须和你声称的矩形坐标自洽——对不上就拒
- 合并打包:所有加密结果合并为最终输出的 ac
5.3 为什么这个设计值得琢磨
这里有一个双重闭环校验的设计:
- 第一层:行为轨迹本身要"像人"——匀速直线是机器,有缓入缓出+抖动+偶尔冲出才有人的特征
- 第二层:轨迹覆盖的网格区域,必须与你声称的矩形坐标自洽——你说刮了 (45,78) 到 (132,156) 这块,那网格编码里的"被覆盖格子"必须确实落在这个范围内
这意味着即使你找到了正确位置,如果行为不像人、或者覆盖数据和坐标不自洽,一样过不了。
这种将"行为仿真"和"坐标校验"耦合在一起的设计,比单纯的滑块验证码复杂了不止一个量级。
六、实际会遇到哪些坑
- 图片宽度不被 32 整除:还原算法必须处理末尾余数,否则右边 strip 会整体错位
- YOLO 返回的坐标不保证顺序:x1 可能大于 x2,需要 swap,不然轨迹直接生成到反方向去
- 设备指纹 C 值会过期:它不是固定值,需要通过独立的 constid 接口定期刷新,否则后端直接拒绝
- 可能返回二次验证:你以为拿到 token 了,结果回来的是
"msg": "二次验证",里面是一个完整的新题目,需要递归处理 - 轨迹参数直接影响通过率:缓入缓出曲线的选型、抖动幅度、冲出概率——这几个参数调不好,坐标再对也过不了
- 某象的 JS SDK 更新频繁:代码结构会变,今天能跑的方案明天可能需要适配
如果你对以下内容感兴趣,欢迎来知识星球(限时九折优惠):
- AC 参数内部逻辑的深度拆解:轨迹生成的每个参数为什么这样取值、曲线选型的考量、网格覆盖编码的端到端实现
- 设备指纹 constid 逆向研究( Canvas/WebGL/字体列表等几十个字段的组装与加密)
- 二次验证的递归处理与死循环防控机制
https://t.zsxq.com/v60KZ
这些内容来自真实的工程实践。星球见。
声明:本文为技术分析,探讨验证码的工作机制。所述内容仅供学习研究。