关于滑动验证码的破解,主要分为几个部分:
1、图像获取
2、缺口定位
3、轨迹模拟
4、加密传输
由于部分滑动验证码加密较为复杂,故大部分均使用selenium绕过第四步进行实现。
近期有需求需要针对一下京东的滑动验证码,发现其加密并不是十分复杂,故本篇使用纯代码破解。
现在我们先一步一步走下去:
下图为京东验证码:
和其他验证码一样,图片分为两部分,一部分为缺口,一部分为背景,将缺口移动到背景上对应部分即可。
我们首先来获取图片:
请求并不复杂,appid可请求首页取到或直接写死,校验得e并不影响登录及图片校验,可随机生成或直接写死。
session = requests.session()
session.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36'
}
url = 'https://passport.jd.com/new/login.aspx'
r = session.get(url, verify=False)
url = 'https://seq.jd.com/jseqf.html?bizId=passport_jd_com_login_pc&platform=js&version=1'
r = session.get(url, verify=False)
seqSid = re.compile('sessionId="(.+?)"').findall(r.text)[0]
eid = 'RKC352YG2KMGV4IJL54WU5ODSEKNI222QI3I6I4N25FE3XRN4U7EZQ3HW3R622BMXWUX3V3JNXXZOVLVKAN6IVJIRQ'
url = 'https://iv.jd.com/slide/g.html'
payload = {
'appId': '1604ebb2287',
'scene': 'login',
'product': 'click-bind-suspend',
'e': eid,
'callback': '',
}
r = session.get(url, params=payload, verify=False)
challenge = r.json().get('challenge')
我们再来看看返回值:
很明显,bg和patch分别对应滑块和背景图片:
可以看到滑块为png格式,但是图片大小仅仅只有滑块那么大,也就是说返回值里面肯定有来确定滑块Y轴方向的参数。
也就是 这个参数估计是表示滑块Y轴的位置。
我们可以用ps打开背景图片定位一下y=23的位置,发现正好是滑块的顶部。
所以这里我们只需对图片二值化后即可找出阴影x的位置了:
def prt_x(path, y):
img = Image.open(path)
img = get_threshold(img)
# img.show()
# 灰度化
img = img.convert('L')
# img.show()
# 二值化阈值
threshold = 95
# 二值化
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
img = img.point(table, '1')
# img.show()
# 滑块偏移量
X_SIZE = 12
Y_SIZE = 38
x_list = []
for x in range(0, img.size[0] - X_SIZE, 2):
# (x0,y0,x1,y1)
box1 = (x, y, x + X_SIZE, y + Y_SIZE)
image1 = img.crop(box1)
# image1.show()
matrix = np.asarray(image1)
x_list.append((np.average(matrix == circle), x))
# print x_list
aaa = max(x_list, key=lambda item: item[0])
print aaa
x = aaa[1]
box1 = (x, y, x + X_SIZE, y + Y_SIZE)
image1 = img.crop(box1)
image1.show()
return max(x_list, key=lambda item: item[0])
接下来我们看看滑动后的请求:
参数也不复杂,c和s可以请求首页取到,关键就剩下一个d,明显是加密过的,而且绝对是和轨迹相关。
那我们就追溯一下这个d:
这样就找到了轨迹加密方法了,我们可以看到传入是一个array,返回则是加密后的字符串。
现在就只需构造轨迹array即可。
我们先看看array是长什么样子的:
一看结构,肯定就是x,y座标+当前时间戳组成。
多试几次就能发现,第一行是表示验证码在当前窗口的位置。
所以我们只需要模拟第一行之后的即可。
生成轨迹的方法网上也有很多了,我就随便copy一个过来了:
def get_tracks(distance):
# 构造滑动轨迹
'''
:param distance: (Int)缺口离滑块的距离
:return: (List)移动轨迹
'''
# 创建存放轨迹信息的列表
trace = []
# 设置加速的距离
faster_distance = distance * 3 / 5
# 设置初始位置、初始速度、时间间隔
start, v0, t = 0.0, 0.5, 0.2
# 当尚未移动到终点时
while start < distance:
# 如果处于加速阶段
if start < faster_distance:
# 设置加速度为2
a = round(random.uniform(0.5, 0.8), 2)
# 如果处于减速阶段
else:
# 设置加速度为-3
a = round(random.uniform(-0.7, -0.9), 2)
# 移动的距离公式
move = v0 * t + 1 / 2 * a * t * t
move = int(move)
# 此刻速度
v = v0 + a * t
# 重置初速度
v0 = v
# 重置起点
start += move
# 将移动的距离加入轨迹列表
trace.append(round(move))
# 返回轨迹信息
return trace, sum(trace)
加密方法解决还是参考之前提到过的,直接调用js的方法,然后将我们构造的轨迹数组传入,即可得到参数d了。
这样就可得到验证成功返回值了: