关于python:人均瑞数系列瑞数-5-代-JS-逆向分析

7次阅读

共计 8018 个字符,预计需要花费 21 分钟才能阅读完成。

申明

本文章中所有内容仅供学习交换应用,不用于其余任何目标,不提供残缺代码,抓包内容、敏感网址、数据接口等均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关!

本文章未经许可禁止转载,禁止任何批改后二次流传,擅自应用本文解说的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K 哥爬虫】分割作者立刻删除!

前言

瑞数动静平安 Botgate(机器人防火墙)以“动静平安”技术为外围,通过动静封装、动静验证、动静混同、动静令牌等技术对服务器网页底层代码继续动静变换,减少服务器行为的“不可预测性”,实现了从用户端到服务器端的全方位“被动防护”,为各类 Web、HTML5 提供弱小的平安爱护。

在 K 哥往期的文章《人均瑞数系列,瑞数 4 代 JS 逆向剖析》中,具体介绍了瑞数的特色、如何辨别不同版本、瑞数的代码构造以及各自的作用,本文就不再赘述了,不理解的同志能够先去看看之前的文章。

Cookie 入口定位

本文案例中瑞数 5 代网站为:aHR0cHM6Ly93d3cubm1wYS5nb3YuY24vZGF0YXNlYXJjaC9ob21lLWluZGV4Lmh0bWw=

定位 Cookie,首选 Hook 来的最快,通过 Fiddler 插件、油猴脚本、浏览器插件等形式注入以下 Hook 代码:

(function() {
    // 谨严模式 查看所有谬误
    'use strict';
    // document 为要 hook 的对象 这里是 hook 的 cookie
    var cookieTemp = "";
    Object.defineProperty(document, 'cookie', {
        // hook set 办法也就是赋值的办法 
        set: function(val) {
                // 这样就能够疾速给上面这个代码行下断点
                // 从而疾速定位设置 cookie 的代码
                console.log('Hook 捕捉到 cookie 设置 ->', val);
                debugger;
                cookieTemp = val;
                return val;
        },
        // hook get 办法也就是取值的办法 
        get: function()
        {return cookieTemp;}
    });
})();

断下之后往上跟栈,能够看到组装 Cookie 后赋值给 document.cookie 的代码,相似如下构造:

持续往上跟栈,和 4 代瑞数相似,(772, 1) 的地位是入口,4 代有一次生成假 cookie 的过程,5 代就没有了,如下图所示:

再往前跟栈,来到首页代码,这里就是咱们相熟的 call 地位了,图中 _$ug 实际上是 eval 办法,传入的第一个参数 _$Cs 是 Window 对象,第二个对象 _$Dm 是咱们后面看到的 VM 虚拟机中的 IIFE 自执行代码。

VM 代码以及 $_ts 变量获取

获取 VM 代码和 $_ts 变量是第一步,和 4 代相似,复制外链 JS(例如 fjtvkgf7LVI2.a670748.js)的代码和 412 页面的自执行代码到文件,本地间接运行即可,须要轻度补一下环境,缺啥补啥,大抵补一下 window、location、document 就行了,补的具体内容能够间接在浏览器控制台应用 copy() 命令复制过去,而后 VM 代码咱们就能够间接 Hook eval 的形式失去,这里 $_ts 变量的获取和 4 代有点儿区别,4 代咱们的做法是运行完代码后间接取 window.$_ts 就行了,5 代运行完代码后会有一个清空 $_ts 的操作,能够本人跟栈看一下逻辑,要么把清空的逻辑删了,要么定义一个全局变量,而后间接在 call 的中央将 $_ts 的值导出来:

大抵的补环境代码如下:

var eval_js = ""var rs_ts =""

window = {$_ts: {},
    eval: function (data) {eval_js = data}
}

location = {"ancestorOrigins": {},
    "href": "https:// 脱敏解决 /datasearch/home-index.html",
    "origin": "https:// 脱敏解决",
    "protocol": "https:",
    "host": "www. 脱敏解决.cn",
    "hostname": "www. 脱敏解决.cn",
    "port": "","pathname":"/datasearch/home-index.html","search":"",
    "hash": ""
}

