某网站登陆滑块逆向-2

本文最后更新于:2025年7月4日 下午

目标:aHR0cHM6Ly92OC5jaGFveGluZy5jb20v

初步调试

访问网站,查看网络,发现第一张图片是访问时直接加载进来的,主要通过两个接口confimage

conf接口是得到了一个时间戳t,暂时不知道干嘛的;captchaId这个参数是写死的,不变的,可以多调试几次就发现了

image接口返回的就比较明显了,两个图片的地址,所以解决问题的关键在这个接口发出的代码上

携带的参数如下:

1
2
3
4
5
6
7
8
9
10
11
params = {
"callback": "cx_captcha_function",
"captchaId": captchaId,
"type": "slide",
"version": "1.1.20",
"captchaKey": captchaKey,
"token": c_token,
"referer": "https://v8.chaoxing.com/",
"iv": iv,
"_": timestamp
}

接下来查看image接口的调用堆栈,关键方法打上断点

调试到_0x46287f()这个方法上,从这里发出conf请求,

从这个方法开始我始终没有找到核心的代码入口,没有找到image请求是从哪里发出来的,后面单步调试发现在这个方法的一条很长的语句中调用了,就是发出conf请求的上一句

1
_0x10e8ea && sessionStorage[_0x423663(0x23b)](_0x423663(0x132)) && sessionStorage['getItem']('serverTime') && new Date()[_0x423663(0x3b2)]() - sessionStorage[_0x423663(0x23b)]('localTime') < 0x3a980 ? _0x35a33e(sessionStorage['getItem'](_0x423663(0x287))) : (_0x27e462 = _0x10e8ea,_0x10e8ea = _0x46cf2d(_0x36bc0c, _0x3831d5, _0x4015b8[_0x423663(0x24d)][_0x423663(0x311)]),

整理一下:大致是判断session过期时间的,如果没过期就直接调用方法获取图片,如果过期了要重新请求时间

1
2
3
4
5
6
7
8
9
_0x10e8ea 
&& sessionStorage[_0x423663(0x23b)](_0x423663(0x132))
&& sessionStorage['getItem']('serverTime')
&& new Date()[_0x423663(0x3b2)]() - sessionStorage[_0x423663(0x23b)]('localTime') < 0x3a980 ? _0x35a33e(sessionStorage['getItem'](_0x423663(0x287))) :
(
_0x27e462 = _0x10e8ea,
_0x10e8ea = _0x46cf2d(
_0x36bc0c, _0x3831d5, _0x4015b8[_0x423663(0x24d)][_0x423663(0x311)]
),

下面分析_0x35a33e()这个方法

核心代码分析

这个方法很明显是发送image请求的地方,发现有3个参数需要生成captchaKeytokeniv,根据输出的值可以明显看出_0x4c771b这个方法是输出的哈希值,猜测为MD5()

验证后确实为MD5计算,再看需要用到的_0x11dbad()方法,通过调试后的结果可以明显看出这是返回一个uuid字符串,下面是混淆后的代码经过整理,生成的是uuid4风格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function _0x11dbad() {
var _0x5c52a4 = _0x2e675c;
// _0x12474f 是一个字符串(如 '0123456789abcdef')
var _0x12474f = _0x5c52a4(0x2f9); // 即一个十六进制字符集
var _0x55977e = [];
// 生成长度为 36(含 `-`)的 uuid 字符数组
for (var i = 0; i < 36; i++) {
_0x55977e[i] = _0x12474f.substr(Math.floor(16 * Math.random()), 1);
}
// 手动调整为符合 UUID v4 的标准格式
_0x55977e[14] = '4'; // UUID 版本位,必须为 '4'
_0x55977e[19] = _0x12474f.substr((parseInt(_0x55977e[19], 16) & 0x3 | 0x8), 1);
// 第 19 位:高 2 位设为 10(variant)
// 插入4个连字符,位置固定
_0x55977e[8] = _0x55977e[13] = _0x55977e[18] = _0x55977e[23] = '-';
return _0x55977e.join('');
}

python生成等效字符串只需要:

1
2
import uuid
uuid_str = str(uuid.uuid4())

多次调试就可以总结需要的三个参数的生成规则:_0x4e0309是传入值实际上就是使用conf接口传回来的时间戳,_0x3fedba是常量captchaId

1
2
3
4
5
6
7
8
9
captchaKey = _0x4c771b(_0x4e0309 + _0x11dbad()) 
= MD5(timestamp + uuid())

token = _0x4e0309 = _0x4c771b(_0x4e0309 + _0x3fedba + _0x589b78 + _0x422ded) + ':' + (parseInt(_0x4e0309) + 0x493e0) || ''
= MD5(timestamp + captchaId + 'slide' + captchaKey) + ':' + int(timestamp) + 300000

iv = _0x4015b8['IMAGE_VERIFY_TAG']
= _0x4c771b(_0x3fedba + _0x589b78 + Date[_0x5876dc(0x3d5)]() + _0x11dbad())
= MD5('qDG21VMg9qS5Rcok4cfpnHGnpf5LhcAv' + 'slide' + time.time() + uuid())

到这里就分析完了,最后流程很简单,conf接口获取一个服务器返回的时间戳,image通过时间戳生成各种参数获取图片地址和一个token,最后通过result请求发起验证。

代码

这次代码比较简短就贴了出来,仅供参考

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# target: aHR0cHM6Ly92OC5jaGFveGluZy5jb20v
import hashlib
import io
import json
import re
import time
import requests
import uuid
from PIL import Image, ImageDraw
from ddddocr import DdddOcr


def getTimestamp() -> str:
return str(int(time.time() * 1000))


def getUUID():
u = uuid.uuid4()
return str(u)


def getMD5(input_str: str) -> str:
res = hashlib.md5(input_str.encode('utf-8')).hexdigest()
return res


# 获取服务器返回时间
def getServerTime() -> str:
params1 = {
'callback': 'cx_captcha_function',
'captchaId': captchaId,
'_': int(now_time),
}
response1 = requests.get("https://captcha.chaoxing.com/captcha/get/conf", params=params1, headers=headers).text
res = re.findall(r'cx_captcha_function\((.*)\)', response1)
return str(json.loads(res[0])['t'])


# 获取图片地址和验证token
def getVerifyParams() -> (str, str, str, str):
server_time = getServerTime()
print(f"[+] 获取到的服务器时间: {server_time}")

# 先计算captchaKey
captchaKey = getMD5(server_time + getUUID())
print(f"[+] 计算得到的captchaKey: {captchaKey}")
# 再计算token
c_token = getMD5(server_time + captchaId + "slide" + captchaKey) + ':' + str(int(server_time) + 300000)
print(f"[+] 计算得到的c_token: {c_token}")
# 最后计算iv
iv = getMD5(captchaId + "slide" + now_time + getUUID())
print(f"[+] 计算得到的iv: {iv}")

params = {
"callback": "cx_captcha_function",
"captchaId": captchaId,
"type": "slide",
"version": "1.1.20",
"captchaKey": captchaKey,
"token": c_token,
"referer": "https://v8.chaoxing.com/",
"iv": iv,
"_": int(now_time) + 2
}

response = requests.get(
url="https://captcha.chaoxing.com/captcha/get/verification/image",
params=params,
headers=headers,
cookies=cookies
).text
# 提取 JSON 内容
json_data = json.loads(re.search(r'cx_captcha_function\((\{.*\})\)', response).group(1))
v_token = json_data["token"]
shade_image = json_data["imageVerificationVo"]["shadeImage"]
cutout_image = json_data["imageVerificationVo"]["cutoutImage"]
print(f"[+] 获取得到的用于验证的v_token: {v_token}")
print(f"[+] 获取得到的背景图片地址: {shade_image}")
print(f"[+] 获取得到的滑块图片地址: {cutout_image}")
return iv, v_token, shade_image, cutout_image


# 计算得到滑动距离
def getDistance(save_path="2_temp_rsult.jpg") -> (str, int):
iv, v_token, shade_image, cutout_image = getVerifyParams()
bg_image = requests.get(url=shade_image, cookies=cookies, headers=headers).content
slice_image = requests.get(cutout_image, cookies=cookies, headers=headers).content
det = DdddOcr(det=False, ocr=False, show_ad=False)
res = det.slide_match(slice_image, bg_image, 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_image))
draw = ImageDraw.Draw(bg_image)
draw.rectangle([left, top, right, bottom], outline="red", width=2)
bg_image.save("2_temp_rsult.jpg")
print(f"[+] 已保存标注后的图片到: {save_path}")
return iv, v_token, res["target"][0]
return res


def passVerify():
iv, v_token, distance = getDistance()
# 模拟滑动轨迹等参数(举例)
params = {
"callback": "cx_captcha_function",
"captchaId": captchaId,
"type": "slide",
"token": v_token,
"textClickArr": '[{"x":' + str(distance) + '}]',
"coordinate": '[]',
"runEnv": "10",
"version": "1.1.20",
"t": "a",
"iv": iv,
"_": now_time
}
response = requests.get(
url="https://captcha.chaoxing.com/captcha/check/verification/result",
cookies=cookies,
headers=headers,
params=params
).text
print(f"[+] 验证结果: {response}")


if __name__ == "__main__":
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/138.0.0.0"
"Safari/537.36",
"Referer": "https://v8.chaoxing.com/",
"Content-Type": "text/plain;charset=UTF-8",
"Sec-Ch-Ua-Platform": "Windows",
"Accept": "*/*"
}
# 获取服务器返回的 cookie(字典格式)
cookies = requests.get(url="https://v8.chaoxing.com/", headers=headers).cookies.get_dict()
captchaId = "qDG21VMg9qS5Rcok4cfpnHGnpf5LhcAv"
now_time = getTimestamp()
passVerify()

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