某网站极验4滑块验证码逆向分析
文章目录
- 1. 写在前面
- 2. 接口分析
- 3. w逆向分析
- 4. JSON参数分析
- 5. 距离识别
- 6. RSA纯算还原
- 7. AES纯算还原
【🏠作者主页】:吴秋霖
【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!
【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》
未来作者会持续更新所用到、学到、看到的技术知识!包括但不限于:各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章
作者声明:文章仅供学习交流与参考!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!
1. 写在前面
极验相关的验证码网上公开的教程很多,这也是每一位爬虫工程师职业生涯中几乎都会遇到的(就跟瑞数一样总有一天你会在职场与其打交道
)也是很多学习Web逆向协议过验证的小伙伴必攻克练手的必经之路。前段时间作者也是看了一眼比较抵触的多个验证码系列,写的验证码相关的文章比较少。类型可以说是五花八门
,复杂的要去训练,没有时间去折腾(难的也不会
)。看也就是看下JS层面的一些加密,这里我们也来分析一下它的流程(适合新手小伙伴参考练习
)
分析网站:
6Ieq5bex5om+5LiA5Liq77yM5b2T5L2g5bCd6K+V6Kej56CB55qE6L+Z5LiA5Yi744CC5oOz5ZGK6K+J5L2g5Yir5Y235LqG
2. 接口分析
整个验证的流程比较简洁,两个接口,一个load
一个verify
。先看一下load
接口请求参数与响应数据如下:
{"status": "success","data": {"lot_number": "80ff732c046e471286a596376f23e6ba","captcha_type": "slide","slice": "pictures/v4_pic/slide_2024_09_02/e9d1dec400/slide/da4cb40154354b98b0aabc59a516407b.png","bg": "pictures/v4_pic/slide_2024_09_02/e9d1dec400/bg/da4cb40154354b98b0aabc59a516407b.png","ypos": 88,"arrow": "arrow_1","js": "/js/gcaptcha4.js","css": "/css/gcaptcha4.css","static_path": "/v4/static/v1.8.9-2c5e49","gct_path": "/v4/gct/gct4.5a2e755576738ba0499d714db4f1c9e0.js","show_voice": false,"feedback": "","logo": false,"pt": "1","captcha_mode": "adaptive","guard": false,"check_device": true,"language": "en-us","lang_reverse": false,"custom_theme": {"_style": "flat","_color": "hsla(166, 100%, 35%, 1)","_gradient": "linear-gradient(180deg, hsla(166, 100%, 35%, 1) 0%, hsla(166, 100%, 35%, 1) 100%)","_hover": "linear-gradient(180deg, hsla(166, 100%, 30%, 1) 0%, hsla(166, 100%, 30%, 1) 100% )","_brightness": "system","_radius": "4px"},"pow_detail": {"version": "1","bits": 0,"datetime": "2025-06-14T14:46:04.908506+08:00","hashfunc": "md5"},"payload": "AgFD8gWUUuHFx-XvpP7J2eXmFGG988RJA9XWIifIhVfD_3urF6TwX10q6TrWNlmoLrmnDZXhh-Ds0tvYuF6iz5alWQDqnpl0rqqdGpTnlPBV0BX8BWJ5g0YFxJt1IOoQVcRgUKRcBVWhr05ylkkJZXmzCjWL1dDhSlKM126f2CZbUhZx485twyTTIgg6TxkHp6RFiEexFFTfAtnhmMzgKU23kd_SmUIpkhKKF4bwPjaDya1ARBt-LMTEJSFl0XZKUXBONNi2GkE0BIbNVEDSLi4QK7Zj55XdDCTUuBkmYr5YxPXUERlbVg3UZyx0Y9EXEHMgaUL7CySS6wrF3_lx4x_5wMrC7FQY9RMZ_mIGUpjk3HIc5OQIQlOZEVDlvEIm6dwESxLJZha5UI92v7w-w9ceoaQGo1oBJ1uqRmPxQsHpmDS77OCNf8r2ymn4nt-mtL4ee2U0yyMX1py5BM1gNWR8kAEVvFWcwwWObMFjMtWSuP4yT193BgbmfCwK_N1dnpCcMCVppkdkfgUQ_Eay_oiTfpR17q7G1gGG52U-NLWM5wgGVTNzmw83Js2pPfl9zQB3By9VIzmwvNqJzsQUg3H_2WyII9YZ5BPAxqe6j3W3-uL1UCyrx4UoYY-Pp5JugjrxQ26vBWE7B5cWc3poPHwTa5nXmyiS3QbZYc_qw3K_YyF96yTL2AVq2TzR6DOiqM1cb6HloK2rVD8mRquqbXXA7rvdKTlYswSxJXSDD6K-bg8CMQ_br4AnxmfC1U_blfAwWSZnqSyoHb9g8X3ejeeW1qxkEYIAcG6bmKhb8gMWTSV1I3WtU7sBLqc5o4O4Qm4q3Xl3gN8bPsh4RSQXJ5M8QudVdBWid0iR5afT2-aL0ZAGOb7a1ooFhrpFndEkT7cHFPG-mMp-HHjY8giZwK8minfPqGKxdOfsQbUqnP8=","process_token": "233085ee7f960fae79d07dcd40515833e0367c2e4edc1ba4bc5b530dc5e7600b","payload_protocol": 1}
}
上面JSON
数据是load
接口的响应,包含了一些图片的信息跟后续验证接口二次携带请求的参数字段。然后看一下verify
验证接口提交的参数,如下所示:
标记出来的基本都是load
接口响应的参数,都是后续提交验证过程中要拿来使用的,唯一要逆向解决的参数就是w
,所以我们需要知道这个w
的明文数据是什么然后通过什么加密方式出来的
3. w逆向分析
首先需要定位这个w
生成的位置(搜索w:、从下往上看栈两三个、搜其他特征明显的请求参数
)均可以帮助我们定位到相关位置处,如下所示:
现在都这么抽象了么,关键的一些变量、函数、参数那些看起来使用了Unicode
字符来替代。这种如果觉得硬看费劲的话可以使用AST
来辅助还原,还原流程大致如下所示:
还原这个混淆JS的关键策略就是构建映射表
根据实际的分析情况去补充跟过滤
接下来我们来看w
参数是怎么生成的,把生成这个参数的这一行代码拿下来,如下所示:
_ᖆᕶᖙᖁ = (0,_ᕴᖁᕹᖄ[_ᖃᕾᕶᖂ(67)])(_ᖁᖉᕿᖈ[_ᕹᖃᕶᕿ(67)][_ᕹᖃᕶᕿ(559)](_ᖘᖄᖁᕿ), _ᕴᖙᖄᖘ)
_ᖆᕶᖙᖁ
就是加密之后的结果,_ᖘᖄᖁᕿ
是加密的明文JSON数据,然后做了一个JSON.stringify
的操作,如下所示:
这里我们核心看_ᕴᖁᕹᖄ[_ᖃᕾᕶᖂ(67)]
,这个函数我们直接跟进去看看是什么,通过调用它传递了JSON数据生成的w
值
这里我们把上面跳转到的核心JS代码拿下来,再联动网页调试来分析一下流程,如下所示:
_ᖘᖄᖁᕿ[_ᖃᕾᕶᖂ(67)] = function _ᖆᕶᖙᖁ(_ᖘᖄᖁᕿ, _ᖁᖆᕸᕸ) {var _ᖃᕾᕶᖂ = _ᖘᖈᖙᖃ.$_CL, _ᖁᕷᕹᖄ = ["$_DCAAk"].concat(_ᖃᕾᕶᖂ), _ᕹᖃᕶᕿ = _ᖁᕷᕹᖄ[1];_ᖁᕷᕹᖄ.shift();var _ᖃᕹᖄᖚ = _ᖁᕷᕹᖄ[0];var _ᖆᖘᕶᕹ = _ᖁᖆᕸᕸ[_ᖃᕾᕶᖂ(439)];if (!_ᖆᖘᕶᕹ[_ᖃᕾᕶᖂ(604)] || _ᖃᕾᕶᖂ(183) === _ᖆᖘᕶᕹ[_ᕹᖃᕶᕿ(604)])return _ᕴᖙᖄᖘ[_ᕹᖃᕶᕿ(67)][_ᖃᕾᕶᖂ(921)](_ᖘᖄᖁᕿ);var _ᖀᕷᖚᖉ = (0,_ᕷᕸᖉᕷ[_ᕹᖃᕶᕿ(93)])(), _ᖂᖗᕿᖁ = new (_ᕷᕸᖉᕷ[_ᕹᖃᕶᕿ(30)])([_ᖃᕾᕶᖂ(873), _ᕹᖃᕶᕿ(898)]), _ᖄᖈᕸᖃ = {1: {symmetrical: _ᕿᕶᖆᖃ[_ᕹᖃᕶᕿ(67)],asymmetric: new (_ᖂᕿᕷᖀ[_ᖃᕾᕶᖂ(67)])},2: {symmetrical: new (_ᖄᕵᕸᖁ[_ᖃᕾᕶᖂ(67)])({key: _ᖀᕷᖚᖉ,mode: _ᕹᖃᕶᕿ(926),iv: _ᕹᖃᕶᕿ(981)}),asymmetric: _ᖂᕸᖄᕿ[_ᕹᖃᕶᕿ(67)]}};if (_ᖂᖗᕿᖁ[_ᖃᕾᕶᖂ(123)](_ᖆᖘᕶᕹ[_ᖃᕾᕶᖂ(604)])) {var o = _ᖃᕾᕶᖂ(873) === _ᖆᖘᕶᕹ[_ᖃᕾᕶᖂ(604)], a = _ᖆᖘᕶᕹ[_ᖃᕾᕶᖂ(604)], _ = _ᖄᖈᕸᖃ[a][_ᕹᖃᕶᕿ(938)][_ᖃᕾᕶᖂ(911)](_ᖀᕷᖚᖉ);while (o && (!_ || 256 !== _[_ᖃᕾᕶᖂ(64)]))_ᖀᕷᖚᖉ = (0,_ᕷᕸᖉᕷ[_ᖃᕾᕶᖂ(93)])(),_ = (new (_ᖂᕿᕷᖀ[_ᕹᖃᕶᕿ(67)]))[_ᖃᕾᕶᖂ(911)](_ᖀᕷᖚᖉ);var u = _ᖄᖈᕸᖃ[a][_ᖃᕾᕶᖂ(934)][_ᖃᕾᕶᖂ(911)](_ᖘᖄᖁᕿ, _ᖀᕷᖚᖉ);return (0,_ᕷᕸᖉᕷ[_ᖃᕾᕶᖂ(5)])(u) + _}}}
它这个w
的值由两段密文拼接组成的,先看_ᖄᖈᕸᖃ[a][_ᕹᖃᕶᕿ(938)][_ᖃᕾᕶᖂ(911)](_ᖀᕷᖚᖉ)
这个位置的代码,生成的是第一段。通过对一个长度16
位的字符串进行了一个encrypt
操作得到的,如下所示:
好,这里看看encrypt
对应的JS
,先解决第一段的密文。再去往下进行分析,跳转后直接拿下来,代码如下所示:
...
_ᖂᖗᕿᖁ[_ᖁᖆᕸᕸ(55)][_ᖁᖆᕸᕸ(1028)] = function _ᖆᕶᖙᖁ(_ᖘᖄᖁᕿ, _ᖁᖆᕸᕸ) {var _ᖃᕾᕶᖂ = _ᖘᖈᖙᖃ.$_CL, _ᖁᕷᕹᖄ = ["$_DGHAO"].concat(_ᖃᕾᕶᖂ), _ᕹᖃᕶᕿ = _ᖁᕷᕹᖄ[1];_ᖁᕷᕹᖄ.shift();var _ᖃᕹᖄᖚ = _ᖁᕷᕹᖄ[0];null != _ᖘᖄᖁᕿ && null != _ᖁᖆᕸᕸ && 0 < _ᖘᖄᖁᕿ[_ᖃᕾᕶᖂ(64)] && 0 < _ᖁᖆᕸᕸ[_ᕹᖃᕶᕿ(64)] ? (this[_ᖃᕾᕶᖂ(96)] = function _ᖆᕶᖙᖁ(_ᖘᖄᖁᕿ, _ᖁᖆᕸᕸ) {var _ᖃᕾᕶᖂ = _ᖘᖈᖙᖃ.$_CL, _ᖁᕷᕹᖄ = ["$_DGHFu"].concat(_ᖃᕾᕶᖂ), _ᕹᖃᕶᕿ = _ᖁᕷᕹᖄ[1];_ᖁᕷᕹᖄ.shift();var _ᖃᕹᖄᖚ = _ᖁᕷᕹᖄ[0];return new b(_ᖘᖄᖁᕿ,_ᖁᖆᕸᕸ)}(_ᖘᖄᖁᕿ, 16),this[_ᖃᕾᕶᖂ(943)] = parseInt(_ᖁᖆᕸᕸ, 16)) : console && console[_ᖃᕾᕶᖂ(370)] && console[_ᕹᖃᕶᕿ(370)](_ᕹᖃᕶᕿ(1130))
}...省略
下一个断刷新后可以看到上面函数先是接受了一个16
位的那个密文然后_ᖘᖄᖁᕿ
参数的值是一个很长的十六进制字符串(256字节
,2048
位)直接丢给GPT把参数值一并提交它会提示我们这通常是一个RSA
公钥的模数(n
)
_ᖁᖆᕸᕸ
参数的值10001
十进制公钥指数,就算我们不进行AST
还原通过作用域结合控制台甚至是鼠标查值的方式也都是也可以看到一些特征的,JS通过new b(_ᖘᖄᖁᕿ, _ᖁᖆᕸᕸ)
创建了一个加密对象,b
可能是RSA
的构造函数,接着parseInt(_ᖁᖆᕸᕸ, 16)
将公钥指数转换为数值
看到有console
相关的信息输出,鼠标扫了一下更加证实了上面的加密方式猜测,如下所示:
另外说一下这个16
位的key
如何生成的,返回的4
个e()
相加,跟进去看看e
是什么,如下所示:
上面e
方法实现了返回一个随机生成四位16
进制字符串,.toString(16)
将整数转为16
进制字符串,substring(1)
再截取字符串的第二个字符到末尾,得到一个四位16
进制字符串,65536
相当于16^4
即四位16
进制数的最大值(FFFF
)+1
return (65536 * (1 + Math.random()) | 0).toString(16).substring(1);# 示例
Math.random() // 0.74321
1 + 0.74321 // 1.74321
65536 * 1.74321 // 114245.12056
114245 | 0 // 114245 (十进制)
(114245).toString(16) // "1bdc5"
"1bdc5".substring(1) // "bdc5"
第一段密文分析完可以成功拿到_
的值,接下来根据最初贴出来的JS继续往下分析u
这个大数组是怎么来的,原始代码如下:
var u = _ᖄᖈᕸᖃ[a][_ᖃᕾᕶᖂ(934)][_ᖃᕾᕶᖂ(911)](_ᖘᖄᖁᕿ, _ᖀᕷᖚᖉ);
u
这里通过调用了_ᖃᕾᕶᖂ(911)
的encrypt
传了两个参数,_ᖘᖄᖁᕿ
是文章开始的那个转成字符串的大JSON
,_ᖀᕷᖚᖉ
则是上面复现的16
位key
,跟进去发现特征跟前面cbc
一样并且iv
也是一样。走了一个AES
的加密操作,如下所示:
可以看到这个流程它走完AES
后返回的是一个长数组(即u
),然后往下再做了一个16
进制的字符串转换操作得到最后w
参数的另一段值,然后把RSA
加密得到的那一段短值拼接到u
出来这串长值后面得到完整的一个w
加密值,如下所示:
4. JSON参数分析
至此,上面关于w
参数的整体加密计算流程就分析梳理出来了,然后接下来回过头来需要分析一下参与加密的那个大JSON
,因为貌似涉及的字段看起来不少,这里我们将它拿出来分析一下,如下所示:
{"pow_sign" : "a689bc86ae4eee78c2cf11a946b1b741","ep" : "123","passtime" : 796,"biht" : "1426265548","pow_msg" : "1|0|md5|2025-06-14T19:18:12.048835+08:00|4f70e8a42240f8f809bea8382e738e53|df5ed6e3f65e49709e9dcbd4d595f199||a6ae0baf4ca6c7d1","lot_number" : "df5ed6e3f65e49709e9dcbd4d595f199","em" : {"wd" : 1,"sc" : 0,"ek" : "11","nt" : 0,"ph" : 0,"cp" : 0,"si" : 0},"geetest" : "captcha","setLeft" : 146,"5ed6e3f6" : "e9dcbd","userresponse" : 147.1369191209607,"device_id" : "","lang" : "zh","w22T" : "72PZ"
}
这里主要分析一下pow_sign
、pow_msg
、userresponse
、setLeft
以及那个5ed6e3f6:e9dcbd
键值对参数,先来看pow_msg
它由两部分拼接,如下所示:
前面的那个长字符串用|
拼接的可以全局搜索这个参数下一个断找初始化的地方,很明显还是拿的load
接口请求的参数加响应的参数拼起来的,如下所示:
上面三张图就是整个一次滑块加载出来的参数值拼接出来的
_ᖀᖙᕵᖀ
第一个大长串,|0|md5|2025-06-14T22:48:45.549309+08:00|
取自于loiad
接口的响应参数pow_detail
中四个参数,后面追第一个32
位字符串取值于请求参数的captcha_id
字段,第二个32
字符串取值与响应参数内的lot_number
字段
再看第二部分的h
是怎么来的,往上看定义的地方直接点进去又来到了上面我们分析的16
位key
生成的e
函数处,如下所示:
那么看样子走的同样的逻辑,至此pow_msg
参数分析完毕。接着来看pow_sign
参数,一样的32
字符跟进去看到md5
的信息。直接在线工具验证证实就是对pow_msg
的值做了一个md5
得到了pow_sign
,如下所示:
接下来我们先不看那两个数字结果相关的字段,先分析那个key value
的字段,这个也是动态生成的。回到最开始我们分析w
位置的地方,往上一点点就能看到这个动态参数的生成,这里多刷新了几次发现也不是加密生成的,还是对参数字段的值做的索引取值拿到的,如下所示:
如上取的是load
接口中返回的lot_number
字段的值,索引的规则目前看是下面这样子来对应的(它这个取下标的规则挂到了window下面,最好动态获取
)
最后剩下三个数字相关的参数字段,来看看如何来的。setLeft
是滑块的距离,userresponse
在滑块距离的数值上进一步做了一个运算。如下所示:
5. 距离识别
最后我们只需要对滑块的缺口的距离进行识别拿到setLeft
参数的值基本就完成了,滑块识别代码实现如下(仅供参考
):
import cv2
import numpy as npdef get_slider_offset(background_img_bytes, slider_img_bytes):"""识别滑块缺口在背景图中的X坐标。:param background_img_bytes: 背景图片的二进制数据:param slider_img_bytes: 滑块缺口图片的二进制数据:return: 缺口的X坐标"""# 解码图像为灰度图bg_gray = cv2.imdecode(np.frombuffer(background_img_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)slider_gray = cv2.imdecode(np.frombuffer(slider_img_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)# 裁剪滑块图像:只保留有效区域(像素值 < 200)ys, xs = np.where(slider_gray < 200)slider_crop = slider_gray[min(ys):max(ys), min(xs):max(xs)]# 边缘检测bg_edge = cv2.Canny(bg_gray, 100, 200)slider_edge = cv2.Canny(slider_crop, 100, 200)result = cv2.matchTemplate(cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB),cv2.cvtColor(slider_edge, cv2.COLOR_GRAY2RGB),cv2.TM_CCOEFF_NORMED)_, _, _, top_left = cv2.minMaxLoc(result)h, w = slider_crop.shapecv2.rectangle(bg_gray, top_left, (top_left[0] + w, top_left[1] + h), (0, 0, 255), 2)cv2.imwrite('distinguish.jpg', bg_gray)return top_left[0]
6. RSA纯算还原
这里我们根据上面的分析先对第一部分的16
位key
的RSA
加密使用Python进行还原,还原实现代码如下所示:
import base64
import binascii
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5, PKCS1_OAEPdef load_public_key(pem_key=None, modulus=None, exponent=None):if pem_key:cleaned = pem_key.replace("\n", "").replace(" ", "")if "-----" in cleaned:cleaned = [part for part in cleaned.split("-----") if len(part) > 25][0]key_bytes = base64.b64decode(cleaned)return RSA.importKey(key_bytes)elif modulus and exponent:n = int(modulus, 16) if isinstance(modulus, str) else moduluse = int(exponent, 16) if isinstance(exponent, str) else exponentreturn RSA.construct((n, e))raise ValueError("必须提供 PEM 公钥或 modulus + exponent")def get_cipher(key_obj, padding_mode: int):scheme = {1: PKCS1_OAEP.new,2: PKCS1_v1_5.new}if padding_mode not in scheme:raise ValueError("padding_mode 只支持 1(OAEP)或 2(v1_5)")return scheme[padding_mode](key_obj)def get_chunk_size(key_obj):return int(key_obj.size_in_bits() / 1024 * 100)def encrypt_bytes(plaintext: str, *, pem_key=None, modulus=None, exponent=None, padding_mode=1) -> bytes:key_obj = load_public_key(pem_key=pem_key, modulus=modulus, exponent=exponent)cipher = get_cipher(key_obj, padding_mode)chunk_size = get_chunk_size(key_obj)encrypted_data = b""for offset in range(0, len(plaintext), chunk_size):segment = plaintext[offset:offset + chunk_size].encode()encrypted_data += cipher.encrypt(segment)return encrypted_datadef encrypt_to_base64(plaintext: str, **kwargs) -> str:return base64.b64encode(encrypt_bytes(plaintext, **kwargs)).decode()def encrypt_to_hex(plaintext: str, **kwargs) -> str:return binascii.hexlify(encrypt_bytes(plaintext, **kwargs)).decode()def encrypt_to_utf8(plaintext: str, **kwargs) -> str:return encrypt_bytes(plaintext, **kwargs).decode("utf-8", errors="ignore")modulus = ("00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74""C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F""09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B5970""6592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81"
)
exponent = "10001"# key动态传递
ciphertext = encrypt_to_hex("189c7043fd718369", modulus=modulus, exponent=exponent, padding_mode=2)
print(ciphertext)
7. AES纯算还原
这里我们根据上面对u
的分析进行AES
加密算法还原,采用的CBC
模式,然后iv
上面几个0
固定的,key
的话跟参与RSA
加密的时候用的一致,算法实现如下所示:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import binascii
import secrets
import stringdef encrypt_aes_cbc(plaintext: str, key_str: str, iv_str: str = "0000000000000000") -> str:"""使用 AES-CBC 模式对字符串加密,并返回 hex 字符串。:param plaintext: 原始明文字符串:param key_str: 16 字节长度的密钥字符串:param iv_str: 16 字节长度的初始向量(默认全0):return: 加密后的十六进制字符串"""key = key_str.encode('utf-8')iv = iv_str.encode('utf-8')# 初始化加密器cipher = AES.new(key, AES.MODE_CBC, iv)# 对明文进行 PKCS7 填充padded = pad(plaintext.encode('utf-8'), AES.block_size)# 加密并转换为 hex 字符串返回encrypted_bytes = cipher.encrypt(padded)return binascii.hexlify(encrypted_bytes).decode('utf-8')# 示例调用
data_to_encrypt = '{"setLeft":146,"passtime":796,"userresponse":147.1369191209607,"device_id":"","lot_number":"df5ed6e3f65e49709e9dcbd4d595f199","pow_msg":"1|0|md5|2025-06-14T19:18:12.048835+08:00|4f70e8a42240f8f809bea8382e738e53|df5ed6e3f65e49709e9dcbd4d595f199||a6ae0baf4ca6c7d1","pow_sign":"a689bc86ae4eee78c2cf11a946b1b741","geetest":"captcha","lang":"zh","ep":"123","biht":"1426265548","w22T":"72PZ","5ed6e3f6":"e9dcbd","em":{"ph":0,"cp":0,"ek":"11","wd":1,"nt":0,"si":0,"sc":0}}'
aes_key = "b9acf19ce1a635a9" encrypted_hex = encrypt_aes_cbc(data_to_encrypt, aes_key)
print(encrypted_hex)
最后整个关于极验4滑块的协议验证流程就差不多到这里结束了,找了一个套极验4的网站测试了一下。如下所示: