腾讯行为验证码 滑块拼图验证

抓包分析

测试aid为 2000000008

image.png

请求了https://t.captcha.qq.com GET方法

parms参数为

1
aid=2000000008&protocol=https&accver=1&showtype=full&ua=TW96aWxsYS81LjAgKExpbnV4OyBBbmRyb2lkIDEzOyBNT01PRSBCdWlsZC9US1ExLjIyMDgyOS4wMDI7IHd2KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBWZXJzaW9uLzQuMCBDaHJvbWUvMTExLjAuNTU2My4xMTYgTW9iaWxlIFNhZmFyaS81MzcuMzYgWFdFQi8xMTEwMDUzIE1NV0VCU0RLLzIwMjMxMTA1IE1NV0VCSUQvNTI3MyBNaWNyb01lc3Nlbmdlci84LjAuNDQuMjUwMigweDI4MDAyQzM3KSBXZUNoYXQvYXJtNjQgV2VpeGluIE5ldFR5cGUvV0lGSSBMYW5ndWFnZS96aF9DTiBBQkkvYXJtNjQ%3D&noheader=1&fb=1&aged=0&enableAged=0&enableDarkMode=0&grayscale=1&clientype=2&userLanguage=zh-cn&cap_cd=&uid=&wxLang=&lang=zh-cn&entry_url=&elder_captcha=0&js=%2Ftcaptcha-frame.921213.js&login_appid=&wb=1&subsid=1&callback=_aq_99695&sess=

拆开一条条看

1
2
3
4
5
6
7
aid=2000000008
protocol=https accver=1 showtype=full
ua=TW96aWxsYS81LjAgKExpbnV4OyBBbmRyb2lkIDEzOyBNT01PRSBCdWlsZC9US1ExLjIyMDgyOS4wMDI7IHd2KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBWZXJzaW9uLzQuMCBDaHJvbWUvMTExLjAuNTU2My4xMTYgTW9iaWxlIFNhZmFyaS81MzcuMzYgWFdFQi8xMTEwMDUzIE1NV0VCU0RLLzIwMjMxMTA1IE1NV0VCSUQvNTI3MyBNaWNyb01lc3Nlbmdlci84LjAuNDQuMjUwMigweDI4MDAyQzM3KSBXZUNoYXQvYXJtNjQgV2VpeGluIE5ldFR5cGUvV0lGSSBMYW5ndWFnZS96aF9DTiBBQkkvYXJtNjQ=
noheader=1 fb=1 aged=0 enableAged=0 enableDarkMode=0
grayscale=1&clientype=2&userLanguage=zh-cn cap_cd= uid= wxLang=&lang=zh-cn
entry_url= elder_captcha=0 js=/tcaptchaframe.921213.js
login_appid= wb=1 subsid=1 callback=_aq_99695 sess=

有三个关键参数 分别是aid ua callback

ua为base64后的 callback决定了返回的json起始值

返回值

