食用地址(过期的链接都会弹出,不一定是这个,因为链接是动态更新的):
aHR0cHM6Ly9rbnMuY25raS5uZXQvdmVyaWZ5L2hvbWU/Y2FwdGNoYVR5cGU9YmxvY2tQdXp6bGUmaWRlbnQ9YmMzOTdjJmNhcHRjaGFJZD1lNGVkYzI0NS03NWYzLTRlNmMtYmUwYS0zMjYxNDI4MGYzMDQmcmV0dXJuVXJsPWlFQ0JEbmhyNzE2UWQ2cGJiN0VsMTBuZ3VQNVNibTlVUXhwenNZeFNBTS1KdVJlSUphalh0SHRBX0VkZXg0cWhpcnJIN0ZDUkNvb3ZaQXBrakFNbkxCRjZRMUEzNXk5WVNfUHBEVzZJeG11VWppTzJMeUFKUDJwY1VKMXhuSU1KSVBYOHhZZXVLSndmR3ZUQm1lM0VSWm01MF9IQm5STjhLVUx2ZW5wLTNCZ0pSVXJyN05BQ0pDNVl3azVqdXhILW1CVXVaQVBQMVVDTGRTQl9mN3V3ckxTd1JUQ3lmRnlSbGpjVEFYRXEzUHFQSVlmeWh5bjQ1V2tMWjJremloRWptWk5mUnJGUTk0RGhuX2k2c0oxTndEdlNXWE9iRWNRdE9sWDBkY3M2NlllRnpkeUhQbXpBa29lcVdIVmRhS3lIN3RiZmsySTJJRnNEdmZiWXJ3YnJLT2hlWjh3THlxWnNucFZkRGs2N1hqc1poOWphSHZoOE5nNXUzU1JOZjVCcw==
一、流程分析

image.png (32.87 KB, 下载次数: 0)
下载附件
请求接口
2025-7-5 15:02 上传
比较简单的一个流程,直接看一下具体的返回参数信息,怎么去获取和使用的

image.png (30.39 KB, 下载次数: 0)
下载附件
前置负载
2025-7-5 15:06 上传

image.png (56.12 KB, 下载次数: 0)
下载附件
前置请求返回值
2025-7-5 15:06 上传
可以明显地看到两个图片地址写着base64,说明要用base64进行编码处理后存储。剩下的就是token和
[color=]secretKey,
多次请求查看具体哪些内容是动态变化的,可以看到前置请求时固定的,只有一个ts时间戳在变化的
token和
[color=]secretKey
也是动态返回的内容

image.png (31.07 KB, 下载次数: 0)
下载附件
校验接口负载
2025-7-5 15:10 上传
很熟悉的有一个值就是token,剩下的有点不是很熟悉,感觉前两个是定值,我们直接跟栈来看看情况,刷新下断点

image.png (65.07 KB, 下载次数: 0)
下载附件
某网断点
2025-7-5 15:21 上传

image.png (109.35 KB, 下载次数: 0)
下载附件
某网加密参数
2025-7-5 15:22 上传
跟栈到这里就会发现很明显的参数,偷懒的也可以直接搜:keai这样我们来做参数逆向和python还原
二、加密参数分析与还原
经过分析我们知道需要处理的参数,先简单处理一下前置请求的内容
import requests
import time
import base64
import ddddocr
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
def main():
cookies = {
}
headers = {
}
json_data = {
'captchaType': 'blockPuzzle',
'clientUid': 'a91a6829f4537dd20eb15da3781158f2',
'ident': 'bc397c',
'captchaId': 'e4edc245-75f3-4e6c-be0a-32614280f304',
'ts': time.time() * 1000,
}
response = requests.post('脱敏处理', cookies=cookies, headers=headers,
json=json_data).json()
res = response['data']
secretKey = res['secretKey']
token = res['token']
jigsawImageBase64 = res['jigsawImageBase64']
originalImageBase64 = res['originalImageBase64']
gap_pic = base64.b64decode(jigsawImageBase64)
bg_pic = base64.b64decode(originalImageBase64)
with open('gap.jpg', 'wb') as f:
f.write(gap_pic)
with open('bg.jpg', 'wb') as f:
f.write(bg_pic)
det = ddddocr.DdddOcr(det=False, ocr=True, show_ad=False)
with open('gap.jpg', 'rb') as f:
target_bytes = f.read()
with open('bg.jpg', 'rb') as f:
background_bytes = f.read()
rest = det.slide_match(target_bytes, background_bytes, simple_target=True)
value = rest['target'][0]
print(rest)
print('识别到距离', value)
这样我们搞定了前置请求,获取到需要的内容,存储到了图片,用ddddocr获取滑块的距离

image.png (108.52 KB, 下载次数: 0)
下载附件
背景图
2025-7-5 15:50 上传

image.png (13.39 KB, 下载次数: 0)
下载附件
滑块图
2025-7-5 15:50 上传

image.png (31.89 KB, 下载次数: 0)
下载附件
识别距离
2025-7-5 15:52 上传
有了这些前提,我们来搞定后面的加密参数,先看看哪些内容是固定的,哪些是变化的

