某网站瑞数5代环境检测JS逆向分析

1. 写在前面

  逆向技术确实很有挑战,经常有看到各种爬虫与逆向群里面五花八门的奇技Y巧。爬虫领域里面就需要更多的分享,才能够成就更好的自己。本期要说的这个网站用到的加密技术相对比较难!我在找案例的时候同时也在学习其他大佬的思路与技巧,学习逆向没有捷径,只能靠我们自己一点点的去分析与累积

目标站点

aHR0cHM6Ly9xaWthbi5jcXZpcC5jb20vUWlrYW4vSm91cm5hbC9TdW1tYXJ5P2tpbmQ9MSZnY2g9OTUyNDNYJmZyb209UWlrYW5fSm91cm5hbF9TdW1tYXJ5

2. 目标分析

首先,打开这个案例网站,点击翻页。本次主要针对的就是获取更多加载里面内容的加密分析,这个网站里面搜索也是加密区
在这里插入图片描述
本次分析的目标就是问号后面的这串密文,只要把这串加密的密文逆向还原出来,就可以通过此url获取到更多数据内容

这个网站检索区那块网上已经有较多的人对此进行了分析,有硬刚加密瑞数的,也有RPC技术的

这个url到其它浏览器上面打开看看,发现没有收到任何数据,这是因为维普期刊网站除了对这个参数做了加密外,还做了特殊处理让我们无法直接从url获取数据,必须从维普期刊网站打开url才能获取数据,这样在一定程度上就保护了维普期刊上面的数据,从而实现反爬的目的

前一部分:

https://*.com/Journal/RightArticle?

加密部分:

X2sCXRB4=0IsPQ0alqEtID3EakumIk.NZiLQ9yQeCdROMNRaLtSO0U74P8MpIcElFncx8UMr8l36GA6zSqfi_DEzmmnT2wbnwsgTuYbvql

经过多次翻页加载发现前一部分是固定的,而加密部分则是不断变化的,并且字符串X2sCXRB4也是固定的

很明显整个URL通过加密后生成一段密文与前一部分进行拼接而成

翻页动作网站使用了ajax技术来实现,不管如何封装发送ajax请求,底层一定是用的XMLHttpRequest技术实现,否则无法无刷新发送请求给服务器,然后网页无刷新返回数据

触发翻页返回数据的流程大至如下:

点击翻页->经过某些初始化->进行加密并拼接完整URL->发送给服务器->服务器接受请求->呈现到网页

接下来要做的就是把加密拼接URL这个环节的代码抠出来进行调用,拼接完整的翻页URL,才能不断获取到更多的数据

加密代码一般很多网站会有混淆,就跟有价值的的网錾数据都会要求账号登录一样(后续会针对模拟登陆的多种方案做一个详细的讲解),直接搜索一般搜索不到的

而这个网站无法直接定位到加密处代码,也没有能够搜索的关键词,所有我们只能从接口开始定位

通过XHR断点:
在这里插入图片描述

因为每次一的翻页都是经过Journal/RightArticle,所以我们直接断它,然后点击其他翻页就会在Ajax请求处给断住

在这里插入图片描述

当然,也可以通过Initiator
在这里插入图片描述

在XMLHttpRequest技术中,定位到栈内的发送函数send是实现与服务器通信的最后一步。通过该函数,将请求的URL发送给服务器。栈内的最后一个函数是加密函数,然而,鉴于加密函数通常经过混淆处理,所以不建议优先研究该函数。相反,建议先分析send函数的逻辑

通过这个请求服务器流程可以看出,加密一定是在send发送前实现的

XMLHttpRequest实现流程:

xhr = XMLHttpRequest()
xhr.open('get', 'http://*.com...', false)
xhr.send(data)

定位send函数:
在这里插入图片描述

因为jquery.js文件是第三方js框架,这个框架里面一般是不会写加密函数的,根据前面的分析,网站都是会改写open函数

分析open函数,给open函数所在的行打上断点,并重新点击翻页:
在这里插入图片描述

把鼠标放在open函数上面,可以发现这个open函数被重写了,我们点链接进入到被重写的函数里面:

在这里插入图片描述

向下跟执行代码跟进,进入到n.apply(this, arguments)
在这里插入图片描述

这个函数很可能是一个加密函数入口,我们继续往下走并在控制台看看

在这里插入图片描述

上面猜测这个函数就是一个加密函数入口

_$8f(arguments[1])

对加密的这个url重写发送请求,得到的结果跟现在是一样的,可以确定这个加密的url就是我们要逆向的url

明明是一个明文字符串,经过函数_KaTeX parse error: Expected group after '_' at position 20: …以后就变成了一个对象,对象里面_̲bt值就是加密的url

return _$6y[_$V8[36]](this, arguments)

这行代码里面的_$6y就是open函数,如下图所示:

在这里插入图片描述

由此可以确定了open函数就是被重写了,最后返回的还是open函数

分析加密函数_$8f

function _$8f(_$H_, _$wH) {
    
    
    var _$Pv, _$pq = null;
    var _$8_ = _$H_;
    function _$Rv(_$tx, _$65) {
    
    
        var _$xW = [];
        var _$DY = '';
        var _$dF = _$u3(_$4i());
        _$xW = _$xW[_$V8[9]](_$65, _$tx, _$wH || 0, _$dF);
        var _$eq = _$4b(923, _$2l[186], true, _$xW);
        var _$Q6 = _$rW + _$eq;
        _$pq = _$Tt(_$XH(_$Q6), _$2l[27]);
        return _$hT[_$V8[5]](_$DY, _$ph, _$V8[26], _$Q6);
    }
    function _$_L(_$tx) {
    
    
        if (_$tx._$V8) {
    
    
            var _$xW = _$ht(_$ht(_$tx._$wa, _$V8[38])[0], _$V8[78])[1];
            if (_$xW[_$V8[3]](_$Z_) >= 0 && _$xW[_$V8[3]](_$ph) >= 0) {
    
    
                return true;
            }
        }
        return false;
    }
    function _$Hc() {
    
    
        try {
    
    
            if (typeof _$H_ !== _$V8[0])
                _$H_ += '';
            _$Pv = _$wa(_$H_);
            if (_$_L(_$Pv)) {
    
    
                return;
            }
            if (_$45) {
    
    
                _$H_ = _$MJ(_$H_, _$Pv);
            }
        } catch (_$xW) {
    
    
            return;
        }
        if (_$Pv === null || _$Pv._$uF > _$2l[40]) {
    
    
            _$4b(953, _$2l[186]);
            return;
        }
        if (_$_u(_$Pv)) {
    
    
            _$4b(953, _$2l[186]);
            return;
        }
        _$H_ = _$Pv._$iB + _$Pv._$fp;
        var _$DY = _$s3(_$Pv);
        var _$dF = _$DY ? _$V8[78] + _$DY : '';
        var _$eq = _$3v(_$zB(_$iL(_$Pv._$nu + _$dF)));
        var _$Q6 = 0;
        if (_$Pv._$9h) {
    
    
            _$Q6 |= 1;
        }
        if (_$bP & _$2l[49]) {
    
    
            _$Q6 |= _$2l[40];
        }
        _$H_ += _$V8[78] + _$Rv(_$Q6, _$eq, _$wH);
        if (_$DY.length > 0) {
    
    
            if (_$24 && _$24 <= _$2l[149]) {
    
    
                _$H_ = _$e7(_$H_);
            }
            if (!(_$bP & _$2l[9])) {
    
    
                _$DY = _$e7(_$DY);
            }
            _$DY = _$V8[66] + _$zL(_$DY, _$pq, _$2l[40]);
        }
        _$H_ += _$DY;
    }
    function _$xa(_$tx) {
    
    
        _$5H(_$2l[27], _$Ka());
        if (_$pq === null || _$lh(_$Pv) === false) {
    
    
            return _$tx;
        }
        if (typeof _$tx === _$V8[0] || typeof _$tx === _$V8[447] || typeof _$tx === _$V8[347]) {
    
    
            _$tx = '' + _$tx;
            if (_$tx.length <= _$pC) {
    
    
                _$tx = _$zL(_$tx, _$pq, _$2l[178]);
            }
        }
        return _$tx;
    }
    function _$BZ() {
    
    
        return _$pq !== null;
    }
    function _$dF(_$tx, _$65) {
    
    
        if ((_$tx === 'get' || _$tx === _$V8[106]) && _$BZ() && (_$4o & 1) && (_$bP & _$2l[49]) && _$Pv && _$Pv._$uF < _$2l[178] && _$P4(_$Pv)) {
    
    
            if (_$Pv._$9h) {
    
    
                this._$Tt = true;
            } else {
    
    
                if (_$65 === _$Sc || _$65 === null || _$65 === '') {
    
    
                    _$65 = _$V8[105];
                }
                if (_$65 === _$V8[105]) {
    
    
                    this._$Tt = true;
                    return _$65;
                }
            }
        }
        return '';
    }
    _$Hc();
    return {
    
    
        _$ni: _$8_,
        _$bt: _$H_,
        _$rI: _$xa,
        _$K$: _$dF,
        _$Wu: _$Qw,
        _$Tt: false
    };
}

