声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
目标网站
aHR0cHM6Ly93d3cua3VhaXNob3UuY29tL3Byb2ZpbGUvM3h4Ymt3ZDhta250ZWFj
参数流程分析
这里简单说一下流程吧,
刷新页面。如果没出滑块。删除cookie或者打开无痕重新打开。
config 请求 => 出滑块
kSecretApiVerify => 验证滑块
config请求 需要一个captchaSession 这个好像也是请求返回的。我们搜索一下就能找到了。
kSecretApiVerify 请求 只有一个负载参数 => verifyParam
然后请求响应呢 得到 captchaToken 最后把这个值带进数据请求里就ok了
ps: 这个接口 也不知道为啥有时候刷不出数据。不过不影响 本来就是研究滑块。
逆向流程分析
在伪造过程中发现。我们在获取数据的时候每次只有第一次能获取到数据。
在你第二次获取数据的时候。反而就获取不到数据了。
其实这里ks应该是校验了 一个cookie值 _did
这个cookie值需要带着去滑块。
然后检验了 你滑块过了 应该就能请求了。
这里_did 的具体算法如下:
1 2 3 4 5 6 | X = "web_" + function () {
for ( var t = 1e9 * Math.random() >>> 0, e = [], n = 0; n < 7; n++)
e.push( "0123456789ABCDEF" .charAt(16 * Math.random()));
return t + e.join( "" )
}();
|
1 2 3 4 5 6 7 8 9 10 11 12 | import random
def B():
def X():
t = int ( 1e9 * random.random())
e = ' '.join(random.choice(' 0123456789ABCDEF ') for _ in range ( 7 ))
return str (t) + e
return f "web_{X()}"
|
然后我们去请求得到captchSession
PS: captchSession 需要附带另一个请求。才能请求出来。
这里返回了很多值,我们来看看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | {
"result" : 1,
"desc" : "ok" ,
"captchaSn" : "Cgp6dC5jYXB0Y2hhEsQCdWDZeMHqrbJPMyOc_oQgwOCbX50LOMegZUbnZRLVVrssF2..." ,
"bgPicUrl" : "https://.../rest/zt/captcha/sliding/bgPic" ,
"cutPicUrl" : "https://..../rest/zt/captcha/sliding/cutPic" ,
"bgPicWidth" : 686,
"bgPicHeight" : 400,
"cutPicWidth" : 122,
"cutPicHeight" : 122,
"disX" : 24,
"disY" : 135,
"verifyUrl" : "https://..../rest/zt/captcha/sliding/verify" ,
"refSes" : "https://.../rest/zt/captcha/refSes" ,
"collectLimit" : 1000,
"verifyUrl2" : "https://.../rest/zt/captcha/sliding/kSecretApiVerify" ,
"a" : 3,
"d" : 15,
"sx" : 4,
"ix" : 74,
"sy" : 2,
"iy" : 68
}
|
如上文所示。
captchaSn 是拼接 bgPicUrl 和 cutPicUrl的参数。以用于请求得到url
bgPicWidth 以及 bgPicHeight 和 cutPicWidth cutPicHeight 是验证码背景图和缺口图的长和宽
disX 和 disY 是 最终获取 captchaToken的重要参数。这里要注意传值后面生成captchaToken的disY 要乘以0.46
这里贴个ocr 识别代码
1 2 3 4 5 6 7 8 | def slide_match(path_target, path_background):
det = ddddocr.DdddOcr(det = False , ocr = False )
with open (f '{path_target}' , 'rb' ) as f:
target_bytes = f.read()
with open (f '{path_background}' , 'rb' ) as f:
background_bytes = f.read()
res = det.slide_match(target_bytes, background_bytes)
return res[ "target" ][ 0 ]
|
后面就是去看 verifyParam 这个值的生成逻辑。
这里直接跟栈 直接进如下图所示的栈
如下图所示 位置 即使传参处。
ast一下方便我们调试。
这里看到 这。可以看到 这个a的生成在一个大的控制流里。
在swtich中 打上断点。重新刷新页面 走一下流程。
这里可以发现 case 0 里包含了一个异步请求。
1 | i[ "hDAgm" ](Tt[ "a" ], c) >>> Tt[ "a" ](c)
|
那后面就很简单了 只需要扣下这个Tt["a"] 这个函数就行了。
然后单步调试追栈就行。
如下图可知 调用了 v 然后传进去了一些参数。最终生成了 这个captchaToken
上文已经讲过了一些参数。现在重新梳理一下
- captchaSn : 接口返回的值
- bgDisWidth:定值
- bgDisHeight | cutDisWidth| cutDisHeight: 同上
- relativeX: 识别的距离
- relativeY: 接口返回的值
- trajectory: 生成的轨迹
- gpuInfo: 显卡指纹 暂时写死(可收集)
- extra_params:浏览器指纹 暂时写死(可收集)
如下图所示 是上文某些接口的信息。以及某些方法的生成点。
将上述代码复制 并且修改一下
1 2 3 | i = a.a[ "stringify" ](c), o = n[ "gOBgK" ](h, i), t[ "next" ] = 4, n[ "OZXaj" ](x, o)
>>>>
i = _a(c), _o = _h(i), _x(_o)
|
这里可能有坑的地方就是 a
函数中的 b(e) 了 跟进去找值就行。其他好像没什么难度。
这里全补完了 会报一个 Jose is not defined
的错误
这里追栈 发现 这是个webpack添加个自执行的模块。我们把某些函数扣下来适当改写就行。如下图所示
搞定之后重新生成了 一组值 但是到这还没结束。继续跟着先前switch 的那个顺序搞就行。
也就是下图所在位置。
这个扣就不讲了。流程都一样 肯定是有小坑的。
再扣下面函数的时候。不要无脑扣。慢慢分析值。看看控制流走的是哪个分支就会很简单。
后面就不讲了。到最后就做个拼接 就生成了captchaToken
这里 还有个问题。因为pyexecjs是不支持异步的。
所以需要用node 去执行 然后拿到返回结果。
这都很基础 就不详细说了。
结果
最后成功返回结果。这个接口成功率感人啊。检测的东西好像有点多。