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在虚拟主机生成的代码,只要刷新一次浏览器就会重新生成变更和函数名
好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章