吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5057|回复: 36
收起左侧

[Web逆向] v5滑块验证逆向

  [复制链接]
zzyzy 发表于 2025-6-15 14:32
本帖最后由 zzyzy 于 2025-6-15 14:41 编辑

本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!


目标:实现滑块验证,python纯算。
网站aHR0cHM6Ly93d3cudmVyaWZ5NS5jb20vZGVtbw==

1.开始今天的滑块分析,直接进官网中demo在线体验,找到滑块验证,先是分析完整流程,再逐步分析,验证成功后,可以发现和以往的协议不同,本网站是一个ws协议传输,发送了六条数据消息,接受三条数据消息,可以先了解一下ws协议,该网站没有参数上的加密只有消息内容做的一些加密处理。
image.png
WebSocket (ws) 协议是一种在单个TCP连接上进行全双工通信的协议。在JavaScript中,我们可以使用WebSocket API来创建WebSocket连接,实现客户端与服务器之间的实时通信。

下面是一个简单的WebSocket客户端案例,包括以下步骤:

  1.创建一个WebSocket对象,指定要连接的服务器地址(以"ws://"或"wss://"开头)。
  2.监听WebSocket事件,如onopen, onmessage, onerror, onclose。
  3.在连接建立后,通过send方法发送数据。
  4.在接收到消息时,处理消息数据(通常为字符串或Blob对象)。
你可以使用Node.js的ws库、Python的websockets库等创建服务器,或者使用在线的WebSocket测试服务器(如echo.websocket.org)进行测试。

2.那就从上到下逐步分析吧,看看是什么加密,首先找onopen,因为请求调用堆栈很少,可以直接跳转进去都打下断点,然后刷新页面验证滑块。
image.png

3.在最后一个断点可以看到,Ja是uqMKPtXJO9FELEwk,new一个WebSocket对象,后面会定义onopen, onmessage等,正好就在try catch下面, he是在上面定义的 搜索 he = 可以找到,进入到onopen里面。
image.png image.png

4.在里面的调用a,在a里面调用g,进入到g中,往里面跟,上面是一些定义,主要看c[P][Ga]的调用,传入三个参数,打个断点进去。
image.png image.png image.png

5.一边分析一边往下跟,走到我断点的位置,E(b, a);的得到加密结果就是我们想要的发送请求的数据,然后push到数组c中,拼接成字符串k,逐步分析c中的三个数据。
image.png image.png

6.在c定义为空数组的地方断点一下,可以看到,第一个数据是传参进来的,就是Ob,上面有定义,可以写死,第二个搜索一下就出来了,在页面返回里面对应的token值,主要是看第三个参数。
image.png image.png image.png

7其中a = Y(d, h); 是获得加密的key,在第一次发送消息时d h时一样的,进入到函数Y中,分析一下,O是charCodeAt  T 是length,可以看到根据token的最后一个字符的uniode编码值对2求余,实际上就是对字符串,单双数索引的提取,直接转python代码。
image.png image.png

8.接下来分析需要加密的明文,需要注意的有两个参数一个是requestId,就是req加上时间戳,一个是data中l的值是对应页面返回的token,其他都是浏览器指纹信息了,可以写死。
image.png

9.进入E函数中可以明显看到是一个aes加密,其中iv是函数q生成的,进入到q中,可以看到是对0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz随机取的值,注意的是返回结果是 iv加上加密结果,base64编码一下。
image.png image.png

10.分析完k的由来后,继续往下走,可以看到根据k的长度,每1024为一段,在函数f,消息拼接一下标识参数,分成三段消息发送,转换成python代码。
image.png image.png

11.使用python发送消息之后会得到一段加密的字符串,ws协议返回值的处理是在onmessage中,在刚才的open下面就有message 的定义。
image.png image.png

12.在ge里面断点后,单步调试两下,进来分析解密流程,可以看到是对返回的结果,Y函数是获得加解密需要的key,可以看到返回的加密结果前32位结合token生成key,剩下位解密的密文。
image.png

13.进入到函数ja中解密,其中偏移量是剩下密文中取前32位,还是aes解密,注意一下密文base64解码。
image.png

14.以下是加解密的python代码。
[Python] 纯文本查看 复制代码
def aes_ctr_encrypt(plaintext: str, key: str, iv: str) -> str:
    """
    AES CTR模式加密,等效于CryptoJS的AES.encrypt(..., mode=CTR, padding=NoPadding)
    并将IV与密文拼接后Base64编码输出。

    参数:
    - plaintext: 明文,bytes类型
    - key: 密钥,bytes类型
    - iv: 初始化向量,bytes类型(CryptoJS中iv长度通常为16字节)

    返回:
    - Base64编码的字符串,内容为 iv + ciphertext
    """

    plaintext = plaintext.encode('utf-8')
    key = key.encode('utf-8')
    iv = iv.encode('utf-8')

    iv_int = int.from_bytes(iv, byteorder='big')
    ctr = Counter.new(128, initial_value=iv_int)

    # 创建AES-CTR加密器
    cipher = AES.new(key, AES.MODE_CTR, counter=ctr)

    # 加密明文
    ciphertext = cipher.encrypt(plaintext)
    encrypted = iv + ciphertext

    return base64.b64encode(encrypted).decode('utf-8')


