JS逆向:D象滑动验证码加密分析

前言

好久没发文章了,最近一直在忙工作和一些零零散散的事,逆向碰的比较少。。也是没什么素材,毕竟我已经码前端有一段时间了~~这次就带来个D象的简单分析吧。ding象、shu美、ji验、yi盾等等。。这些专业的验证码反爬,可以说是非常毒瘤了(虽然我们在他们眼里也是毒瘤,就互相伤害呗。。),各种验证码的花样也是层出不穷。有空想学学深度学习,断断续续也有一阵子了,连个完整的点选还没写出来,他居然搞出了这么多花样了。。。

这里有一些验证码还是有难度的。。。至少我这种渣渣水平是想不到解决办法。一时也想不起哪个网站在用D象,所以直接用D象官网示例入手吧。

开干

先打开Fiddler,走一遍完整流程,看看都有什么参数需要找。

倒着看,首先是这个v1的:
响应是返回success:true和token,就是最后的验证请求了。参数。。

哎好大一坨,就不能像某盾一样搞一小段吗。。看着都头大。这个ac不用猜,肯定是和轨迹相关的,这么长不可能是别的。不过当初我看见它的时候,看见前面1914#心头一紧,让我想起了被140#,也就是某里的UA支配的恐惧。。这里应该和他同行们差不多,好多都是可以写死固定的。那就换个浏览器再抓一次看看:
除了ac以外,sid、aid、x、y也都是变化的。首先是sid:
图片地址也在这里了,请求中又是一大坨参数,不过仔细观察参数都是一样的。x、y、aid暂时没有找到。
顺手搜了下ak,这个请求应该也是一个重要的请求。
ak,JS里的,这个是可以直接写死的,和验证类型有关:
大概就是这些东西了。重要的请求:c1(第一次验证环境)、a(获取验证图片)、v1(请求验证结果)。参数:param、ac、aid、x、y。
按照顺序来呗,先从c1开始走一遍。

下个XHR断点,刷新:
这个混淆。。好吧看着是有点难受。跟着调用栈一步步找,走到这里的时候
就是这了。
是对这个对象处理而来的。
下断点,清cookie继续刷新。。。跟到这个n.ZqRHlZD里看看:
。。。算了,我还是用AST先还原一下吧。AST还原的代码在最后面讲,这里先走逻辑。还原后也就是这个方法:
也就是对r里的每个参数循环处理:
传到M中:
进行一些位运算,直接扣就行了。最后得出来的t:
再把t传到前面那个P的方法群里,拼接上version版本号+“#”就是param了。
这个地方如果你觉得他这么写比较迷惑。。可以自己写个示例。
(0, func)()等价于func()。
P.encode方法依旧是位运算。这个方法就很清晰了,然后我们向上找r是哪来的,这个r里的东西,除了lid我们目前不知道以外,其他的都知道。于是不断的下断、调试,找到了这里:
在这里下断,再刷新向上找。
不方便看,去反混淆好的代码里对照着看。

I的话,各种特征显示他就是Cookie..所以这里的逻辑是,先到Cookie里找Lid,也就是
这个Cookie,有就直接返回,否则生成一个。
再经过下面的方法加密,这段代码也非常简单,略过。第一次验证就结束了。
接下来请求验证码图片。initiator直接定位
就是他,下断!
顺着往上找:
Very EASY,下一个。

可以看到验证码图片格式是webp,图片也是做了切片混淆的:

