记一次某东登录滑块 识别+轨迹

前言

前段时间闲来无事想找个带轨迹的滑块练练手,正好找到一个算是入门级的小demo了,近期有空整理了技术点更新上来记录一下~~

这个滑块主要难点就是轨迹,只要轨迹过了剩下的都很轻松,所以作为滑块类入门的demo最合适了~ 把这个滑块过了再去对抗更高级些的就有思路了。

入口就不放了 从官网进入手动登录就会弹

接口分析

点击一下刷新验证码可以看到这个包

其中bg为后面的背景图,patch为滑块的小拼图。后续可以使用模板匹配来进行识别。

验证接口

手动滑一次可以看到该包

c就是前面接口中返回的challenge的值,d为轨迹的密文,下面进行逆向分析。w的值就较为重要了,应该是bg图片在页面上的大小,一开始我没注意到这个值,导致折磨了我很久,后续的轨迹以及识别位置对了也会返回失败,后面才反应出来去分析这个值。

  • 返回值的说明先写在前头
  1. 返回值0为验证失败
    jsonp_xxxxx({"success":"0","message":"fail","nextVerify":"SLIDE_VERIFY"})

  2. 返回值1为识别滑块的距离正确 validate有返回值说明验证通过
    jsonp_xxxxx({"success":"1","message":"success","validate":"xxxxxxx","nextVerify":"NULL_VERIFY"})

  3. 返回这个即为 轨迹校验失败
    jsonp_xxxxx({"u":"2","success":"0","message":"refuse","nextVerify":"SLIDE_VERIFY"})

轨迹的加密

首先定位下断点,方法有很多种,我这里通过松开鼠标事件来定位

然后滑动滑块,随便找个位置松开就会断住

断住后发现没有混淆,通过代码可以进行猜测分析。

a['mousePos'] 很明显就是轨迹的数组了 在第0和第1组的x有一个偏差,这里大概是22-25左右。

后续跟入submit函数看他是怎么对该数组进行加密的

进入submit函数后通过控制台输出来慢慢分析,发现d参数就在下面

通过a['getCoordinate'](b)来进行生成密文

再进入a['getCoordinate']()函数

非常简单的代码,扣下来就行,后面缺啥补啥,大概补2-3个函数即可。

滑块距离的识别

我想到的方案大致有两种一种是使用模型来识别,还有一种是使用Opencv来进行模板匹配识别。

我这里为了方便省事采用第二种,正确率大概为96%左右 已经很够用了! 给出参考代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import base64
import cv2
import numpy as np

def _tran_canny(image):
"""消除噪声"""
image = cv2.GaussianBlur(image, (3, 3), 0)
return cv2.Canny(image, 50, 150)

def draw_slider(image, template, top_left, max_loc):
w, h = image.shape[1], image.shape[0]
cv2.line(template, (top_left, 0), (top_left, 94), (0, 255, 0), 1)
bottom_right = (max_loc[0] + w, max_loc[1] + h)
cv2.rectangle(template, max_loc, bottom_right, (0, 255, 0), 1)
cv2.imshow('Detected', template)
cv2.waitKey(0)

def detect_displacement(img_slider_path, image_background_path):
x = base64.b64decode(img_slider_path)
img_array = np.frombuffer(x, np.uint8)
image = cv2.imdecode(img_array, cv2.COLOR_RGB2BGR)
image = cv2.resize(image, (34, 34))

