声明
本文仅供学习参考,如有侵权可私信本人删除,请勿用于其他途径,违者后果自负!
如果觉得文章对你有所帮助,可以给博主点击关注和收藏哦!
插句个人内容:本人最近正在找工作,工作城市:广州。如果有合适的机会,希望有大佬可以内推,感激不尽。🙏
js逆向马马虎虎,app会弱一些。感兴趣的大佬可以私信本人要简历。
流程分析
本篇使用网站:
aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s
整个滑块验证码的流程如下,其中需要注意的接口都已经标记过。
分别解释一下用途。
- 第一个接口
无加密参数参数,返回gt
和challenge
两个字段,这两个字段贯穿整个流程,十分重要。
- 第二个接口
该接口会返回很多字段,能够使用到的也就是c
,此时出现了第一个w
。
过去旧版的第一个w
和第二个w
都可以选择置空,但是现在这样做已经校验不过去了。
请求参数如下gt
和challenge
前面一个接口已经获得了,然后就是这个w
了。
- 第三个接口
返回了滑块类型,之前一直觉得这个接口没什么用,直接就忽略了,发现怎么都不能校验过去。
同时该接口的w
值也不可忽略,还是要拿下,不可写死或置空处理。
- 第四个接口
这个接口就是校验验证码缺口的接口了,成功了就会返回success
,否则就是fail
,当然还有其他的一些状态,可以根据返回的状态去排查对应的问题。
逆向分析
第一个w
第一个接口处,可以在Initiator
中跳进js文件打下断点一步步跟栈。
或者直接搜索i + r
,然后打下断点。
将i
和r
对应生成的值拿下来就可以还原第一个w
了。
先看一下r
是什么内容。
先把这个函数直接拿下来,然后运行。
报错,yCtOu未定义,寻找一下是什么东西。
将需要的部分都拿下。
重新运行,继续报错。
X没有定义,继续扣代码。这样做太慢了,可以直接把整个文件copy到本地然后慢慢补。
然后就可以很轻松的找到需要的内容了。
继续运行,window未定义。
pe是什么不知道,回到网页中看一下
缺少navigator
补上就完了,需要的环境大致如下:
window = global;
navigator = {
appName:'Netscape'
}
pe = navigator
报错复制到网页上观察一下。
看样子像一个随机数,找到函数位置。
四位随机数生成四次然后进行字符串拼接。
最后结果就出来了。
o的代码:
o = $_BFo()[$_CEGDt(1108)](de[$_CEGEy(459)](t[$_CEGEy(369)]), t[$_CEGDt(1196)]())
手动解混淆:
o = $_BFo()['encrypt1'](JSON.stringify(t['$_EJV']), t["$_CCHU"])
t[‘$_EJV’]是一个大的对象,除了gt和challenge之外,其他都可以固定。
{
"gt": "传入gt",
"challenge": "传入challenge",
"offline": false,
"new_captcha": true,
"product": "float",
"width": "300px",
"https": true,
"api_server": "apiv6.geetest.com",
"protocol": "https://",
"type": "fullpage",
"static_servers": [
"static.geetest.com/",
"static.geevisit.com/"
],
"beeline": "/static/js/beeline.1.0.1.js",
"voice": "/static/js/voice.1.2.4.js",
"click": "/static/js/click.3.1.0.js",
"fullpage": "/static/js/fullpage.9.1.9-r8k4eq.js",
"slide": "/static/js/slide.7.9.2.js",
"geetest": "/static/js/geetest.6.0.9.js",
"aspect_radio": {
"slide": 103,
"click": 128,
"voice": 128,
"beeline": 50
},
"cc": 20,
"ww": true,
"i": "-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1!!-1"
}
t[“$_CCHU”]则是上一次省的16位随机数,需要和生成r
部分保持一致的值。
然后将$_BFo()
函数拿下来就得到结果。
i
就简单了,将o
生成的数组变成字符串。
还是缺啥补啥,最终第一个w
结果。
第二个w
同样使用Initiator
追溯,向上寻找堆栈,慢慢分析也可以找到。
不过比较麻烦,可以直接搜索var n = {}
。
调试发现断点没有经过t[$_CFEEo(1109)]();
时w
还没有生成,所以w肯定在该函数之前生成了。
函数很长,直接看return处。
最终定位到。
手动解混淆:
p['$_HEt'](c["encrypt"](r, i["$_CCHU"]()));
实际上和第一个w的加密一样
重点看一下r,r是一个字符串,反序列化
{
"lang": "zh-cn",
"type": "fullpage",
"tt": "MZ(*((1(j(CM7((",
"light": -1,
"s": "c7c3e21112fe4f741921cb3e4ff9f7cb",
"h": "321f9af1e098233dbd03f250fd2b5e21",
"hh": "39bd9cad9e425c3a8f51610fd506e3b3",
"hi": "09eb21b3ae9542a9bc1e8b63b3d9a467",
"vip_order": -1,
"ct": -1,
"ep": {
"v": "9.1.9-r8k4eq",
"te": false,
"$_BBp": true,
"ven": "Google Inc. (Intel)",
"ren": "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A6) Direct3D11 vs_5_0 ps_5_0, D3D11)",
"fp": [
"move",
744,
147,
1707040727652,
"pointermove"
],
"lp": [
"up",
672,
82,
1707040728689,
"pointerup"
],
"em": {
"ph": 0,
"cp": 0,
"ek": "11",
"wd": 1,
"nt": 0,
"si": 0,
"sc": 0
},
"tm": {
"a": 1707040721021,
"b": 1707040721290,
"c": 1707040721290,
"d": 0,
"e": 0,
"f": 1707040721021,
"g": 1707040721056,
"h": 1707040721056,
"i": 1707040721056,
"j": 1707040721122,
"k": 1707040721089,
"l": 1707040721122,
"m": 1707040721286,
"n": 1707040721288,
"o": 1707040721292,
"p": 1707040721503,
"q": 1707040721503,
"r": 1707040721504,
"s": 1707040721562,
"t": 1707040721562,
"u": 1707040721564
},
"dnf": "dnf",
"by": 0
},
"passtime": 1190827,
"rp": "6fc9d795969c2c344ce5bc11e50c9916",
"captcha_token": "1265749785",
"otpj": "jm4jwcx7"
}
经过测试发现,其他值均可固定,但是rp的加密过程passtime参与了,所以要注意保持一致。
再看rp如何生成,向上寻找。(代码比较混乱,我也找了半天o(╥﹏╥)o)
使用到了gt、
challenge以及
passtime,加密长度32位,盲猜一波md5。 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/39047339/1707042842696-bb495516-f94b-4b69-b857-e2177b168e7b.png#averageHue=%23f6f5f4&clientId=u7beac4ef-06cb-4&from=paste&height=367&id=u837eeb7b&originHeight=459&originWidth=778&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=52709&status=done&style=stroke&taskId=uaad1ca3c-47d4-418b-892d-69fb067be2b&title=&width=622.4) 完全一致,rp就已经生成了。
i"$_CCHU"依然是第一次生成的16位随机字符串,也需要使用同样的秘钥。不然会出现
param decrypt error`的错误。
至此w生成完毕。
第三个w
这个w网上有很多现成的文章了,直接搜索"\u0077"
即可定位,也可以选择上面的方法,跟堆栈。
这个w其实和上面的加密方式一样,没什么好说的,这里就不展开了。
说一下不同的地方。
{
"lang": "zh-cn",
"userresponse": "9777999999979766a",
"passtime": 266,
"imgload": 73,
"aa": "K,,(!!Msssyyt!)!)ttusYsx(!!($)/(/0101010110$nd)c",
"ep": {
"v": "7.9.2",
"$_BIE": false,
"me": true,
"tm": {
"a": 1707046714483,
"b": 1707046715148,
"c": 1707046715148,
"d": 0,
"e": 0,
"f": 1707046714484,
"g": 1707046714533,
"h": 1707046714533,
"i": 1707046714533,
"j": 1707046714614,
"k": 1707046714574,
"l": 1707046714614,
"m": 1707046714724,
"n": 1707046714741,
"o": 1707046715150,
"p": 1707046715369,
"q": 1707046715369,
"r": 1707046715370,
"s": 1707046715434,
"t": 1707046715434,
"u": 1707046715435
},
"td": -1
},
"h9s9": "1816378497",
"rp": "6ca7d8a547390ce95dc57399fd88f50d"
}
userresponse 、aa和rp是需要处理的部分,其他固定即可。
- userresponse 部分
H(滑动距离,34位challenge)
将H函数扣下来就可以实现该算法了。
- aa部分
此时的aa已经生成了,向上找一层。
n[$_DAAAU(985)][$_CJJJU(1075)]
有三个参数,分别是轨迹加密得到的结果,接口的c
和s
(和34位challenge为一个接口)
然后将剩余代码补齐即可得到结果。
十次成功率80%
参考
- 【验证码逆向专栏】某验三代滑块验证码逆向分析
- 【验证码逆向专栏】某验四代滑块验证码逆向分析
- js逆向案例-zzjg之jy3/ast/woff.2
- 极验滑块验证码破解与研究(四):滑块轨迹构造
最后
最近开通了个人公众号,有一些被博客下架的文章后续也会同步上去。
大佬们可以关注一波。