猿人学爬虫攻防赛 第10题

查看 133|回复 10
作者:天空宫阙   
猿人学爬虫攻防赛 第10题
题目地址
https://match.yuanrenxue.com/match/10
总的评价:4代R数的低配静态版的壳 + jsjiami.com.v6 + obfuscator
最近学习了AST拿这个题试一下水,从编写提高代码可读性的插件、搭建本地调试环境、扣代码花了整整3天,最后成功的一刻,喜悦满溢。
调试工具和本地调式环境的搭建
确保安装以下软件或环境  
  • fiddler 使用AutoResponser 替换响应 用于搭建调试环境
  • python 和 nodejs 环境和相关依赖,如python的flask nodejs的express等
  • chrome 开发者工具

    [ol]
  • 启动调试代码中app.py
  • 在fiddler AutoResponer中添加两条规则(如图所示)
  • EXACT:https://match.yuanrenxue.com/match/10 -> http://127.0.0.1:5000/10
  • regex:https://match.yuanrenxue.com/eval.*? -> http://127.0.0.1:5000/eval


    01.png (91.27 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传



    [/ol]
    经过这样配置重新打开猿人学第十题的首页仍可以加载数据,按F12打开chrome开发者工具不会进入无限debugger说明调试环境已经搭建成功。
    简单讲一下几处使用AST提高代码可读性的地方  
    [ol]
  • 把$_ts["dfe1683"]的字符串解密为eval执行的字符串
    let yuanrenxue_36='';var yuanrenxue_59=968;var i锝塴='jsjiami.com.v6',yuanrenxue_150=[i锝塴, .... 此处省略几百行代码
    还原后的结果  
    let yuanrenxue_36 = '';
    var yuanrenxue_59 = 968;
    for (let yuanrenxue_229 = 0; yuanrenxue_229
  • 把首页请求数据的代码还原,用obfuscator混淆的,其他还有几段,ob混淆的都不重要但无限debugger很烦,还原之后就清爽多了。请求在这里但请求的参数m并不是在这里生成的,具体后面再讲。
    [/ol]
    window["url"] = "/api/match/10";
    request = function () {
      const _0x434855 = {
        "page": window["page"]
      };
      $["ajax"]({
        "url": window["url"],
        "dataType": "json",
        "async": false,
        "data": _0x434855,
        "type": "GET",
        "beforeSend": function (_0x291a2d) {},
        "success": function (_0x1f076c) {
          if (window["page"]) {} else window["page"] = 1;
          window[_0x1f076c["k"]["k"]["split"]("|")[0]] = parseInt(_0x1f076c["k"]["k"]["split"]("|")[1]);
          _0x1f076c = _0x1f076c["data"];
          let _0x110e6b = '';
          // let _0x14dced = [省略一些不重要的数据,总的来说这段都不重要]
          });
          $("#feedContent0")["text"]('')["append"](_0x110e6b);
        },
        "complete": function () {},
        "error": function (_0x402e16, _0x4f092f, _0x1c5dfd) {
          $(".page-message")["eq"](0)["addClass"]("active");
          $(".page-message")["removeClass"]("active");
        }
      });
    };
    request();
  • eval中控制流平坦化代码的ifelse转switch,减少单步调试时跳的次数
    具体见github
    https://github.com/skygongque/match-yuanrenxue/tree/master/match10
    由于控制流平坦化代码中有对自增变量进行加减操作,以及最大的控制流中属于是多个函数合并的大型控制流代码,存在多个入口和出口,我还还原不了请大佬指点
    [/ol]
    入口寻找
    eval入口
    它套了一个4代R数的壳,易得入口是下图,首页搜call即可


    02.png (183.68 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传

    eval中m参数加密的入口
    上面这个特别长的eval执行完了什么也没返回还是不知道m参数加密的的入口,细心一点可以发现这时候发的ajax请求会自己加上m参数
    控制台输出以下xhr的open方法,发现果然被重写了,还是熟悉的配方  


    03.png (41.57 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传

    进入这个函数打上断点,翻页重新请求一下进入这个函数就看到入口了


    04.png (179.23 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传


    function _yrxyA$(_yrx7jl, _yrxcze) {
        try {
          if (typeof _yrx7jl !== _yrxQ9C[6]) {
            _yrx7jl += '';
          }
        } catch (_yrxrqQ) {
          return _yrx7jl;
        }
        if (!(_yrxCJw & 1024)) {
          _yrx7jl = _yrxR2F(_yrx7jl);
        }
        var _yrx$Kn = _yrxtSa(_yrx7jl);
        if (_yrx$Kn === null) {
          return _yrx7jl;
        }
        if (_yrx$Kn._yrxKni > 3) {
          return _yrxtY2(_yrx$Kn);
        }
        var _yrxmEu = _yrxWKg(_yrxyHJ(_yrx5XG(_yrx$Kn._yrx2ad + _yrx$Kn._yrxAmM)));
        var _yrx7jl = _yrx$Kn._yrxCiX + _yrx$Kn._yrxAmM;
        if (_yrx$Kn._yrxAmM === '') {
          _yrx7jl = _yrx7jl + '?';
        } else {
          _yrx7jl = _yrx7jl + '&';
        }
        var _yrx2LR = _yrx$Kn._yrxiv8 + _yrx7jl;
        // 入口,在eval中搜(779 可以直达
        _yrx2LR += _yrxBXT(779, _yrx$Kn._yrxQZs, _yrxmEu, _yrxcze);
        _yrx2LR += _yrx$Kn._yrxcFt;
        return _yrx2LR;
      }
    扣代码
    接下去就是痛苦的扣代码环节了,比较浏览器的结果和nodejs环境的结果最终使得nodejs可以得到与浏览器中相同的结果就可以了。
    一个小技巧就是打印了依次执行的case的数字,nodejs与浏览器比较以免误入歧途  


    05.png (83.13 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传

    部分浏览器环境监测的代码处理   

  • 如检测创建页面元素的  
    function _yrxWxt() {
    var _yrxrqQ = 3
    // var _yrxrqQ = 3
    //     , _yrx$Kn = _yrxQXc[_yrxQ9C[9]]('div')
    //     , _yrxmEu = _yrx$Kn[_yrxQ9C[51]]('i');
    // while (_yrx$Kn[_yrxQ9C[38]] = _yrxQ9C[478] + ++_yrxrqQ + _yrxQ9C[118],
    //     _yrxmEu[0])
    //     ;
    // if (_yrxrqQ > 4)
    //     return _yrxrqQ;
    // if (_yrxWeF[_yrxQ9C[87]]) {
    //     return 10;
    // }
    // 在控制流中有setcookie
    // if (_yrxBXT(135, _yrxWeF, _yrxQ9C[315]) || _yrxQ9C[87] in _yrxWeF) {
    //     return 11;
    // }
    }

  • 检测userAgent
    case 156:
    // _yrxTY4 = !(_yrxCJw & 64) || _yrxWeF[_yrxhy4(_yrxQ9C[7])].userAgent[_yrxQ9C[73]](_yrxQ9C[531]) !== -1 || _yrxWeF[_yrxhy4(_yrxQ9C[7])].userAgent[_yrxQ9C[73]](_yrxQ9C[65]) !== -1;
    _yrxTY4 = false;
    break;

  • 检测HeadlessChrome
    case 454:
    // _yrxTY4 = /HeadlessChrome/[_yrxQ9C[125]](_yrxrqQ[_yrxQ9C[48]]) || _yrxrqQ[_yrxQ9C[275]] === '';
    _yrxTY4 = false;
    break;
    还有很多就不列举了。

    最终代码
    https://github.com/skygongque/match-yuanrenxue/tree/master/match10
    [ol]
  • 先使用npm instal express安装依赖,node server.js启动服务
  • python main.py玩耍
    [/ol]
    其他重要的点
  • eval中变量的发现,可以通过比较两份eval字符串发现


    06.png (92.4 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:08 上传


    成功留念


    1b4dab1c-f0ec-4d20-b7d2-28f27cd694cb.png (299.09 KB, 下载次数: 0)
    下载附件
    2021-8-18 23:14 上传

    代码, 下载次数

  • 天空宫阙
    OP
      


    shiliudu 发表于 2021-8-20 10:24
    谢谢分享,唉,一直不会这样的,看了就头疼.看了大姥的分析自己再琢磨琢磨

    控制流平坦化代码确实看着头晕,习惯了也可以调试,更牛逼的大佬还可以用AST还原成可读性更高的代码
    wgf4242   

    支持一下。。.
    之前看过它的题目。 。。对我来说挺有难度的。。一直没花时间去研究。。。
    cheng911001   

    谢谢楼主大大分享,感谢
    urbadman   

    其实可以调用 类似 jquery 的dom组件来获取url
    C2021   

    谢谢楼主大大分享,感谢
    snakenba580   

    这个好难啊,还是要支持一下,正在学习中
    18702770531   

    感觉很屌,就是看不懂
    wangyitu   

    我也想学爬虫,但是不知道从何下手~
    Strive8   

    感谢分享,多学习
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部