def aes_ctr_decrypt(ciphertext: str, key: str) -> str:
    """
    对应JavaScript的AES CTR解密函数,等效于CryptoJS实现。

    参数:
    - ciphertext: Base64字符串,包含加密数据
    - key: 密钥(Utf8字符串)

    返回:
    - 解密后的明文字符串
    """

    # 1. Base64解码输入密文
    decoded = base64.b64decode(ciphertext).hex()
    iv = bytes.fromhex(decoded[:32])  # 前32位hex字符作为IV
    cipher_text = base64.b64encode(bytes.fromhex(decoded[32:])).decode('utf-8')

    # 2. 初始化CTR模式
    iv_int = int.from_bytes(iv, byteorder='big')
    ctr = Counter.new(128, initial_value=iv_int)

    # 3. 创建AES-CTR解密器
    cipher = AES.new(key.encode('utf-8'), AES.MODE_CTR, counter=ctr)

    # 4. 解密并尝试解码为UTF-8字符串
    decrypted = cipher.decrypt(base64.b64decode(cipher_text))
    try:
        return decrypted.decode('utf-8')
    except UnicodeDecodeError:
        raise ValueError("Token is wrong or expired.")


15.第一次返回值会的到一个u,这是接下来用到的,结合token 生成加解密的key,其实也就是返回值的前32位,接下来就是第二次的消息发送了,和第一次消息的区别就是明文和key了,参数基本相同只需要改requestid 和data中l对应token值,还有就是消息头标识是1|1|开头的。
image.png image.png

16.收到第二次消息后还是结合u生成的key解密流程和之前一样,这样就可以得到需要验证图片的url,返回结果还有和m值,和u一样,m是下面加密轨迹发送第三次请求用到的,可能是因为demo吧,u、m都是一样的。
image.png

17.滑块的识别还是使用的ddddocr,代码如下。
[Python] 纯文本查看 复制代码
def get_distance(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


18.剩下就是第三次的消息了,是对轨迹的加密,需要的参数,requestId command 和data中s 和f,f是token,requestId时间戳组合,主要看轨迹s,前两个是时间戳,后面三个为一组,滑动时间,滑动距离x,y偏移量等,我这直接给出我使用轨迹生成的算法了。
image.png

19.python轨迹代码。
[Python] 纯文本查看 复制代码
def generate_slider_track(target_distance):
    track = []
    start_time = int(time.time() * 1000)
    current_distance = 0
    current_time = 0
    y_offset = 0

    track.extend([str(start_time)])
    while current_distance < target_distance:
        # 时间步长随机,模拟不均匀采样,20~60ms之间
        time_step = random.randint(20, 60)
        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

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

        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


20.好啦,基本所有流程都分析完了,结果是true,如果轨迹或者requestid的时间戳是过去的,会是false,完结撒花,这个demo分析完了,感谢大佬们观看。
image.png









免费评分

参与人数 17威望 +2 吾爱币 +114 热心值 +14 收起 理由
Ceciliana + 1 + 1 用心讨论,共获提升!
zzbk5269 + 1 我很赞同!
lwGoodChinese + 1 热心回复!
sanqiuye + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
小狐狸FM + 1 + 1 用心讨论,共获提升!
yanhuo0 + 1 谢谢@Thanks!
Golive180 + 1 + 1 用心讨论,共获提升!
瑾年Lee + 1 + 1 用心讨论,共获提升!
qmsy273 + 1 热心回复!
ioyr5995 + 1 + 1 我很赞同!
a2279871879 + 1 + 1 用心讨论,共获提升!
涛之雨 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lys76 + 1 + 1 热心回复!
逍遥黑心 + 1 + 1 用心讨论,共获提升!
wzh103103 + 1 热心回复!
飘缈孤鸿影 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
ZenoMiao + 1 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ssdyaqcn 发表于 2025-8-19 15:43
我写的代码总的来说是成功了,但是成功率只有大约一半,另一半时间会报错:
asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of 2 expected bytes
The above exception was the direct cause of the following exception:
websockets.exceptions.ConnectionClosedError: no close frame received or sent
感觉是异步读取这一块不太稳定,有人遇到吗?
hellotoday 发表于 2025-6-15 14:45
 楼主| zzyzy 发表于 2025-6-15 14:47
ssscan 发表于 2025-6-16 01:31
牛牛牛,一步一步的教程
tzblue 发表于 2025-6-16 05:16
感谢楼主分享思路过程!
yiluoen0502 发表于 2025-6-16 08:33
感谢分享
DearFantasy 发表于 2025-6-16 09:37
厉害,现在看见这种混淆过的代码就头疼
wzh103103 发表于 2025-6-16 16:51
感谢博主分享
 楼主| zzyzy 发表于 2025-6-16 19:36
DearFantasy 发表于 2025-6-16 09:37
厉害,现在看见这种混淆过的代码就头疼

可以了解以下ast
逍遥黑心 发表于 2025-6-17 09:43
本帖最后由 逍遥黑心 于 2025-6-17 09:44 编辑

老哥怎么触发的,demo地址进去咋是个死页面。
没事了 ,挂代理进去竟然不触发
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-18 03:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表