1
_aq_99695({"state":1,"ticket":"","capclass":"1","subcapclass":"10144","src_1":"cap_union_new_show","src_2":"template/new_placeholder.html","src_3":"template/new_slide_placeholder.html","sess":"s0XdZ2kavzZ4lMrQ4uwCfaXhEbuMZNR2MsyBPJmeK4mVInREcH1vqyhSe7G6MlWm-fTKVGCdkqkf3jEmZXaOMnELsMWZj9sgB5lexBUxzPD08ERn-Z3pxYIgBcxXvbds63HGgnY9HBQaI2hB3L5HYHdcwqM1Rwy4HBGElODCyNfcYXmJoqXO2aal0lW3-P3JTgy0yay97hg2tyNVSSlmVJft--TJdt2pquRgRgjg-dP3wj1tCyp7Rv-aNVcOcOfBsdiF58Uo3vDppW4ntaagLkR3fvcKynqh_jCnKswt892qqqAl03wq7-9mJFprEu3hdehv3gfitjm6VIuPsulX9lq658W6yS4H0oB-xMXDqJU2p7EYddI2mVgnZxrC3VBSyaosj5Cng5eSrhtBILTLTBIWKGXpPokIYStV-EMjLJiVE*","randstr":"","sid":"7198604069692559360","log_js":"","extra":"{\"rainbow\":\"{\\n  \\\"samplingRate\\\":0.1,\\n  \\\"apiReportThrottle\\\":200,\\n  \\\"sourceReportThrottle\\\":300,\\n  \\\"loadReportThrottle\\\":1000,\\n  \\\"sampleAppidList\\\":[\\\"2053860784\\\",\\\"2091569087\\\"]\\n}\"}","data":{"comm_captcha_cfg":{"tdc_path":"/tdc.js?app_data=7198604069692559360&t=1213916534","feedback_url":"https://support.qq.com/products/304457","pow_cfg":{"prefix":"2c4eae921213dcf2#","md5":"ef7d702689e9682a8a6f93b695c6383b"}},"dyn_show_info":{"lang":"zh-cn","instruction":"拖动下方滑块完成拼图","bg_elem_cfg":{"sprite_pos":[0,0],"size_2d":[672,480],"img_url":"/cap_union_new_getcapbysig?img_index=1&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*"},"fg_elem_list":[{"id":3,"sprite_pos":[0,422],"size_2d":[672,32],"init_pos":[0,422]},{"id":1,"sprite_pos":[140,490],"size_2d":[120,120],"init_pos":[50,109],"move_cfg":{"track_limit":"x>=50&&x<=552","move_factor":[1,0],"data_type":["DynAnswerType_POS"]}},{"id":2,"sprite_pos":[0,490],"size_2d":[130,70],"init_pos":[45,402],"move_cfg":{"track_limit":"x>=50&&x<=552","move_factor":[1,0]},"type":"slider"}],"fg_binding_list":[{"master":1,"slave":2,"bind_type":"DynBindType_DX_DX","bind_factor":1},{"master":2,"slave":1,"bind_type":"DynBindType_DX_DX","bind_factor":1}],"sprite_url":"/cap_union_new_getcapbysig?img_index=0&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*","color_scheme":"#26C067"}},"uip":"11.4.5.14"})

去掉_aq开头进行格式化

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
{
"state": 1,
"ticket": "",
"capclass": "1",
"subcapclass": "10144",
"src_1": "cap_union_new_show",
"src_2": "template/new_placeholder.html",
"src_3": "template/new_slide_placeholder.html",
"sess": "s0XdZ2kavzZ4lMrQ4uwCfaXhEbuMZNR2MsyBPJmeK4mVInREcH1vqyhSe7G6MlWm-fTKVGCdkqkf3jEmZXaOMnELsMWZj9sgB5lexBUxzPD08ERn-Z3pxYIgBcxXvbds63HGgnY9HBQaI2hB3L5HYHdcwqM1Rwy4HBGElODCyNfcYXmJoqXO2aal0lW3-P3JTgy0yay97hg2tyNVSSlmVJft--TJdt2pquRgRgjg-dP3wj1tCyp7Rv-aNVcOcOfBsdiF58Uo3vDppW4ntaagLkR3fvcKynqh_jCnKswt892qqqAl03wq7-9mJFprEu3hdehv3gfitjm6VIuPsulX9lq658W6yS4H0oB-xMXDqJU2p7EYddI2mVgnZxrC3VBSyaosj5Cng5eSrhtBILTLTBIWKGXpPokIYStV-EMjLJiVE*",
"randstr": "",
"sid": "7198604069692559360",
"log_js": "",
"extra": "{\"rainbow\":\"{\\n \\\"samplingRate\\\":0.1,\\n \\\"apiReportThrottle\\\":200,\\n \\\"sourceReportThrottle\\\":300,\\n \\\"loadReportThrottle\\\":1000,\\n \\\"sampleAppidList\\\":[\\\"2053860784\\\",\\\"2091569087\\\"]\\n}\"}",
"data": {
"comm_captcha_cfg": {
"tdc_path": "/tdc.js?app_data=7198604069692559360&t=1213916534",
"feedback_url": "https://support.qq.com/products/304457",
"pow_cfg": {
"prefix": "2c4eae921213dcf2#",
"md5": "ef7d702689e9682a8a6f93b695c6383b"
}
},
"dyn_show_info": {
"lang": "zh-cn",
"instruction": "拖动下方滑块完成拼图",
"bg_elem_cfg": {
"sprite_pos": [0, 0],
"size_2d": [672, 480],
"img_url": "/cap_union_new_getcapbysig?img_index=1&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*"
},
"fg_elem_list": [
{
"id": 3,
"sprite_pos": [0, 422],
"size_2d": [672, 32],
"init_pos": [0, 422]
},
{
"id": 1,
"sprite_pos": [140, 490],
"size_2d": [120, 120],
"init_pos": [50, 109],
"move_cfg": {
"track_limit": "x>=50&&x<=552",
"move_factor": [1, 0],
"data_type": ["DynAnswerType_POS"]
}
},
{
"id": 2,
"sprite_pos": [0, 490],
"size_2d": [130, 70],
"init_pos": [45, 402],
"move_cfg": { "track_limit": "x>=50&&x<=552", "move_factor": [1, 0] },
"type": "slider"
}
],
"fg_binding_list": [
{
"master": 1,
"slave": 2,
"bind_type": "DynBindType_DX_DX",
"bind_factor": 1
},
{
"master": 2,
"slave": 1,
"bind_type": "DynBindType_DX_DX",
"bind_factor": 1
}
],
"sprite_url": "/cap_union_new_getcapbysig?img_index=0&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*",
"color_scheme": "#26C067"
}
},
"uip": "11.4.5.14"
}

