当前位置 博文首页 > 少年时未觉悟 ,觉悟时不再年少,还危机四伏!:JS 逆向:追踪某

    少年时未觉悟 ,觉悟时不再年少,还危机四伏!:JS 逆向:追踪某

    作者:[db:作者] 时间:2021-07-16 15:33

    背景

    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 手段高明多了,就算知道能调试,但实际上却是一本无字天书!神呐,救救我吧!

    cs