document = {"scripts": ["script", "script"]
}

获取 VM 代码以及 $_ts 变量:

善用 Watch 跟踪性能

在跟栈剖析之前,有必要理解一下浏览器开发者工具的 Watch 性能,它可能继续跟踪某个变量的值,对于瑞数这种控制流很多的状况,设置相应的变量跟踪,可能让你晓得你当初处于哪个控制流中,以及生成的数组的变动,不至于跟着跟着不晓得到哪一步了。如下图所示,_$S8 示意目前正处于第 279 号大控制流,_$5x 示意大管制流下的哪个分支,_$mz 示意 128 位大数组。

跟栈剖析

老样子,本地替换一套 412 页面的代码,固定下来,而后开始跟栈剖析。间接从 (772, 1) 开始跟(文中说的第多少号控制流、第几步均为作者本人的叫法,第多少步并不代表实际上的步骤,仅示意关键步骤):

单步进来,_$qh 是传进来的参数 1,行将进入 742 号控制流:

进入 742 号控制流,第 1 步通过一个办法获取了一个工夫戳,进入这个办法外部,对工夫戳进行了差值计算,会发现有两个变量 _$tb_$t1 曾经生成了值:

这两个值也是工夫戳,怎么来的?间接搜寻这两个变量,搜寻后果有几个全部打上断点,刷新断下后往前跟栈,会发现是最开始走了一遍 703 号控制流:

先单步跟一遍 703 号控制流,703 号控制流第 1 步是进入 699 号控制流,返回一个数组,没有特地的,间接扣代码即可:

703 号控制流第 2、3 步别离取数组的值:

703 号控制流第 4、5、6 步生成两个工夫戳并赋值给后面提到的 _$tb_$t1 变量,波及到的办法也没有什么特地的,缺啥搜啥补啥即可:

703 号控制流第 7 步,这里批改了 $_ts 的某个值(VM 代码中,$_ts 被赋值给了另一个变量,下图中是 _$iw),_$iw._$uq 本来的值是 _$ou,批改后的值是 181,这个值也是前面要害 4 位数组中的其中一个,具体逻辑前面再讲。

703 号控制流完结,咱们持续后面的 742 号控制流,742 号控制流第 2 步,将后面生成的工夫戳赋值给另一个变量。

742 号控制流第 3 步,进入 279 号控制流,279 号控制流是生成 128 位数组的要害。

进入 279 号控制流,第 1 步定义了一个变量:

279 号控制流,第 2 步,进入 157 号控制流,157 号控制流次要是做自动化检测

279 号控制流,第 3、4、5 步,做了一些运算,一些全局变量的值会扭转,后续的数组里会用到。

279 号控制流,第 6 步,初始化了一个 128 位的空数组,后续的操作都是为了往这个数组外面填充值。

279 号控制流,第 7 步,进入 695 号控制流,生成一个 20 位的数组。

进入 695 号控制流看一下,第 1 步,取 $_ts 的一个值,生成 16 位数组。

695 号控制流,第 2 步,取 $_ts 里的四个值,与后面的 16 位数组一起组成 20 位数组。

这里留神这四个值怎么来的,以第二个值 _$iw._$KI 为例,搜寻发现有一条语句 _$iw._$KI = _$iw[_$iw._$KI](_$bl, _$n2);,首先等号左边取 _$iw._$KI 的值为 _$Mo,而后 _$iw["_$Mo"] 实际上就是 _$iw._$Mo,后面的定义 _$iw._$Mo = _$1D_$1D 是个办法,所以原语句相当于 _$iw._$KI = _$1D(_$bl, _$n2),其余三个值的起源也是相似的。

695 号控制流完结,回到 279 号控制流,第 8 步,将后面的工夫戳转换成了一个 8 位数组。

279 号控制流,第 9 步,往 128 位数组外面增加了一个值。

_$ae 这个值怎么来的?搜寻下断点并跟栈,发现是结尾走了第 178 号控制流得来的,跟着走一遍即可。

