vaptcha的验证页面可能为弹出的一个单独标签页,为了方便调试,可以设置一下Chrome的DevTools
在类似如下的验证页面加载过程中,会生成VM环境并加载验证逻辑
VM的执行入口为
其VM代码就是vaptcha-sdk接口返回的内容
返回的内容是混淆过的,可以适当使用AST还原并替换一下。除了单步调试定位外,还可以通过具体要分析的接口的Initiator中的Request call stack定位,如获取图片的接口
通过对请求流程的分析,可知大致的关键链路接下来就是对链路中部分关键的请求体参数做详细的分析首先看一下请求体中的关键参数 多次请求可以发现vi是固定的,应为网站标识。然后根据刚才的链路分析或者搜一下k参数的值可知k参数就是config接口返回的
对应其中的knock参数(这里是不同的两次请求,故图中的值不一样)接下来重点分析一下en参数的生成。先跟着调用栈进去看一下,找到请求体
_0x45bdb3["data"]就是完整的请求体
可以看到_0x45bdb3就是当前函数的入参跟着调用栈回溯看下_0x45bdb3是怎么生成的
能看到_0x45bdb3就是在这里组成的对象,其中data的值就等于_0x26b8e6,继续跟
_0x1e0f55就是data的值,往上面看还能看到en就是对应_0xad7777,至此就可以专注分析en值的生成了。跟一下en,也就是_0xad7777的生成逻辑
_0xad7777的生成和_0x23221c有关,而_0x23221c也是由一堆其他变量相加得到的先来挨个分析这些变量的值是怎么生成的,最后再用encryFunc处理一下就行了。搜一下变量名,找到赋值的地方
其等于_0x354394["GenerateFP"](),看下这个函数是干啥的(根据函数名猜测和指纹有关)可以看到其中实现和Canvas指纹相关,看下getComplexCanvasFingerprint这个函数结合extractCRC32FromBase64函数,就是通过Canvas生成一段具有特征的base64,在进行加密处理生成最终的指纹值,其中Canvas绘图时还会根据入参_0xb1e678来生成不同的文本不同设备(环境)的Canvas指纹一般是唯一的,在结合具体网站应用上,使用当前浏览器生成的指纹,或用node、playwright、python相关库之类的环境去模拟生成指纹去做请求,大多数情况下都是不稳定的甚至是完全没法用的,如何批量稳定的绕过这里暂且不讨论了这里的extractCRC32FromBase64函数可以整体扣下来或转换为Python代码。搜一下变量名,找到赋值的地方
可以看到其等于_0x23367b["sent"](),跟一下之后遇到的处理流程中涉及sent的en参数的也和接下来要分析的流程类似,故只以当前流程为例进行详细分析其实现的位置为正常情况下应该是能走到sent中return语句,而sent中只有简单的取值,也就是说在调用sent之前,即走到case 1块之前,_0x1df4cd[1]的值就已经生成了,那先看一下这个东西是在哪赋值的搜一下这个变量名,可以发现对其的赋值操作主要集中在_0x1e72d1这个函数中
简单分析一下这个函数的处理逻辑,_0x1df4cd的值可能和_0x49bd55有关该函数主要围绕_0x49bd55这个入参,同时也会走下面的逻辑重新赋值这里的函数调用内容是会发生变化的,暂时只用关注当前_0xb049bd这个en参数的流程可以刷新一下重新跟一下getImage流程,看下_0x49bd55或_0x1df4cd怎么来的
可以看到getImage流程对应的函数返回的是经_0x52fdcb函数处理的一个值,稍微看一下这个函数可知最后它返回的是一个Promise对象然后_0x52fdcb的第四个函数参数返回的是一个经_0x5513de函数处理的值
可以看到该函数返回的是一个key指向不同函数执行逻辑的对象,每个执行逻辑都和_0x1e72d1函数有关继续跟(或者结合这些流程稍微梳理一下),就能看到 _0x41c1ce["call"](_0x160280, _0x4bd611)的返回值,即_0x49bd55是_0x5513de函数的第二个函数参数中case 0块的返回值,即对应上述分析的第一个en参数的的处理逻辑所在的块看一下_0x298102["GenerateFP"](false, true)
其返回的是一个Promise,可以在Promise包裹中的函数下几个断点看看其中的处理逻辑及返回值,将该Promise记为Promise0return后回到_0x1e72d1函数的处理流程中,由于返回的列表第一个值是4,故走下面的逻辑
继续跟
根据上述对_0x5513de的简单分析,这里走了next的执行逻辑,可以看到上面_0x1e72d1的返回值就是_0x4234aa函数的入参_0x4921b0,可以发现其主要围绕Promise的执行,并逐层传递Promise执行中产生的参数_0x4234aa函数也是在一个Promise内,而上述提到的getImage对应的函数返回时也正是调用了返回这个Promise对象的_0x52fdcb函数
如果done被标记为true,则会取value给到_0x12a155,即resolve,否则会一直执行then,fulfilled时执行_0x910b2a,其参数简单来说就是Promise执行过程中resolve接收的值,reject时执行_0x447f35,这里主要关注Promise的onFulfilled函数,即_0x910b2a,逻辑如下
分析可知这里的next最终还是要回去执行刚才分析的_0x1e72d1函数,来标记done的。这里的_0x243cf0,即resolve的值,会作为_0x1e72d1函数的列表类型的入参的第二个元素继续跟
发现先去执行了_0x3edf5e函数,看一下_0x3edf5e的逻辑,主要是将传入的_0x48cfb7,也即resolve的值,包装成类似下面的形式再往下看,可以发现一段流程后又回到了Promise0中的case 2,
可以看到这里面components的值和上面说的包装的形式很像,合理怀疑就是刚才的流程中一步步组装起来的,这里暂时不管简单分析下case 2块,根据_0x4b4666是否有值,再结合components(这里的compoents对象可以先扣下来固定下来),组成新的对象_0x1938e6,后面经过hashComponents得到_0x297a60_0x4d7c27就是当时的第二个入参,根据_0x298102["GenerateFP"](false, true)可知这里是true,也就是不需要切割,最后将处理好的_0x297a60交给Promise0的resolve根据上面的分析,这里的Promise0处于resolved(进入下一个状态)后,即在_0x1e72d1函数中执行完了 _0x49bd55 = _0x41c1ce["call"](_0x160280, _0x4bd611),返回了[2],继续跟,走到default块
在第
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288534.htm
[原创][JS逆向]Vaptcha逆向分析
404 浏览
6 回复
感谢分享
感谢分享
感谢分享
tql
提供一份反混淆后的js,仅供参考
上传的附件:
vaptcha-sdk.1.2.35.669f69e6-fix.js
(386.30kb,13次下载)
上传的附件:
vaptcha-sdk.1.2.35.669f69e6-fix.js
(386.30kb,13次下载)
大佬,招资深JS逆向,有意愿的话,请联系我