函数里面又定义了几个函数和变量,在最后调用了Hc()函数,为什么这里要调用一下?由上面可知8f函数的结果有返回值,并且返回了加密后的url,所以这里调用函数_$Hc()很有可能是在生成加密参数

最后返回一个对象,在对象里面找一下键为bt的,确实找到了这个键对应的值就是加密的url,也就是说$H就是好个加密后的url

这个H哪里来的?在整个8f函数里面,只有_Hc()函数调用执行了,其它函数只是声明了函数并没有调用,可以猜测H这个url值肯定是在_$Hc()函数生成的,因为别的函数没有调用

分析_$Hc()函数

这个函数里面真正生成url的是下面这行代码:

_$H_ += _$V8[78] + _$Rv(_$Q6, _$eq, _$wH)

V8[78]是一个问号,而后面的_$Rv函数才是真正生成url加密参数的函数

现在进一步找到了真正生成加密参数的是函数_$Rv,而这个函数正是上面大函数_$8f里面的第一个定义的函数

分析_$Rv函数

在这个函数里面真正加密的函数,_$4b才是真正加密的函数,后面的代码是把加密好的代码通过函数concat接拼起来

return _$hT[_$V8[5]](_$DY, _$ph, _$V8[26], _$Q6)

_$ph值是:

X2sCXRB4

这个字符串不就是url问号后面跟的第一个字符串吗,从这一点也可以确定真正生成加密字符串的函数是_$4b

分析_$4b函数,进入这个函数发现有几千行代码,_$4b函数是一个控制流程平坦化结构的反爬加混淆代码

url加密参数就是在这个平坦流代码中加密生成的,简化_$4b代码如下

var _$1V, _$IF, _$f4 = _$P2, _$je = _$oY[0];
function _$4b(_$xI, _$H_, _$wH, _$_M) {
    
    
    function _$bc() {
    
    }
    function _$Lp() {
    
    }
    function _$gd() {
    
    }
    function _$q9() {
    
    }
    function _$xQ() {
    
    }
    function _$Po() {
    
    }
    var _$Pv, _$Xl, _$Rv, _$BZ, _$_L, _$pq, _$gy, _$xp, _$Xt, _$xW, _$8_, _$cy, _$iO, _$Xi, _$bX, _$DY, _$dF, _$Q6, _$p4, _$aB, _$9P, _$eq, _$zH, _$Hc, _$ss, _$xq, _$Hu, _$ya, _$xa, _$hI;
    var _$LP, _$vc, _$Rb = _$xI, _$eS = _$oY[1];
    while (1) {
    
    
        _$vc = _$eS[_$Rb++];
      if (_$vc < 256) {
    
    
        ...
      }
    }
}

省略处大量if逻辑

到此已经找到具体的加密代码,后面就是如何把这些混淆的代码抠出来,平坦流结构代码是难点重点,时间有限,之后找时间补

如果走RPC的话,完全可以不用抠代码,只要找到加密函数入口直接调用就行

另外,感兴趣的朋友可以看看这个网站,文章里面的函数,以及变量名称是动态变化的,因为Chrome在虚拟主机生成的代码,只要刷新一次浏览器就会重新生成变更和函数名

  好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章

猜你喜欢

转载自blog.csdn.net/qiulin_wu/article/details/131837792