天空宫阙 发表于 2021-8-18 23:15

猿人学爬虫攻防赛 第10题

本帖最后由 天空宫阙 于 2021-8-18 23:14 编辑

# 猿人学爬虫攻防赛 第10题
题目地址
```
https://match.yuanrenxue.com/match/10
```
总的评价:4代R数的低配静态版的壳 + jsjiami.com.v6 + obfuscator
最近学习了AST拿这个题试一下水,从编写`提高代码可读性的插件`、`搭建本地调试环境`、`扣代码`花了整整3天,最后成功的一刻,喜悦满溢。
## 调试工具和本地调式环境的搭建

**确保安装以下软件或环境**
- fiddler 使用AutoResponser 替换响应 用于搭建调试环境
- python 和 nodejs 环境和相关依赖,如python的flask nodejs的express等
- chrome 开发者工具

1. 启动调试代码中`app.py`
2. 在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`
   

经过这样配置重新打开[猿人学第十题的首页](https://match.yuanrenxue.com/match/10)仍可以加载数据,按F12打开chrome开发者工具不会进入无限debugger说明调试环境已经搭建成功。

简单讲一下几处使用AST提高代码可读性的地方
1. 把$_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 < $_ts["dfe1683"]["length"]; yuanrenxue_229++) {
yuanrenxue_36 += String["fromCharCode"]($_ts["dfe1683"]["charCodeAt"]() - yuanrenxue_229 % yuanrenxue_59 - 50);
    }
yuanrenxue_18 = atob(yuanrenxue_36);
yuanrenxue_31(78, yuanrenxue_18);
```
2. 把首页请求数据的代码还原,用obfuscator混淆的,其他还有几段,ob混淆的都不重要但无限debugger很烦,还原之后就清爽多了。请求在这里但请求的参数m并不是在这里生成的,具体后面再讲。

```
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["k"]["split"]("|")] = parseInt(_0x1f076c["k"]["k"]["split"]("|"));
      _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();
```

3. eval中控制流平坦化代码的ifelse转switch,减少单步调试时跳的次数
具体见github
```
https://github.com/skygongque/match-yuanrenxue/tree/master/match10
```
由于控制流平坦化代码中有对自增变量进行加减操作,以及最大的控制流中属于是**多个函数合并的大型控制流代码**,存在多个入口和出口,我还还原不了**请大佬指点**

## 入口寻找
### eval入口
它套了一个4代R数的壳,易得入口是下图,首页搜`call`即可



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




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




```
function _yrxyA$(_yrx7jl, _yrxcze) {
    try {
      if (typeof _yrx7jl !== _yrxQ9C) {
      _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与浏览器比较以**免误入歧途**



部分浏览器环境监测的代码处理   
- 如检测创建页面元素的
```
function _yrxWxt() {
    var _yrxrqQ = 3
    // var _yrxrqQ = 3
    //   , _yrx$Kn = _yrxQXc]('div')
    //   , _yrxmEu = _yrx$Kn]('i');

    // while (_yrx$Kn] = _yrxQ9C + ++_yrxrqQ + _yrxQ9C,
    //   _yrxmEu)
    //   ;

    // if (_yrxrqQ > 4)
    //   return _yrxrqQ;

    // if (_yrxWeF]) {
    //   return 10;
    // }
    // 在控制流中有setcookie
    // if (_yrxBXT(135, _yrxWeF, _yrxQ9C) || _yrxQ9C in _yrxWeF) {
    //   return 11;
    // }
}
```

- 检测userAgent
```
case 156:
    // _yrxTY4 = !(_yrxCJw & 64) || _yrxWeF)].userAgent](_yrxQ9C) !== -1 || _yrxWeF)].userAgent](_yrxQ9C) !== -1;
    _yrxTY4 = false;
    break;
```

- 检测HeadlessChrome
```
case 454:
    // _yrxTY4 = /HeadlessChrome/](_yrxrqQ]) || _yrxrqQ] === '';
    _yrxTY4 = false;
    break;
```
还有很多就不列举了。

## 最终代码
```
https://github.com/skygongque/match-yuanrenxue/tree/master/match10
```
1. 先使用`npm instal express`安装依赖,`node server.js`启动服务
2. `python main.py`玩耍

## 其他重要的点
- eval中变量的发现,可以通过比较两份eval字符串发现


## 成功留念


天空宫阙 发表于 2021-8-20 16:38

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

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

wgf4242 发表于 2021-8-19 00:06

支持一下。。.

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

cheng911001 发表于 2021-8-19 00:48

谢谢楼主大大分享,感谢

urbadman 发表于 2021-8-19 08:07

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

C2021 发表于 2021-8-19 08:38

谢谢楼主大大分享,感谢

snakenba580 发表于 2021-8-19 08:44

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

HarckerG 发表于 2021-8-19 08:45

感觉很屌,就是看不懂

wangyitu 发表于 2021-8-19 08:46

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

Strive8 发表于 2021-8-19 09:11

感谢分享,多学习

z744153532 发表于 2021-8-20 08:36


感谢分享
页: [1] 2 3
查看完整版本: 猿人学爬虫攻防赛 第10题