image.png (22.92 KB, 下载次数: 0)
下载附件
加密参数位置
2025-7-5 15:35 上传
还是挺明显的,都是在this里面,可以肯定的是pointjson是通过函数f实现的加密
var e = parseInt((this.moveBlockLeft || "").replace("px", ""));
e = 310 * e / parseInt(this.setSize.imgWidth);

image.png (30.17 KB, 下载次数: 0)
下载附件
滑动距离
2025-7-5 16:00 上传
这里的e应该是一个简单的滑动距离做了取整的处理,接下来就是用f进行加密,同时传入了两个参数,一个是序列化后的x和y,然后是动态的密钥,是前置接口的返回值

image.png (27.25 KB, 下载次数: 0)
下载附件
aes
2025-7-5 16:03 上传
很熟悉的一个加密,aes,ecb,都是熟悉的词汇(有不熟悉小伙伴可以看看密码学文章,前几篇帖子中都有直达链接,可以读一下),非常标准的加密,我们先用固定值测试一下结果。
def aesEncrypt(plaintext, aes_key):
if isinstance(plaintext, str):
plaintext = plaintext.encode('utf-8')
if isinstance(aes_key, str):
aes_key = aes_key.encode('utf-8')
cipher = Cipher(algorithms.AES(aes_key), modes.ECB(), backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plaintext) + padder.finalize()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
return base64.b64encode(ciphertext).decode('utf-8')
function f (t, e="XwKsGlMcdPMEhR1B") {
var n = l().enc.Utf8.parse(e)
, r = l().enc.Utf8.parse(t)
, i = l().AES.encrypt(r, n, {
mode: l().mode.ECB,
padding: l().pad.Pkcs7
});
return i.toString()
}

image.png (13.3 KB, 下载次数: 0)
下载附件
加密值对照
2025-7-5 16:08 上传
、
非常完美,搞定了这个加密参数,接下来看看其他的,还是多抓包分析看看哪些是不变的,要注意的是 returnUrl参数,我们搜索一下,因为他在this里面,不熟悉的大佬可以补补js基础

image.png (29 KB, 下载次数: 0)
下载附件
retrunurl
2025-7-5 16:12 上传
在这里看看下一个断点,重新刷新,他的操作大概就是先获取网站的链接,然后再通过k函数处理后赋值给e,进去看看k是怎么个事儿

image.png (24.95 KB, 下载次数: 0)
下载附件
k函数
2025-7-5 16:14 上传

image.png (37.63 KB, 下载次数: 0)
下载附件
裁剪拼接
2025-7-5 16:15 上传
很明显是一个分割重组的过程,很多参数就是来自于这里,说明这个也是动态变化的,只是相对于我们这次分析是静态的内容,可以简单复现一下
def k(t):
e = []
n = 0
r = {}
i = t.find("?")
t = t[i + 1:]
e = t.split("&")
n = len(e)
for o in range(n):
parts = e[o].split("=", 1)
if len(parts) > 1:
r[parts[0]] = parts[1]
else:
r[parts[0]] = ''
return r
这样我们就是实现了完整的第一次真实滑块的任务,完善一下代码内容:
def check(token, point_json):
json_data = {
'captchaType': 'blockPuzzle',
'pointJson': point_json,
'token': token,
'ident': 'bc397c',
'returnUrl': '自行修改'
}
response = requests.post('脱敏处理', cookies=cookies, headers=headers,
json=json_data).text

image.png (369.65 KB, 下载次数: 0)
下载附件
真实滑块结果
2025-7-5 16:20 上传
三、二次滑块分析与绕过
所谓虚实相生,真的里面有假的,当我们点开这个返回值中的链接,惊喜的发现又是一个滑块,惊不惊喜,意不意外;www

image.png (1.15 MB, 下载次数: 0)
下载附件
假滑块
2025-7-5 16:23 上传
大大的一个滑块,流程还在上面,我们再来一次检测一下。

image.png (43.49 KB, 下载次数: 0)
下载附件
滑动测试
2025-7-5 16:26 上传
我勒个豆啊,没一点反应,就一直加载图片信息,有点恶心了,这样我可以猜测一下,有没有可能是cookie加密,或者是前端,但是为啥没有检验呢获取请求信息呢?查一下网页的源码.

image.png (111.32 KB, 下载次数: 0)
下载附件
源码
2025-7-5 16:32 上传
大佬们可以自行阅读一下,我js也比较菜,大致我们可以分析出所有的操作都是在前端实现的,没有后端参与,说明什么,这是可以绕过的啊。我们让他滑动正确看看是什么情况(注意看包的加载,最好是借助抓包工具),我选择下一个xhr断点,当我们滑动正确时他断住了,我们往上跟一下看看

image.png (125.97 KB, 下载次数: 0)
下载附件
xhr
2025-7-5 16:39 上传

image.png (25.78 KB, 下载次数: 0)
下载附件
取值
2025-7-5 16:40 上传

image.png (32.11 KB, 下载次数: 0)
下载附件
元素
2025-7-5 16:40 上传
好家伙灯下黑,就在这里,取了一个id为v-value的属性的值,然后做一个拼接跳转。{:1_918:}
四、总结
入门级的网站大佬们可以玩玩,非常感谢我的小伙伴们@littlewhite11 ,还有几个偷着卷找不到{:1_932:},拓展了思路的滑块,绕过后面的步骤就是元素定位以及获取返回值,可以自行尝试,大佬们冲冲冲{:1_918:}