y = base64.b64decode(image_background_path)
img_array2 = np.frombuffer(y, np.uint8)
template = cv2.imdecode(img_array2, cv2.COLOR_RGB2BGR)
#这里的242即为前面的w值 在F12源代码中取即可
template = cv2.resize(template, (242, 94))
#进行模板匹配
res = cv2.matchTemplate(_tran_canny(image), _tran_canny(template), cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
#计算出来需要加上一点偏移 就是前面分析的轨迹数组中的第0项与第1项的插值
top_left = round(max_loc[0])+11
print(top_left,max_loc)

draw_slider(image, template, top_left, max_loc)
return top_left

这里需要注意的是对template进行resize为242 对应上前面的w值,而后续的top_left计算出后需要加上一点偏移,偏移量就是前面分析的轨迹数组中的第0项与第1项的差值,我这里取的为11,一定要与轨迹数组中的对应。

滑动轨迹

这就到了整个流程中最难的一步了,轨迹校验上了模型,我尝试了几种简单的生成都过不了,贝塞尔曲线 随机生成模拟 基于前两种后加柏林噪声

本来想研究出一套能过的算法,久经折磨之后还是放弃了,使用手动采集轨迹后面再进行裁剪合成。这样通过率大概只有70%-80%左右 也勉强算够用了。

生成轨迹的参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import random
import time

track=[[1177,339,1723343514943],[1188,370,1723343514943],[1189,370,1723343515185],[1191,371,1723343515192],[1192,371,1723343515200],[1194,371,1723343515208],[1195,372,1723343515225],[1196,372,1723343515233],[1197,372,1723343515248],[1199,372,1723343515264],[1200,373,1723343515280],[1201,373,1723343515288],[1202,373,1723343515304],[1203,373,1723343515385],[1204,373,1723343515401],[1205,373,1723343515417],[1205,374,1723343515425],[1206,374,1723343515432],[1207,374,1723343515440],[1208,374,1723343515448],[1209,374,1723343515456],[1210,374,1723343515464],[1211,374,1723343515472],[1212,374,1723343515480],[1213,374,1723343515488],[1214,374,1723343515496],[1215,374,1723343515504],[1216,374,1723343515520],[1217,374,1723343515530],[1218,374,1723343515553],[1219,374,1723343515577],[1220,374,1723343515593],[1221,374,1723343515609],[1222,374,1723343515641],[1223,374,1723343515665],[1224,374,1723343515674],[1225,374,1723343515680],[1226,374,1723343515689],[1227,374,1723343515696],[1229,374,1723343515704],[1230,375,1723343515712],[1232,375,1723343515729],[1234,375,1723343515737],[1235,376,1723343515744],[1237,376,1723343515752],[1239,376,1723343515760],[1241,377,1723343515768],[1242,377,1723343515776],[1244,377,1723343515784],[1245,377,1723343515793],[1246,377,1723343515800],[1247,377,1723343515809],[1248,377,1723343515816],[1249,377,1723343515824],[1250,377,1723343515832],[1251,377,1723343515840],[1252,377,1723343515848],[1253,377,1723343515857],[1255,377,1723343515864],[1256,377,1723343515872],[1257,377,1723343515880],[1258,377,1723343515888],[1259,377,1723343515896],[1261,377,1723343515905],[1263,377,1723343515912],[1264,377,1723343515920],[1266,377,1723343515928],[1268,377,1723343515936],[1270,377,1723343515944],[1272,377,1723343515952],[1275,377,1723343515960],[1276,377,1723343515968],[1277,377,1723343515976],[1279,377,1723343515984],[1280,377,1723343516000],[1281,377,1723343516009],[1282,377,1723343516025],[1283,377,1723343516033],[1285,377,1723343516047],[1287,377,1723343516048],[1288,377,1723343516056],[1290,377,1723343516064],[1291,377,1723343516072],[1292,377,1723343516080],[1294,377,1723343516088],[1295,377,1723343516096],[1296,377,1723343516105],[1298,377,1723343516112],[1299,377,1723343516121],[1300,377,1723343516128],[1302,377,1723343516136],[1303,377,1723343516144],[1305,377,1723343516152],[1307,377,1723343516160],[1309,377,1723343516168],[1310,377,1723343516176],[1312,377,1723343516184],[1315,377,1723343516192],[1316,377,1723343516200],[1318,377,1723343516211],[1320,377,1723343516216],[1322,377,1723343516225],[1323,377,1723343516232],[1325,377,1723343516241],[1327,377,1723343516248],[1329,377,1723343516256],[1332,377,1723343516264],[1334,377,1723343516273],[1336,377,1723343516281],[1339,377,1723343516289],[1341,377,1723343516296],[1343,377,1723343516305],[1345,377,1723343516312],[1346,377,1723343516320],[1348,377,1723343516328],[1349,377,1723343516337],[1350,377,1723343516344],[1351,377,1723343516353],[1353,377,1723343516360],[1354,377,1723343516368],[1355,377,1723343516376],[1356,377,1723343516388],[1357,377,1723343516392],[1358,377,1723343516401],[1360,377,1723343516409],[1361,377,1723343516416],[1363,377,1723343516424],[1364,377,1723343516433],[1366,377,1723343516441],[1368,377,1723343516449],[1369,377,1723343516456],[1371,377,1723343516465],[1373,377,1723343516472],[1375,377,1723343516480],[1376,377,1723343516488],[1378,377,1723343516497],[1379,377,1723343516504],[1380,377,1723343516530],[1381,377,1723343516567],[1382,377,1723343516568],[1384,377,1723343516576],[1385,377,1723343516584],[1386,377,1723343516592],[1387,377,1723343516600],[1388,377,1723343516608],[1389,377,1723343516616],[1390,377,1723343516625],[1391,377,1723343516632],[1392,377,1723343516641],[1393,377,1723343516648],[1394,377,1723343516664],[1395,377,1723343516672],[1396,377,1723343516689],[1397,377,1723343517035],[1398,377,1723343517040],[1399,377,1723343517048],[1400,377,1723343517068]]
roch=[[0, 414, 1723343562711], [0, 415, 1723343562719], [1, 415, 1723343562735], [2, 415, 1723343562751], [3, 415, 1723343562767], [4, 415, 1723343562783], [5, 415, 1723343562799], [6, 415, 1723343562808], [7, 415, 1723343562818], [8, 415, 1723343562831], [9, 415, 1723343562847], [10, 415, 1723343562855], [11, 414, 1723343562863], [12, 414, 1723343562871], [13, 414, 1723343562887], [13, 413, 1723343562894], [14, 413, 1723343562903], [15, 413, 1723343562975], [14, 413, 1723343563159], [13, 413, 1723343563175], [12, 413, 1723343563191], [11, 413, 1723343563223], [10, 413, 1723343563255], [9, 413, 1723343563263], [8, 413, 1723343563271], [7, 413, 1723343563278], [6, 413, 1723343563286], [5, 413, 1723343563295], [4, 413, 1723343563302], [3, 413, 1723343563311], [2, 413, 1723343563375], [1, 413, 1723343563490], [0, 413, 1723343563527], [0, 413, 1723343564062]]

def get_track(distance):
sliders=[]
timenow=str(int(time.time()/10))
for i in track:
if i[0] <= distance+track[0][0]:
sliders.append([i[0],i[1],int(timenow+str(i[2])[-4:])])
else:
sliders.append([distance + track[0][0], i[1], int(timenow + str(i[2])[-4:]) + 700 + int(random.random() * 1000)])
break

lastx=sliders[-1][0]
lasty=sliders[-1][1]
for i in range(len(roch)-1):
if i == 0 and roch[i][0] == 0:
times=sliders[-1][2]
else:
times=sliders[-1][2]+(roch[i+1][2]-roch[i][2])
sliders.append([lastx+roch[i][0],lasty,times])
sliders.append([lastx+roch[-1][0],lasty,sliders[-1][2]+1000])
return sliders

track数组为从滑块起点到滑块末尾的轨迹 roch为晃动的轨迹

roch的录制:先打开滑块,准备手动滑一次,先从滑块开始然后滑正确的滑块缺口上稳住鼠标,然后模拟晃动往左或者往右进行滑动,然后再滑回滑块的位置 后面这一段就是晃动的轨迹了。把后面这一段的x减去滑块缺口的位置就可以得出0 1 2 3 4 5 ··· 5 4 3 2 1 0相关的轨迹点

得到两个数组后进行裁剪的合成,首先生成时间戳,利用track中的时间戳后4位+当前时间戳的前6位

组成新的时间戳,先从trach数组中获取滑块起点到滑块位置的轨迹点,例如当前滑块位置为103,则获取起点到103的轨迹点,然后再最后面添加一个精准的轨迹点 使用distance + track[0][0]的方法来添加最后一个轨迹点。

得到上面的正常轨迹之后,通过率也能有60%左右,还想通过率再高一点就在正常轨迹的后面拼接晃动的轨迹点。

在sliders后的第一位添加时 时间戳为正常轨迹的最后一个点的时间戳作为起始点,后续的每一个晃动轨迹点的时间戳都为前一项的时间戳+晃动数组中后一位的时间戳-当前晃动数组中的时间戳 times=sliders[-1][2]+(roch[i+1][2]-roch[i][2])

全的代码就不放上了,还有一个点需要注意的获取验证码提交之间不能太快,不然就都是失败,延迟大概5秒左右再提交。

最后附上被折磨了几天后的结果~


记一次某东登录滑块 识别+轨迹
https://wantoper.github.io/2024/09/10/old/JD-Slider/
作者
Wantoper
发布于
2024年9月10日
许可协议