文章旨在学习和记录,若有侵权,请联系删除
前言
数美验证码的应用非常广泛,像某书、某街就是使用了数美滑块来进行风控的,当爬虫没有使用代理ip、ip质量比较差或者单个代理ip使用次数过多的时候,就会出现滑块验证码,严重的时候还会出现无限滑块的情况。
那么我们通过官网的示例,来分析下数美滑块的流程,并使用协议来通过数美滑块的验证。
目标网址:https://www.ishumei.com/trial/captcha.html
目标特点:ob混淆、环境检测、轨迹模拟
一、请求分析
-
抓包分析
拖动滑块,主要的请求流程如下:
-
验证码注册
该接口主要用于验证码注册,请求参数基本上都是固定的,然后在响应中有三个需要重点关注的地方,分别是背景图片、滑块图片、验证图片绑定的rid
,先将他们保存下来。 -
请求验证
在请求验证接口中,有三个重要的加密参数,这三个参数是分别对
滑动距离比例、滑动耗时、鼠标轨迹
进行加密的结果,使用协议请求时需要对这三个参数进行逆向,并构造出来。
–
验证通过,响应返回 PASS,表示滑动通过了验证码。
–
验证失败,响应返回 REJECT,表明参数有误,或者轨迹没通过验证。
那么,我们下面来分析加密参数 lm、fg、fm 生成的位置,以及如何构造。
二、参数逆向
-
参数定位
通过initiator定位js文件,由于网站使用了定时函数,断点断在发请求出的话堆栈不好跟踪,所以可以选择在setTimeout函数前的每个堆栈都下一个断点,观察下加密参数最早在哪边出现的。
–可以发现,在倒数第二个断点处发现了所有参数都已经生成了,可以推断就是在这个函数断点前面某个地方生成的加密参数。
–那么全局搜索一下这个变量,看下是在哪里进行赋值的。接着搜索定位到了变量赋值的地方,是通过调用
this[_0x416bd9(0x937)]()
函数来生成的加密对象,继续追进函数里面,看下这个函数是如何进行加密的。
–由下图可知,加密参数的生成位置出来了,同时加密函数 _0x84c366 的声明也是在下面,很棒。
仔细分析下代码,加密函数主要就是使用了对称加密算法des来进行加密,同时也能清晰的观察到传入的参数和密钥,简化出来的目标参数生成逻辑伪代码如下:lm = des(横向滑动距离/300, 密钥) fm = des(轨迹数组, 密钥) fg = des(开始时间-结束时间, 密钥)
-
算法还原
用python代码来实现des加密:import base64 from Crypto.Cipher import DES def des_encrypt(key, text): """des加密计算""" cipher = DES.new(key.encode('utf-8'), DES.MODE_ECB) text = text.replace(' ', '').encode() block_size = 8 while len(text) % block_size: text += b'\0' encrypted_text = cipher.encrypt(text) return base64.b64encode(encrypted_text).decode('utf-8')
三、模拟轨迹
- 识别图片、计算距离
这边使用OpenCV来识别图片,计算滑动距离。(也可以使用深度学习的方式,采集大量数美的背景图和滑块图作为训练材料,使用pytorch进行回归模型训练,将图像数据映射到用户滑动的距离上,这种方式的识别准确率相对来说会高一些)import cv2 import numpy as np from io import BytesIO def get_distance(fg_resp, bg_resp): """计算滑动距离""" # 1. 将背景图、滑块图的二进制响应体转为BytesIO对象 bg = BytesIO(bg_resp.content) fg = BytesIO(fg_resp.content) # 2. 使用imdecode进行图像解码,转成OpenCV中的Mat对象 target = cv2.imdecode(np.asarray(bytearray(fg.read()), dtype=np.uint8), 0) template = cv2.imdecode(np.asarray(bytearray(bg.read()), dtype=np.uint8), 0) # 3. 使用matchTemplate方法进行模板匹配,返回背景图中与滑块的位置匹配值数组 result = cv2.matchTemplate(target, template, cv2.TM_CCORR_NORMED) # 4. 使用numpy中的unravel_index函数获取result中位置匹配值最大的索引位置,既是滑动的距离 _, distance = np.unravel_index(result.argmax(), result.shape) return distance
- 轨迹模拟
生成轨迹的方法很多,此处分享一个通过加速度来模拟生成轨迹的方法def get_random_tracks(distance): """生成轨迹""" tracks = [] y = 0 v = 0 t = 1 current = 0 mid = distance * 3 / 4 exceed = 20 z = t tracks.append([0, 0, 1]) while current < (distance + exceed): if current < mid / 2: a = 15 elif current < mid: a = 20 else: a = -30 a /= 2 v0 = v s = v0 * t + 0.5 * a * (t * t) current += int(s) v = v0 + a * t y += random.randint(-5, 5) z += 100 + random.randint(0, 10) tracks.append([min(current, (distance + exceed)), y, z]) while exceed > 0: exceed -= random.randint(0, 5) y += random.randint(-5, 5) z += 100 + random.randint(0, 10) tracks.append([min(current, (distance + exceed)), y, z]) return tracks
四、请求验证
如图,滑动成功率还是ok的。
总结
- 数美验证码的数据包请求流程还是挺清晰的,比较好分析。
- 数美的版本迭代会比较频繁,每次迭代一般都会改变加密参数des的密钥,可以尝试通过正则来匹配最新的密钥参数。
- 如果想要追求更高的通过率的话,可以进一步优化下图像识别和轨迹模拟这两方面。完。