背景
JS 逆向真的是一门技术活,有一个公众号已经做成了高价的付费课程。奈何经费有限,下不了决心购买。所以就把课程所有能试看的视频都看了一遍,窥得了一些 JS 逆向技巧,对我这个小白来说,还是很开眼界的。
今天继续跟踪某招标网站的反扒流程,利用 JS 的单步调试功能,成功定位到了 ajax 发送请求前对 URL 偷梁换柱的地方,也是一大进展。
条件断点和堆栈定位
之前可以断定就是 ajax 请求中存在钩子事件,导致请求 URL 参数变化的,但一直没找到证据。偷学到一些 JS 逆向技巧之后,利用条件断点和堆栈调用链,终于定位到了。
第一步,在 Sources 右侧调试面板中,开启 Ajax 任意请求断点:
第二步,刷新页面,进入断点函数中,是 jquery.min.js 这个文件,点击左下角 {}
格式化,就能清楚地看到断点停留的地方:
利用堆栈链,倒推查看调用方,是 ajax 的 send 方法:
这个 send
方法并没有修改 URL 参数,所以继续在前面 g.open
那里打断点,它解释的函数最终完成了对 URL 参数的重置逻辑。
注意:open 和 send
之前调用的方法,是动态变动的,每一次请求后这个函数名称也会变化,这里是 _$zh
,后面就变成其他了套路如此深,让我等小白只能望而却步!
重写 ajax 的 open
前面利用条件断点后,再单步调试跟踪,最终找到了 ajax 发送之前修改 URL 路径的方法,继续分析它的流程。
首先,网站的 send
方法存在钩子,在 ajax 的发送逻辑中,open
被解释为另一个函数【网页每次刷新,该函数名称都会变化】:
第二步,继续定位到 _$eg
函数定义的地方:
第三步,查看它的代码
function _$eg() {
_$6f();
var _$Rc = _$ep(arguments[1]);
arguments[1] = _$Rc._$s3;
this._$Wl = _$Rc._$Wl;
return _$UA[_$uT[43]](this, arguments);
}
当心,这里存在障眼法,所见的 _6f()
函数并不是真实的函数,它被解释为:
在当前 JS 片段中搜索 _$CU
函数定义:
function _$CU() {
var _$lh = [65];
Array.prototype.push.apply(_$lh, arguments);
return _$wV.apply(this, _$lh);
}
继续追 _$wV
是一堆很长的混淆 JS ,它并没有影响请求参数,暂时不管它。
修改 URL 的地方
后面那句 var _$Rc = _$ep(arguments[1])
函数调用后,ajax 的请求的 URL 就变化了,增加了两个额外参数:
定位到这个方法:
function _$ep(_$SZ, _$uA) {
var _$G_, _$uG = null;
var _$Rc = _$SZ;
function _$kw(_$Mj, _$jU) {
var _$Rc = [];
var _$fL = '';
var _$nC = _$Fm(_$Q5());
_$Rc = _$Rc[_$uT[4]](_$jU, _$Mj, _$uA || 0, _$nC);
var _$eg = _$5Z(743, 6, true, _$Rc);
var _$x$ = _$EJ + _$eg;
_$uG = _$hx(_$4T(_$x$), 2);
return _$aW[_$uT[9]](_$fL, _$3a, _$uT[3], _$x$);
}
function _$fL() {
try {
if (typeof _$SZ !== _$uT[6])
_$SZ += '';
_$G_ = _$Vr(_$SZ);
if (_$O0) {
_$SZ = _$RJ(_$SZ, _$G_);
}
} catch (_$Rc) {
return;
}
if (_$G_ === null || _$G_._$R9 >= 4) {
_$5Z(773, 6);
return;
}
if (_$8H(_$G_)) {
_$5Z(773, 6);
return;
}
_$SZ = _$G_._$Sy + _$G_._$lJ;
var _$fL = _$Rf(_$G_);
var _$nC = _$fL ? _$uT[7] + _$fL : '';
var _$eg = _$3f(_$91(_$nZ(_$G_._$$G + _$nC)));
var _$x$ = 0;
if (_$G_._$_B) {
_$x$ |= 1;
}
_$SZ += _$uT[7] + _$kw(_$x$, _$eg, _$uA);
if (_$fL.length > 0) {
if (_$HV && _$HV <= 8) {
_$SZ = _$s9(_$SZ);
}
if (!(_$Zu & 1024)) {
_$fL = _$s9(_$fL);
}
_$fL = _$uT[62] + _$E6(_$fL, _$uG, 4);
}
_$SZ += _$fL;
}
function _$nC(_$Mj) {
_$40(2, _$nP(5));
if (_$uG === null || _$hS(_$G_) === false) {
return _$Mj;
}
if (typeof _$Mj === _$uT[6] || typeof _$Mj === _$uT[615] || typeof _$Mj === _$uT[77]) {
_$Mj = _$E6(_$Mj, _$uG, 5);
}
return _$Mj;
}
function _$eg(_$Mj, _$jU) {
if ((_$Mj === 'get' || _$Mj === _$uT[247]) && (_$yA & 1) && (_$Zu & 8192) && !(_$G_ && (_$G_._$R9 >= 5 || _$G_._$_B))) {
if (_$jU === _$Xm || _$jU === null || _$jU === '')
_$jU = _$uT[299];
if (_$jU === _$uT[299]) {
return _$jU;
}
}
return '';
}
_$fL();
return {
_$7W: _$Rc,
_$s3: _$SZ,
_$Wl: _$nC,
_$Wm: _$eg
};
}
跟到这里真的是无能为力了,这一堆不明所以的 JS ,可能就是传说中利用凯撒密码混淆过的 JS 。
启示录
第一,这里面如果断点停留事件过久后,即使是浏览器点击,也会出现 403 Forbidden
的问题:
可能这个逻辑中存在时间戳信息。
第二,直接调用一下 open 之前那个修改 URL 的函数,可以拿到后面的参数:
理论上逆向解释出上面这段 JS ,用 Java 实现这个 return
语句得到修正后的 URL ,就可以解决反扒问题了。难点在于怎么用代码在模拟 ajax 请求之前定位到当前页面返回的这个 open 钩子函数呢?人家可是动态生成的啊,还是得解密上面那段钩子函数所在的 JS 片段。
说实话,这个可比上一篇介绍的无限 debugger 手段高明多了,就算知道能调试,但实际上却是一本无字天书!神呐,救救我吧!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论