需要注意的参数为

1
2
3
4
5
6
7
8
9
10
11
12
"tdc_path": "/tdc.js?app_data=7198604069692559360&t=1213916534"

"pow_cfg": {
"prefix": "2c4eae921213dcf2#",
"md5": "ef7d702689e9682a8a6f93b695c6383b"
}

"img_url": "/cap_union_new_getcapbysig?img_index=1&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*"

"sprite_url": "/cap_union_new_getcapbysig?img_index=0&image=02a027000000001a0000001eb7f57e512fc3&sess=s0S6ZReNGNUQ43mWmkryTKQ-GtePsp7eN7mZyFCs7Vu2_IwXtvWdMKhUTZJDs71_GuUZiO_4zCcQ-N4FcqIvY4mEOPRNIyyuaUuB7Qilkem_02OF5uRqtXakeQteZuF0VcmxW177S1Sa1XrHPKdR3xaHwdAnNKrqqoE46B3roBjidIYCdEeOp18snNq4bpz_B405I6acSRC7SSOBzFC0xQAOIMiZhQHsnY8h67lLuEbesyJCjzPfVyahFwTsQiR2K98OisU5xWDf73uc0QEgBcBEfYky8P3FLbfFPxttlFPNYsqdWHM02ibttaVaRIn_30wyKOLeYSRqagkZkK8BxE-twNQUY36xgYyeJ3ZR6QUs87YMvO8cSydJRURGBcVffG5IsmgCw3erjF64ap_3CeXLx4trYeZqt9xFDXj3bI-Bo*"

"sess": "s0XdZ2kavzZ4lMrQ4uwCfaXhEbuMZNR2MsyBPJmeK4mVInREcH1vqyhSe7G6MlWm-fTKVGCdkqkf3jEmZXaOMnELsMWZj9sgB5lexBUxzPD08ERn-Z3pxYIgBcxXvbds63HGgnY9HBQaI2hB3L5HYHdcwqM1Rwy4HBGElODCyNfcYXmJoqXO2aal0lW3-P3JTgy0yay97hg2tyNVSSlmVJft--TJdt2pquRgRgjg-dP3wj1tCyp7Rv-aNVcOcOfBsdiF58Uo3vDppW4ntaagLkR3fvcKynqh_jCnKswt892qqqAl03wq7-9mJFprEu3hdehv3gfitjm6VIuPsulX9lq658W6yS4H0oB-xMXDqJU2p7EYddI2mVgnZxrC3VBSyaosj5Cng5eSrhtBILTLTBIWKGXpPokIYStV-EMjLJiVE*"

img_url与sprite_url分别代表背景图和缺口图

cap_union_new_getcapbysig.png
cap_union_new_getcapbysig2.png

最终提交验证的地址为 https://t.captcha.qq.com/cap_union_new_verify

提交的参数格式为form-data

1
collect=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&tlg=3416&eks=KT0GxYUSVeYeeXHunYofyH3jHR7V3YVvp0/XHzAUjMC92n1V5230gK6MxAaCp2fYMHpWsYx/WeiR1S6sO1UBL3NofUZfsO8CQqoAQNy2WyUTeiKpyBk0ZeXM2JI8FX+N0xk5Rr70QiWX6zKyq8P8S0yoQMSZFf14YZQGw68dzXnryUtFbB3l3uW/XUjrYctEqThF2x7UOzqbDALD1nBeqsxE8U84XKJ8lR6syYdd/pI=&sess=s0XdZ2kavzZ4lMrQ4uwCfaXhEbuMZNR2MsyBPJmeK4mVInREcH1vqyhSe7G6MlWm-fTKVGCdkqkf3jEmZXaOMnELsMWZj9sgB5lexBUxzPD08ERn-Z3pxYIgBcxXvbds63HGgnY9HBQaI2hB3L5HYHdcwqM1Rwy4HBGElODCyNfcYXmJoqXO2aal0lW3-P3JTgy0yay97hg2tyNVSSlmVJft--TJdt2pquRgRgjg-dP3wj1tCyp7Rv-aNVcOcOfBsdiF58Uo3vDppW4ntaagLkR3fvcKynqh_jCnKswt892qqqAl03wq7-9mJFprEu3hdehv3gfitjm6VIuPsulX9lq658W6yS4H0oB-xMXDqJU2p7EYddI2mVgnZxrC3VBSyaosj5Cng5eSrhtBILTLTBIWKGXpPokIYStV-EMjLJiVE*&ans=[{"elem_id":1,"type":"DynAnswerType_POS","data":"465,109"}]&pow_answer=2c4eae921213dcf2#290&pow_calc_time=50

参数分析

参数有点多 精简一下看重点

1
2
3
4
5
6
collect=
tlg=3416 // tlg为collect的长度
eks=
sess= //为第一次请求获得的sess参数
ans=[{"elem_id":1,"type":"DynAnswerType_POS","data":"465,109"}]
pow_answer=2c4eae921213dcf2#290&pow_calc_time=50

其中collect与eks为tdc.js计算得到 collect为getData参数 eks为getInfo参数

提交验证无误后服务器会返回

1
2
3
4
5
6
7
{
"errorCode": "0",
"randstr": "@IFs",
"ticket": "t03tc7qmJ5J7hv1nKoMXqlqWBy3rNdOlapvSugmPWgQc9Mq_pRbuc8nOThAB2zVX5S6omlbjqFw9HJ9HBdl4ABQ30Yqy81dBUenH8qGTIVt-Ky95644-h-gbzhsz8ECCczh8iFjcuYeL7gzlWqER1p7b1wQs9o4ESs3peLX7DHNytLwpUti_l-pRB_tyBdXmPJq",
"errMessage": "",
"sess": ""
}

所需要的参数randstr ticket


tds使用腾讯自家的TENCENT_CHAOS_VM技术 这部分可以参考下面链接

分析参考:
Zhihu: https://zhuanlan.zhihu.com/p/621903162
JamzumSum: https://zzsblog.top/tcaptcha.html

模拟计算

这里咱们使用Python ChaosVM Executor 来模拟执行tdc.js获取collect与eks参数

项目地址: https://github.com/aioqzone/pychaosvm

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
//参考代码 注释由Ai生成 
def ChaosVM_Exm(vmjs, uip: str, mouse_track) -> tuple:
"""
与ChaosVM交互的函数,通过给定的虚拟机JavaScript代码、用户IP地址和鼠标追踪信息,来获取数据。

参数:
- vmjs: 虚拟机的JavaScript代码,用于在ChaosVM环境中执行。
- uip: 用户的IP地址,作为与ChaosVM交互的一部分。
- mouse_track: 鼠标追踪信息,包含用户的鼠标活动数据。

返回值:
- 返回一个元组,包含collect, eks。
"""
# 构造用户代理和准备与ChaosVM交互的环境
ua = user_agent
tdc = prepare(js_vm=vmjs, ip=uip, ua=ua, mouse_track=mouse_track)

# 获取ChaosVM的信息
info = tdc.getInfo(None)
# 确保info不为空,并且info中的"info"键对应的值可以被转换为字符串
assert info.__dict__
assert str(info["info"])

# 处理info中的"info"键对应的字符串,移除单引号
eks = str(info["info"]).replace("'", "")

# 从ChaosVM收集数据,并进行URL解码
collect = tdc.getData(None, True)
collect = unquote(collect)

# 确保收集到的数据满足特定的长度要求,并且不包含"chaosvm"字符串
assert len(collect) > 4
assert "chaosvm" not in collect

return collect, eks

鼠标轨迹算法参考

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
def imitate_drago(x1: int, x2: int, y: int) -> t.Tuple[t.List[int], t.List[int]]:
"""
模拟拖拽动作产生的x和y方向上的抖动效果。

参数:
x1: int - 拖拽开始点的x坐标,必须位于(0, x2)之间。
x2: int - 拖拽结束点的x坐标,必须位于(x1, +∞)之间。
y: int - 拖拽过程中垂直方向上的固定值,表示在y轴上没有移动。

返回值:
t.Tuple[t.List[int], t.List[int]]: 一个元组,包含两个列表。
第一个列表表示x轴上的抖动路径,第二个列表表示y轴上的抖动路径。
"""

# 确保x1位于(0, x2)之间,y大于0
assert 0 < x1 < x2, (x1, x2)
assert 0 < y, y

# 随机生成抖动点的数量在50到64之间
n = randint(50, 64)
# 生成y轴上的随机抖动值,主要集中在y值附近
noise_y = choices([y - 1, y + 1, y], [0.1, 0.1, 0.8], k=n)

# 根据抖动点数量,确定x轴上的抖动范围
if n >= 51:
noise_x = [0] # 起始抖动点固定为0
noise_x += choices(list(range(-3, 4)), k=max(n - 51, 0)) # 补充长尾抖动
else:
noise_x = [] # 如果点数少于51,则不添加长尾抖动

# 添加中间区域的抖动
noise_x += choices(list(range(-2, 3)), k=30)
noise_x += choices(list(range(-1, 2)), k=19)
noise_x.append(0) # 结尾抖动点固定为0

# 计算x轴上各点的均匀分布,再进行随机抖动
d, lsv = (x1 - x2) / (n - 1), x1
for i in range(n):
noise_x[i] += round(lsv)
lsv += d
noise_x.sort() # 确保x轴上的点按顺序排列
return noise_x, noise_y

现在解决了collect和eks 来看后面的ans 可以看到ans的DynAnswerType_POS就是滑块正确位置

X坐标465 Y坐标109 滑块的Y坐标为固定 可以从"init_pos": [50, 109]提取Y坐标

这里再介绍一个Python库 DdddOcr 带带弟弟OCR通用验证码识别

项目地址: https://github.com/sml2h3/ddddocr

1
2
3
4
5
6
7
8
9
10
11
12
Demo代码

slide = ddddocr.DdddOcr(det=False, ocr=False)

with open('target.jpg', 'rb') as f:
target_bytes = f.read()
with open('background.jpg', 'rb') as f:
background_bytes = f.read()

res = slide.slide_match(target_bytes, background_bytes, simple_target=True)

print(res)

传入背景图与缺口图即可识别坐标 但是因为缺口图有边框透明背景 识别率并不是很满意

这里加入一些图像操作 对缺口图进行暴力裁切以符合匹配条件

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
def cropmatch_imagem(reference_image: bytes, target_image: bytes) -> int:
"""
:param reference_image: 参考图像的字节串
:param target_image: 目标图像的字节串
:return: 匹配结果的目标索引
"""

# 加载目标图像
hy_image_bytes = BytesIO(target_image)
image = Image.open(hy_image_bytes)

# 检查图像尺寸以确保裁剪操作不会超出边界
if image.size[0] < 255 or image.size[1] < 600:
raise ValueError("目标图像尺寸不足以进行裁剪操作。")

# 裁剪图像
cropped_image = image.crop((145, 490, 255, 600))

# 保存裁剪后的图像为字节串
cropped_image_bytes = BytesIO()
cropped_image.save(cropped_image_bytes, format="PNG")
cropped_image_bytes = cropped_image_bytes.getvalue()
# 执行图像匹配
ddres = det.slide_match(cropped_image_bytes, reference_image, simple_target=True)

# 返回匹配结果
return ddres["target"][0]

图像匹配的问题解决 继续看下一个参数

1
2
3
4
5
6
pow_answer=2c4eae921213dcf2#290&pow_calc_time=50
// 从第一个返回参数拿到
"pow_cfg": {
"prefix": "2c4eae921213dcf2#",
"md5": "ef7d702689e9682a8a6f93b695c6383b"
}

腾讯这边的pow算法为prefix#步数 计算MD5 匹配给出的md5

这里 2c4eae921213dcf2#290 得到的MD5即为->ef7d702689e9682a8a6f93b695c6383b

