声明
本文仅供学习参考,如有侵权可私信本人删除,请勿用于其他途径,违者后果自负!
如果觉得文章对你有所帮助,可以给博主点击关注和收藏哦!
前言
目标网站:aHR0cDovL3d3dy5mYW5nZGkuY29tLmNuL25ld19ob3VzZS9uZXdfaG91c2VfZGV0YWlsLmh0bWw=
在此十分感谢十一姐与K哥爬虫两位大佬的指导。
插句个人内容:本人最近正在找工作,工作城市:广州。如果有合适的机会,希望有大佬可以内推,感激不尽。????js逆向马马虎虎,app会弱一些。感兴趣的大佬可以私信本人要简历。
参数分析
对于很多的爬虫工程师来说,rs可以说是一座难以攀爬的大山了,确实是这样。不过有逆向就是这样【耐心+细心=大佬】废话不多说正式进入主题,rs最显著的特征之一就是cookie反爬,相信很多人都已经了解过了,对于4代来说。会发送两次请求(以本网站为例),第一次会发送一个202的请求,服务器端会生成第一个cookie,然后通过客户端生成cookie并且携带第一次生成的cookie请求,然后发送第二次的请求获取数据,也就是状态码为200的请求。
打开F12后会出现两个debugger,如图:
鼠标右键 Never pause here,就可以轻松过掉了。当然也可以使用最新版的火狐浏览器。通杀无限 debugger,目前只有 1% 的人知道!
第二个属于百度统计,不影响,可忽略。HttpOnly就是服务器生成,反之客户端生成。最为主要的就是FSSBBIl1UgzbN7N80T,长度为217位。所以主要目标也就是本地复现该cookie的算法。
既然已经知道是cookie的问题,没有什么比hook cookie来的快了。直接使用油猴脚本注入,刷新页面即可断住。
找到所需的cookie,然后回溯堆栈。hook会断住两次,可以看到组装的过程,第一次生成了一个假的cookie,但是后面会使用到,第二次才是真正的cookie。
扣代码
一共分为两部分,假cookie的算法逻辑和真cookie的算法逻辑。推荐使用vscode调试,pycharm实在是太卡了。
假cookie逻辑
开始之前最好把首页代码固定一下,避免意外发生,因为是动态生成的。
简单看一下202页面的内容:
- content的内容随着每次请求都是变化,后面在VM中的初始化会使用;
- c.FxJzG50F.dfe1675.js 打开是乱码,里面包含了重要内容window.$_ts,并且会执行生成庞大的VM代码,至关重要。
- script的内容也会使用到部分逻辑。
通过前文的hook,向上追溯堆栈,其实就能发现函数入口。或者直接搜索.call。为了方便每次都能够调试,我们需要找到函数入口,在这里简单修改一下代码,在call上方加上debugger,这样就可以自动断住了。前期的准备工作到这里就okl,那么现在要处理第一个cookie了。_$vK 就是第一个假cookie,为了方便美观简单修改一下代码。
接下来不断的运行,会不断的报缺少各种函数和变量未定义的错误,只需要慢慢补就可以了,然后再和网页上对比生成是否一致即可。到此第一步就完成了。需要注意:
- content在这里被使用了,需要留意使用位置。
在代码开头就已经使用,WR函数获取到了meta标签中的内容并返回。执行后再删除removeChild。2、代码运行参数对比发现异常时就要注意在代码开始时是否已经被调用过了。3、留意window.$_ts被使用的地方,后续要动态变化。
经过一系列的缝缝补补就生成了第一个cookie。和网页对比一下。
一模一样,第一步结束。函数接着向下会判断cookie是否存在。看一下5G函数。给os赋值"FSSBBIl1UgzbN7N80T",这个也是为什么前文会被hook两次的原因。Ut函数就是hook的逻辑,就是document.cookie的赋值,没什么好说的。_$uw是localStorage,复制下来即可,不是很重要。
真cookie逻辑
剩下的就是第二层真cookie的生成了,跳进去会发现进入了无尽的if-else,顶着头皮硬刚几个小时就可以拿下来了。
在这里可以使用watch记录控制流的变化,发现大佬的文章处处是细节。
控制流的顺序如下:这是比较大的控制流,其中还有不少小的控制流嵌套在其中。在这里可以选择将每个控制流组成一个函数,也可以使用switch-case的方法。本文选择的是第一种。第一步会跳到747号,直接拿下。此时可以这样定义函数
function _$eQ768() {
var _$vK = _$eQ747(1);
……
}
继续F11跳进747号,然后继续定义函数,往里面填充内容,依然是缺啥补啥。
不断地步入会发现在709号中取了第一次生成的假cookie
可以直接改写,例如:
var _$x1 = _$No(); // 取第一次生成的假cookie
紧接着会调用ot方法将cookie变成一个162位数组。
然后取值操作,for循环进行位运算等等操作,然后将返回一个长度4位的数组,至此709号控制流结束。
到了268号控制流,里面又包含了很多小的控制流看一看该固定就固定,该补就补,问题不大。
有一个小的点要注意,154号会检查自动化工具是否使用,由于是扣代码,所以可以直接跳过。
比较重要的是668号,这里面使用了ts的四个值,这四个值在每次请求的时候位置都是变化的,所以很多cookie生成后不能够成功返回数据的原因有很大一部分都在这里。
这一部分的处理也很简单,只需要知道每个值对应的数值和名称做一个映射,键值对关系即可动态变化。
一共二十个值,举个例子怎么变成对象。
ts_20_object = {
'_$oB':0
…… // 以此类推
}
再顺着把逻辑补完即可。
685的逻辑如下。
然后就是回到747,至此恭喜代码就全部扣完了。大致代码结构如下:
还需注意的是凡是使用到ts的变量最好都要做好记录然后进行替换,如果遇到问题本地本地打上断点和浏览器对比,排除错误。
MmEwMD逻辑
这是rs的又一大显著特征,即在请求中携带MmEwMD参数字段,处理这一步也十分简单,观察请求都是xhr请求,直接下xhr断点即可。只要包含此字段就断下。
继续单步发现已经请求包中已经携带了该参数,那么必定是在发送之前做了手脚。
原生的xhr发送最为关键的就是open和send,分别观察一下,最后发现端倪是在open方法中。open方法被重写了。正常状态应该是:
//1、创建xhr的对象
let xhr = new XMLHttpRequest()
xhr.open('GET',"https://www.example.com")
区别明显,所以肯定是在这里动了手脚。
第一个函数没什么东西,继续下一个。8H函数的参数是请求的接口地址字符串。发现已经生成了参数MmEwMD。
主要逻辑就是779号控制流,还原下来就可以得到最后的结果。最后再来验证一下结果: