某网站滑块逆向-1

本文最后更新于:2025年6月30日 下午

目标网站:aHR0cHM6Ly93d3cudmVyaWZ5NS5jb20vZGVtbw==

参考文章

前期调试

v5推出的滑块验证的demo

首先需要初步调试,弄清楚该网页的调用流程,发现核心代码

这个网页很奇怪,可能是个demo的原因,将生成的token值嵌入在了返回的页面当中

在数据传输中也是采用了WebSocket的方式

查看启动器,方法不多,均打上断点

代码用了AST混淆,不过可以动态调试看值。可以发现在上面第一个方法的断点处,初始化了一个套接字对象,那么直接从这里逐步调试分析

下面这三个函数分别对应webSocket的三种方法,onopenonclodeonmessage

接着调试,发现在创建Socket之后,也就是即将发送信息,调用了方法F,接着进入到e[Ga](De)方法中

调试到下面这个方法中,这个方法是核心部分,我们具体分析

核心部分

下面分析a[Ga] = function(a, b, c)这个方法:

下面是传入的参数值,整理后b是一个json结构

1
2
3
4
5
6
7
8
"a": "7051CA8BF3E64DEDAA9334620DA8F5F1", // 数组c的第一部分
"b": {
"l": "7bb66a357fd84eaa9839c79b9b641a7f", // 数组c的第二部分
"f": "43b18dd09497378a6413ca595b0c9b4e577ccc6c",
"m": "af50574c19134f4c925188895e21076a",
"j": "ES5",
"tl": 5,
...

下面走到Y(d,h)E(b,a),d和h是相同的值

进入到Y(d,h),发现这是一个返回输入字符串取奇偶位的拼接字符串的方法,起点通过b的最后一个字符的Unicode值模2得到,因为这里b[0]charCodeAt()

接着进入到E(b,a),这就是加密函数,只用了AES,然后key是上面Y()方法的返回值,注意最后返回的是base64(iv + encText)

发现iv是通过q(16)产生,看一下这个方法,f[b]Math.randomsc是一个字符数组,那么这个方法就是返回一个长度为16的随机字符串

加密之后,通过push方法将a加入到数组c中,最后kc中三个字符串拼接而成。c[0]c[1]是方法输入的参数值,一个常量一个token

继续往下面调试,发现将k赋值给了a,并且将一些常量拼接进了字符串中

拼接完成后,会回转到上面的e[P][qe](a)处,这里查看值可以发现就是调用WebSocket.send(a),也就是发送最终的加密字符串

还有一点需要注意,这里是分三次将字符串发送出去,每段1024字节,并且前面都有标识号0|3|0

发送完成后,这个方法就分析完了

接着会得到服务端返回的消息,

分析过程中由于不断刷新页面调试,相关数据可能前后不一致。

这里c仍然为tokeng为截取的返回字符串前32位;a为被截断后,32位后面的字符串;这里Y(a,b)和上面一样,得到解密的key

ja()是解密函数,iv是截取了输入a的前32位,密文是32位之后的部分

下面就是解密之后a的明文:

得到这个明文后,还需要发送第二次数据,来得到真正的图片地址

关于第二次数据的生成,可以调试得到存在两部分数据,一个常量和一个json结构的变量,和第一次差不多

1
2
3
4
5
6
7
8
9
E97CE473AE1A46A8BF4A88FD73636D7E
{
"requestId":"Req_17511751856524", // 时间戳
"command":"E97CE473AE1A46A8BF4A88FD73636D7E",
"data":{
"l":"f5de218081f5418a963a4b2a63eecc2d", // token
"z":"43b18dd09497378a6413ca595b0c9b4e577ccc6c"
}
}

然后将两部分拼接进行加密,前面加上1|1|0|就得到了第二次的发送数据。

下面是第二次返回结果解密后的展示,这里发现密钥和第一次的一样就直接用,发现已经可以获取本次的图片和滑块地址

得到图片地址后,使用ddddocr库直接滑块识别,最后实际用到的其实是res['left']

1
2
3
4
5
6
7
8
9
10
11
12
13
def getDistance(bg, tp, save_path=None):
det = DdddOcr(det=False, ocr=False, show_ad=False)
res = det.slide_match(tp, bg, simple_target=True)
if save_path is not None:
# 将背景图片的二进制数据加载为Pillow Image对象
left, top, right, bottom = res['target'][0], res['target'][1], res['target'][2], res['target'][3]
bg_image = Image.open(io.BytesIO(bg))
draw = ImageDraw.Draw(bg_image)
draw.rectangle([left, top, right, bottom], outline="red", width=2)
bg_image.save(save_path)
print(f"[+] 已保存标注后的图片到: {save_path}")
return res
return res

得到的移动距离后,需要产生模拟滑块的移动参数:这里需要产生两个时间戳,拼接在轨迹参数的前后,保证前面的时间t1大于后面的t2即可。同时发现,只有当request_id中的时间戳等于t1才会得到通过。

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
def generateSliderTrack(target_distance):
track = []
start_time = int(time.time() * 1000)
current_distance = 0
current_time = 0

track.extend([str(start_time)])
while current_distance < target_distance:
# 时间步长随机,模拟不均匀采样,25~65ms之间
time_step = random.randint(25, 65)
current_time += time_step

# 横向步长,接近目标时减小步长
if target_distance - current_distance > 10:
x_step = random.randint(4, 10)
else:
x_step = random.randint(1, 4)

current_distance += x_step
if current_distance > target_distance:
current_distance = target_distance

# 纵向浮动在-4到4之间,模拟抖动
y_offset = random.randint(-4, 4)

track.extend([str(current_time), str(current_distance), str(y_offset)])

end_date = start_time + current_time + random.randint(100, 500)
track.extend([str(end_date)])
track_str = ",".join(track)
return track_str, str(start_time)

计算完成后,还需要将滑动产生的轨迹参数加密后发送给服务端,加密的key和第二次一样,都是first_msg[:32],需要注意的是带上2|1|0的前缀

最后整个流程:得到data:{success:true}即为通过


某网站滑块逆向-1
https://3xsh0re.github.io/2025/06/27/某网站滑块逆向-1/
作者
3xsh0re
发布于
2025年6月27日
许可协议