279 号控制流,第 10 步,又往 128 位数组外面增加了一个值,这个值是开始 279 号管制流传过去的。

279 号控制流,第 11、12、13、14 步,工夫戳相干计算,而后生成两个 2 位数组。留神这外面的两个变量,_$ll_$ed,在刷新 cookie、生成后缀的时候可能是有值的,仅拜访主页没有值不影响。

279 号控制流,第 15 步,往 128 位数组外面增加了一个 4 位数组 _$bl,搜寻也能够找到是通过 723 号控制流得来的。

这里的 723 号控制流,实际上是取了 $_ts 某个值进行运算,生成 16 位数组,而后截取前 4 位数组返回的。

279 号控制流,第 16 步,往 128 位数组外面增加了一个 8 位数组 _$Yb

8 位数组 _$Yb 同样搜寻打断点,能够在一个赋值语句断下:

能够看到 _$EJ 的值就是 _$Yb,往前跟栈,会发现先后通过了 657 号、10 号、777 号控制流,其中 777 号控制流是入口:

如果单步跟 777 号控制流,你会发现步骤较多,两头有些语句不好解决,且容易跟丢,所以咱们这里就间接关注 657 号控制流就行了,777 号控制流间接到 10 号控制流,再到 657 号控制流,两头的一些过程临时不论,跟到缺什么的时候再说(后续有很多取值赋值等操作都是在 777 号控制流里实现的,能够留神一下),这段逻辑在本地体现的代码如下图所示:

这里间接单步跟一下 657 号控制流,第 1、2 步 new 了一个办法。

这里就要留神了,容易跟丢,先进入 _$bH 办法打上断点,而后下一个断点就走到外面了,接着在单步调试,会进到另一个小的控制流外面,如下图所示:

开始单步跟第 96 号小控制流,第 1 步定义了一个变量。

96 号小控制流,第 2 步将 _$PI 的值赋值给了 _$fT,而 _$PI 的值其实是 window.localStorage.$_YWTUwindow.localStorage 外面有很多值,这个货色咱们文章最初再讲,其中一些值与浏览器指纹相干,这里先晓得他是取值就行了。

96 号小控制流,第 3 步,进入第 94 号小控制流,最终生成的是一个 8 位数组,这个其实就是后面咱们想要的 _$Yb 的值了。

前面没有什么特地的,两头几步我就省略了,照着扣代码就行了,而后 96 号小控制流,第 4 步,就将 _$EJ 的值赋值给 _$Yb 了。

到这里先别急着完结,前面还有要害的几步,96 号小控制流,第 5 步,又遇到了和后面相似的写法。

同样的,先进 _$pu 打断点,再单步跟。

来到另一个小控制流,如下图所示:

10 号小控制流第 1 步,取 window.localStorage.$_cDro 的值,转为 int 类型,赋值给 _$5s,这个 _$5s 后续也会加到 128 位大数组外面。

10 号小控制流后续还有几步,没啥用能够省略,最初一步返回 96 号小控制流。

而后 96 号小控制流后续也没啥了,返回 657 号控制流。

此时咱们曾经拿到 _$Yb 了,777 号控制流就先不论了,后续还有些代码先不论不必扣,等用到的时候再说,返回 279 号控制流,接着后面的步骤,来到第 17 步,变量 _$5s 通过 264 号控制流后,生成了一个值并增加到 128 位大数组外面,而 _$5s 的值正是后面咱们跟 _$Yb 时,通过 777 号控制流拿到的,实际上也就是取 window.localStorage.$_cDro 的值,转为了 int 类型。

279 号控制流,第 18、19、20 步,往 128 位数组外面增加了两个定值、一个 8 位数组。

279 号控制流,第 21 步,往 128 位数组外面增加了一个 undefined 占位,后续会有操作将其填充值。

279 号控制流,第 22 步,进入 58 号控制流,58 号控制流与 window.localStorage.$_fb 的值无关,如果有这个值,就会生成 20 位数组,如果没有就是 undefined。58 号控制流就只有一步,返回一个变量,本文中是 _$0g

