Hi,大家好,欢迎大家观看由IT猫之家打造的【网络爬虫教学】虫师终极武器之Chromium定制开发系列教学文章的第六篇,如果您是第一次观看本系列教程,请先移步到这里看完前面的文章后再回来哦!大家在学习的过程中,有任何疑问可以留言或加入我们的QQ技术交流群进行探讨: 544185435
前言
前面我们已经实现了多个FP重点检测对象的接口随机化,事实上只要完成这些接口的重写就足以应付大多网站了,不过我们既然要定制就做足全套吧,在FP检测脚本中,尚且还有一些也算是较为重要的判断依据,如:系统字体检测、浏览器插件(plugins) 检测、以及非人为触发事件检测isTrusted等。
FontStyle 随机化的实现
由于每台设备的字体可能存在差异等原因使得第三方服务可以轻松的通过获取字体来判断请求的客户端是否为同一客户端,我们知道Chromium为了规避安全的风险,不对JS提供太多的可用权限,比如字体读取,JS本不具备这种检测权限,但FP巧妙的利用了字体的(长、宽、描边)等属性来精确的判断出客户端是否包含了哪些字体,最终导出一串哈希值以便于作为有力的凭证,另除了这种方式,FP还通过Flash接口来调用字体进行判断(需浏览器支持)
对于系统字体接口的随机,我们可以从传入的font-Style着手,在之前的Canvas随机方案中,我们有做过类似的操作,就直接篡改传入的Style,而对于字体我们也是可以这么干的,只需将其替换成指定的字体即可。
要想实现该接口的随机化,我们首先得要搞懂网站对这个接口的检测是如何实现的以及它是如何运作的,而最好最直接的方法就是直接从目标网站分析并找到答案,我们可以打开browserleaks 然后在关键处下断点,从上图我们可以看到它预设了一堆的常见字体。
从上图,我们可以看到一串:mmm₹▁₺ꜽ�₸׆ẞॿmmmmmmmlli 这样的字符,在我接触过的脚本中它们都会以这种形式作为检测的基准,至于为何一定要用这给字符串,大家可以参考下这篇文章,这位大佬已经解释得很清楚:JavaScript/CSS Font Detector | JavaScript / CSS 字体检测器
从图中我们可以看出,它每次循环都会通过接口style.fontFamily来为当前标签设置字体并获取其宽度与高度,进而与原始的字体进行一一比对,一旦不相等则表示该字体存在,通过该方法几乎可以100%的测得准确结果,而我们要想实现该接口的随机化,可以考虑从两个点着手,首先,就像前面说的,接口每次都会通过 style.fontFamily 来设置字体,那么我们完全可以在这里进行篡改,只要保证每次传入的字体都不一致,则表示肯定会与结果有出入从而达到了随机化的目的,其二,既然它是通过字体的宽度与高度来判断是否成立的,那么我们也可以hook该接口返回随机偏移的数值,从而达到随机化的目的。
通过API查询,我们可以很方便的找到该接口的路径,我们只需按自己的需求实现随机化即可,在这,我建议大家直接修改它的头文件,因为我尝试修改cc文件并未成功,当然大家也可以自行尝试,万一是我操作姿势不对呢,啊哈哈….
plugins 接口随机化的实现
事实上,单单依赖这个插件指纹,服务端是无法判断出是否同一客户端的,也就是说只要完成前面的所有指纹伪造,基本上可以瞒天过海了,但为了满足部分强迫症的看官,我还是有必要将这个给拉出来讲解了。
我们直接在控制台中输入:navigator.plugins,来看看这个插件到底包含了什么
我们可以看到,基本上 navigator.plugins 的子项包含了:四个字段以及2个对象(事实上是一个对象),而事实上我们浏览器里的这个对象基本上都是一样的,所以我开头说可以忽略掉这个接口,我们可以查看每个子项,可以发现它的字段是一样的,同样包含了: name、 filename 、 description 、 MimeType ;那么这样就好办了,直接从以上的字段着手即可。
通过API查询,我们定位到这个 navigator.plugins 的接口位于:third_party\blink\renderer\modules\plugins目录下,我们只需对其实现随机化即可。
上图是插件随机化后的效果,经过篡改:String DOMPlugin::name()、String DOMPlugin::filename()、String DOMPlugin::description()我们可以很轻松的便实现了该接口的随机化。
接口事件触发之底层篡改大法
在FP脚本检测的过程中,还有一项作为检测最为重要的评判指标 “isTrusted”,之所以将它留到最后讲,是为了体现它的价值与其重要性,该字段通常会出现在事件被触发的时,它也是唯一的一个不可直接通过JS语法进行篡改的字段,也就是说前面介绍的所有接口其实我们都是可以通过JS去篡改的,(篡改是没问题,但不见得一定能用,因为部分网站是有针对这个进行检测的),而这个 “isTrusted ”则是例外。
我们来看看上图,我们随便定义了一个鼠标事件以及坐标的事件,然后我们可以发现,它们都携带了一个“ isTrusted ”的字段,并且它的值为false,通过上图我们可以发现,这个接口并不能重写,因为它是只读的,覆盖不了,而我也有尝试过在茫茫网海中寻找可通过JS改写的方案,最终都以失败告终,当然,也有老外告诉我,它们可以通过配合扩展插件去实现,但必须得借助debugger来实现,并且无法取消弹窗,而这方法我也尝试过,是成功了,但是及其耗资源,所以我才打算将其以基于底层的方式实现。
我们再来看看真实触发事件的情况,我们从上图中可以看出,当我们以真实鼠标去触发它, “ isTrusted ” 是为真的,而部分网站以该字段作为判断的依旧,从而判断你是否为机器人;事实上这个接口并不属于是否同一客户端的判断范畴,而是为了校验客户端是否人为发起的请求。
Event接口的isTrusted是一个Boolean类型的只读属性.当事件由用户操作生成时为true,由脚本创建或修改,或通过调用EventTarget.dispatchEvent生成,为false;从这里我们可以看到通过脚本创建或者修改的都会返回false,从而我们可以更加肯定这个字段是肯定会被网站作为重要的判断依旧的。
通过API我们顺藤摸瓜的找到了接口的路径,并将其篡改为true,而,因为所有的事件(如:MouseEvent、PointEvent) 都是派生与event ,所以一旦在其根源修改了,所有的事件都势必会返回真,从而达到我们想要的目的。
最后,附上一张篡改后的效果图,以表示成功。
作者寄语
感谢大家一直阅读本系列文章,到本文为止,我们已经实现了FP里的大部分检测,而通过这些属性的伪造,我们已经可以在大部分网站上执行了,而由于接口都是随机的,所以网站无法确认是否为同一客户端,从而达到了真正的匿名效果;但部分网站还是采用的Cookie形式来记录的,所以,我们也可以通过隐身模式或者通过第三方扩展来屏蔽。另外,请大家不要再私下问我怎么不完全把接口暴露出来,之类的话,首先,一旦方法暴露,势必会遭到第三方网站进行特征收集,没有什么是绝对的,收集过多的数据,照样可以判断你是机器人,或者直接屏蔽你的浏览器,其二,研究是需要时间与精力的,如果大家都是抱着做伸手党的心思来讨要结果,那么您可能找错方向了,天天研究头发都白了,你伸下手就想要,有这么好的事欢迎留言告诉我,我也想要;最后如果大家有业务需求可以找我合作,JS逆向或浏览器定制均可。