直接上代码

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
// 注释由Ai生成
def pow_solve(prefix, pow_md5, timeout: float = 30.0):
"""
解决工作量证明问题。

参数:
- prefix: 字符串前缀,用于生成最终的答案。
- pow_md5: 目标MD5字符串,需要找到一个数字与前缀组合,其MD5值等于此目标字符串。
- timeout: 超时时间,以秒为单位,默认为30秒。

返回值:
- pow_answer: 解决工作量证明后的字符串答案,即前缀加上找到的数字。
- pow_calc_time: 计算工作量证明所花费的时间,以毫秒为单位,至少为50ms。
"""
# 初始化nonce为前缀的字符串形式,并将目标MD5转换为小写。
nonce = str(prefix).encode()
target = pow_md5.lower()
start = time() # 记录开始时间。
cnt = 0 # 初始化计数器。

# 在超时时间内循环,尝试不同的计数值直到找到匹配目标MD5的哈希值。
while time() - start < timeout:
if md5(nonce + str(cnt).encode()).hexdigest() == target:
print("PowMd5:", target)
break
cnt += 1

# 生成最终的答案字符串,并计算花费的时间(确保最低为50ms)。
pow_answer = prefix + str(cnt)
pow_calc_time = max(int((time() - start) * 1e3), 50)

return pow_answer, pow_calc_time

至此参数准备完成

参考代码

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
async def TencentCAPTCHA(self) -> bool:
ua = base64.b64encode(user_agent.encode()).decode('utf-8')
callrandom = randint(10000, 99999)
callback = f"_aq_{callrandom}"
url = 'https://t.captcha.qq.com/cap_union_prehandle'
params = {
'aid': 2000000008, 'protocol': 'https',
'accver': 1, 'showtype': 'full',
'ua': ua, 'noheader': 1, 'fb': 1, 'aged': 0,
'enableAged': 0, 'enableDarkMode': 0,
'grayscale': 1, 'clientype': 2, 'userLanguage': 'zh-cn', 'cap_cd': '',
'uid': '', 'wxLang': '', 'lang': 'zh-cn', 'entry_url': '', 'elder_captcha': 0,
'js': '/tcaptcha-frame.114514.js',
'login_appid': '', 'wb': 1, 'subsid': 1,
'callback': callback, 'sess': '',
}
get_prehandle = await self.FetchData(url, method='GET', params=params, json=False)
allinfo = json.loads(get_prehandle.text[10:-1])
sess = allinfo['sess']
uip = allinfo['uip']
tdc_path = allinfo['data']['comm_captcha_cfg']['tdc_path']
pow_cfg = allinfo['data']['comm_captcha_cfg']['pow_cfg']['prefix']
pow_md5 = allinfo['data']['comm_captcha_cfg']['pow_cfg']['md5']
y_line = int(allinfo['data']['dyn_show_info']['fg_elem_list'][1]['init_pos'][1])
img_url = allinfo['data']['dyn_show_info']['bg_elem_cfg']['img_url']
sprite_url = allinfo['data']['dyn_show_info']['sprite_url']

url = f'https://t.captcha.qq.com{img_url}'
get_img = await self.FetchData(url, method='GET', json=False)
image_bytes = get_img.content
url = f'https://t.captcha.qq.com{sprite_url}'
get_img = await self.FetchData(url, method='GET', json=False)
target_image_bytes = get_img.content
x_line = cropmatch_imagem(image_bytes, target_image_bytes)
ans = f"{x_line},{y_line}"
xs, ys = imitate_drago(50, x_line, y_line)
mouse_track = list(zip(xs, ys))
url = f'https://t.captcha.qq.com{tdc_path}'
tdcjs = await self.FetchData(url, method='GET', json=False)
collect, eks = ChaosVM_Exm(tdcjs.text, uip, mouse_track)
print("Coordinate:", ans)
url = "https://t.captcha.qq.com/cap_union_new_verify"
pow_answer, pow_calc_time = solve_workloado(pow_cfg, pow_md5)
ans = json.dumps([{"elem_id": 1, "type": "DynAnswerType_POS", "data": ans}]).replace(' ', '')
form_data = {
'collect': collect,
'tlg': len(collect),
'eks': eks,
'sess': sess,
'ans': ans,
'pow_answer': pow_answer,
'pow_calc_time': pow_calc_time
}
verify = await self.FetchData(url, method='POST', data=form_data)
print("Slider:", verify)
  • FetchData为封装的httpx函数