这个 _$0g 是咋来的呢?同样的间接搜寻,下断点,发现是通过 112 号控制流得来的,往前跟栈,同样是先通过了 777 号控制流,和之前的状况相似,两头的过程就不看了,间接看这个 112 号控制流。

本文中,112 号管制流传的参是 _$bd[279]$_fb,112 号控制流第 1 步,进入 247 号控制流。

247 号控制流就 3 步,先将 window.localStorage 赋值给一个变量,而后取其中 $_fb 的值再返回。

112 号控制流第 2、3 步,一个 try-catch 语句,取 window.localStorage.$_fb 计算失去 25 位数组,而后取前 20 位并返回,这就是后面咱们须要的 _$0g 的值了。

279 号控制流,第 23 步,将后面 window.localStorage.$_fb 计算失去的 20 位数组增加到 128 位大数组外面,留神这一步如果没有 window.localStorage.$_fb 值的话,是不会增加的。

279 号控制流,第 24 步,对一个变量进行位运算,而后取 window.localStorage.$_f0 进行运算,如果 $_f0 为空的话是不会往 128 位大数组里增加值的。

279 号控制流,第 25 步,对一个变量进行位运算,而后取 window.localStorage.$_fh0 进行运算,如果 $_fh0 为空的话是不会往 128 位大数组里增加值的。

279 号控制流,第 26 步,对一个变量进行位运算,而后取 window.localStorage.$_f1 进行运算,如果 $_f1 为空的话是不会往 128 位大数组里增加值的。

279 号控制流,第 27 步,进入 611 号控制流,611 号控制流次要是检测 window.navigator.connection.type,即 NetworkInformation 网络相干信息,外面判断了 type 是不是 bluetoothcellularethernetwifiwimax,失常的话应该返回 0。

279 号控制流,接下来几步都是相似的,这里就间接统称第 28 步了,首先对一个变量进行位运算,而后别离取 window.localStorage.$_frwindow.localStorage.$_fpn1window.localStorage.$_vvCIwindow.localStorage.$_JQnh 进行运算,同样如果这些变量为空的话,也是不会往 128 位大数组里增加值的。

279 号控制流,第 29 步,往 128 位大数组里增加了一个定值 4,本文中该变量名是 _$kW

_$kW 这个变量是咋来的,和后面的套路相似,间接搜寻下断,同样是通过结尾的 777 号控制流得来的,如下图所示:

持续 279 号控制流,两头有一些变量位运算之类的就省略了,第 30、31 步,取了一个 https:443 的长度进行计算,先后往 128 位大数组里增加了一个定值和一个 9 位数组。

279 号控制流,接下来几步都是在取值,都差不多,就统称为第 32 步了。

279 号控制流,第 33 步,之前 128 位大数组第 12 位是个 undefined,这里就将第 12 位填充上了一个 4 位数组,其中有个变量 _$8L,后面咱们跟步骤的时候就有一个变量始终在做位运算,此处的 _$8L 就是这么来的。

279 号控制流,最初两步,原来的 128 位大数组,只取有值的前 21 位,一共有多少位与 window.localStorage 的某些值无关,有值的话就长一些,没有就短一些,而后再将数组的每个元素合并成最终的一个大数组并返回,279 号控制流就完结了。

返回到文章结尾的逻辑,279 号控制流完结,返回到 742 号控制流,第 2 步,定义了一个变量并生成了一个 32 位数组。

742 号控制流,第 3 步,取 $_ts 外面的某个值并赋值给一个变量。

742 号控制流,第 4 步,将后面 279 号控制流失去的大数组与上一步 $_ts 外面的某个值进行合并,合并后计算失去一个值。

742 号控制流,第 4 步,将上一步失去的值进一步计算失去一个 4 位数组,再将其和大数组合并。

742 号控制流,接下来几步是对工夫戳进行各种操作,这里统称为第 5 步。

742 号控制流,第 6 步,将上一步失去的 4 个工夫戳进行计算,失去一个 16 位数组。

742 号控制流,第 7 步,将上一步失去的 16 位数组进行异或运算。

742 号控制流,第 8 步,将上一步的 16 位数组进行计算,失去一个字符串。

742 号控制流,第 9 步,正式生成 cookie 值,其中 _$bd[274] 定值,个别视为版本号,将上一步失去的字符串、之前失去的大数组和一个 32 位数组进行计算、组合,失去最终后果。

742 号控制流完结,返回 772 号控制流,利用了一个办法,组装 cookie,而后赋值给 document.cookie,整个流程就完结了。

代码中用到的 $_ts 的值须要咱们本人去匹配进去,动静替换,这些步骤和 4 代是相似的,本文就不再反复叙述,能够参考 K 哥 4 代的那篇文章进行解决即可。

后缀生成

本例中,申请头中有个 sign 参数,Query String Parameters 有两个后缀参数,这两个后缀和 4 代相似,都是瑞数生成的。

和 4 代的解决办法一样,咱们下一个 XHR 断点,先让网页加载结束,而后关上开发者工具,过掉有限 debugger 后,点击搜寻就会断下,如下图所示:

往上跟栈到 hasTokenGet,是一个 sojson 旗下的 jsjiami v6 混同,不值一提,重点是 jsonMD5ToStr 办法,先对传进去的参数做了一些编码解决,最初返回的是 hex_md5,和在线 MD5 加密的后果是一样的,阐明是规范的 MD5。

重点来看瑞数的两个后缀生成形式,和 4 代一样,XMLHttpRequest.sendXMLHttpRequest.open 被重写了,如下图所示,在 XMLHttpRequest.open 下个断点,也就是图中的 _$RQ 办法,arguments[1] 就是原始 URL,通过图中的 _$tB 办法解决后就能拿到后缀。

跟进图中的 _$tB 办法,_$tB 办法里嵌套了一些其余办法,走一遍逻辑,到图中的 _$5j 办法里,后面的一部分都是在对传入的 URL 做解决。

接下来是生成了一个 16 位数组:

而后这个 16 位数组通过一个办法后就生成了第一个后缀,如下图所示,本文中这个办法是 _$ZO

跟进 _$ZO 办法,次要有以下 5 步:

第 1 步:生成了一个 32 位数组;

第 2 步:将之前的 16 位数组以及两个变量拼接生成一个 50 位的数组;

第 3 步:进入 744 控制流,这里你会发现和之前咱们跟 cookie 时的 742 号控制流是一样的,反复走了一遍,所以这里就不再跟了;

第 4 步:将生成的第一个后缀值进行解决,失去一个两位的字符串,这个字符串在获取第二个后缀的时候会用到;

第 5 步:将第一个后缀名称和值进行拼接并返回,此时,第一个后缀 hKHnQfLv 就生成了。

接着后面的 _$5j 办法,图中的 _$5j 这一步,就是获取第二个后缀 8X7Yi61c 的值:

次要是看一下图中的 _$UM 办法,先将后面生成的两位的字符串与 URL 参数进行拼接,而后会通过一个 _$Nr 办法就能失去第二个后缀的值了。

再来看一下 _$Nr 办法,学生成一个相似 53924 的值,而后一个 try 语句,留神这里有个办法,图中的 _$Js 办法,外面用到了 $_ts 外面的某个值,前面又生成了一个由数字组成的字符串,再次通过组合、计算后失去最终的值。

回到后面的 _$UM 办法,前缀 8X7Yi61c 与值组合,自此,两个后缀都拿到了:

指纹生成

咱们后面曾经剖析了,在往 128 位数组里增加值的时候,会有取 window.localStorage 外面的某些值进行计算的步骤,这些值就是取浏览器 canvas 等指纹生成的,指纹随机就能并发,通常拜访独自的一个 html 页面是不校验指纹的,生成的短 cookie 就能通过,然而一些查问数据接口会校验指纹,通过触发 load 事件来向 cookie 里增加指纹,使得 cookie 长度变长,怎么查找指纹在哪里生成的,这里举荐间接看视频材料,曾经讲得很分明了,篇幅太长,本文就不再赘述了,材料链接:https://mp.weixin.qq.com/s/DE…

正文完
 0