文章旨在学习和记录,若有侵权,请联系删除
数美验证码的应用非常广泛,像某书、某街就是使用了数美滑块来进行风控的,当爬虫没有使用代理ip、ip质量比较差或者单个代理ip使用次数过多的时候,就会出现滑块验证码,严重的时候还会出现无限滑块的情况。
那么我们通过官网的示例,来分析下数美滑块的流程,并使用协议来通过数美滑块的验证。
目标网址:https://www.ishumei.com/trial/captcha.html
目标特点:ob混淆、环境检测、轨迹模拟
验证码注册
该接口主要用于验证码注册,请求参数基本上都是固定的,然后在响应中有三个需要重点关注的地方,分别是背景图片、滑块图片、验证图片绑定的rid
,先将他们保存下来。
请求验证
在请求验证接口中,有三个重要的加密参数,这三个参数是分别对 滑动距离比例、滑动耗时、鼠标轨迹
进行加密的结果,使用协议请求时需要对这三个参数进行逆向,并构造出来。
–
验证失败,响应返回 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')
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