如果不知道啥是webp,可以百度查查。
查了半天不知所云,这句是重点了。可以用canvas导出,也就是可以在前端对图片进行修改。
我们去看看他怎么还原的。依旧是追initiator,因为这里是对响应处理,所以要找靠后一点的位置,最终发现这里十分可疑:
又canvas又math,是不是非常像,下断!跟了一会没什么收获。。于是继续向上不断的找,这里各种异步很恶心。
然后找到了这里。这个32位数组有一点点惊喜。。因为他就是正确的切片还原顺序。他是从一个变量r来的,r是什么?
明白了吗。然后对图片切割还原的代码,其实在上面已经找到了,就是那个canvas所在的整个代码段:
这里可以看看他的逻辑,真正去做的时候也尽量不要用js搞。。。
最后是验证,也就是轨迹了。还是initiator,xhr断点也可以,找到这里下断,不解释原因了,参数都在这。
进到这个JS里,接下来要去研究他的混淆了。
依旧AST,这里我自己尝试写了一份,效果不大好,替换之后验证过不了,于是找了一份大神写的来用了。
还原之后还是相当清晰的。我们来看一下大神是怎么写的吧。为方便理解,我加了一些注释:
这段代码是针对数组类型的字面量混淆,比如下面这种:
这个r啊,t啊实际就是最上面的自执行函数传进来的参数:
两个大数组:
然后下面这段,也是处理字面量,这里做的非常好的一点是根据作用域也就是binding.constantViolations判断该节点值是否被修改过,如果被修改过则不对该节点修改。binding.constantViolations会返回对该节点的修改情况。
这一段是对所有的字符串混淆还原,不得不说这里我没想到居然可以用一个插件直接搞定,D象的字符串混淆函数还是很多的。
处理这一类的混淆:
非常大胆,且有效。其余的大家可以自己去看看,大佬的github地址:https://github.com/daisixuan/dingxiangAST/blob/main/dx.js
然后我们把还原好的用fd替换:
这里URL是会变的,所以用正则。
autoresponser操作一波以后你会发现:
嗯?报错了?此时你可能会首先怀疑是代码出了错,但是仔细看看就会发现,不是代码的问题。是一个同源跨域的问题。我们看一下他正确的请求是什么样的:
再看看我们替换的:
其实就是差了一个头。在浏览器中,加载JS代码是不受跨域限制的,所以也就有了jsonp这种钻空子的跨域方式。但是这里是一个标准的JS,为什么会出现这个问题?其实,加载非当前域名的JS不受跨域限制限通过script标签加载,他可能用了一些奇奇怪怪的方式去加载这个JS,让我们不能顺利的替换响应。所以这里,我们要么改响应头,要么继续改代码。我查了一会资料,并没有找到怎么给fd添加响应头,于是乎。。
你既然是这两个域名不一样,我就改你的代码让你变成一样不就完了。反正我的fd可以构造任意请求的响应,哪怕是不存在的。搜green,找到这里:
把URL改成这样:
再构造一个rule:
现在知道为啥前面那个我要用正则了吗,哈哈~完活,清缓存,刷新。
这个getUA,是返回已经生成好的UA,ua根本不是在这里生成的。需要在其他地方找找。反正js已经被我们反混淆了,直接搜ua就好了。出来的结果不多,可疑的地方也只有这里了:
感觉跟之前param的套路很像:
跟着调用栈往上走,验证浏览器指纹了:
在上面一行一行的往下走,验证不同的浏览器信息:
这些都走完以后,再移动鼠标。他又断下来了,这里就来到了生成轨迹的地方了。D象绑定了很多鼠标事件,每次鼠标动都会生成一小段密文,最后加在一起就是完整的ac:
他取了鼠标事件的ClientX、ClientY,与浏览器的scrollLeft/Top做计算得到PageX、PageY:
下面那几个bs也是加密的方法,
绑定鼠标事件的地方有很多,真正的做滑动轨迹验证的地方是这里:
移动鼠标不断的向sa里push轨迹,至于加密前的是什么,不必太在意。。本身他也更改过数据,要拿到真正的轨迹的话还是要通过hook。
这里代码就在我本地,所以我也就不废那力气了,直接改代码打印出来。。分别打印出时间,X,Y:
这个时间有点怪,不过他既然能过,也就没啥说的了,可能是也有其他事件走了这个方法:
最后就生成了很多的这样的不可见字符数组。。
再加密做处理拼到ua中去。
所以,这些东西是重点。。。都要按他给的监听上。

后记

据说顶象的算法是每天更新2次,所以想扣算法去解决也可以,需要找出每次他改变的地方,不过是否可行不知道,毕竟我这边没有业务上的需求,只是自己找找玩玩,也就没有细抠每一行代码。不过大佬们都是补环境模拟事件监听来调用的,应该是补环境调用比较方便吧。具体大家可以试一试。这次就到这里了,后面分析的有点毛草,没有每一行都细看,这些监听都是一个套路,补对东西就可以了,细心调试,不行就hook,还不行每个生成ua的地方都插桩。目前看D象的JS难度还好。这次从AST上学到了不少知识,看来我的AST水平还是太弱了。

本文分析过程仅供学习交流,并无任何个人以及商业或其他用途。如有不慎侵权,请联系我删除。

推荐阅读更多精彩内容