AST反混淆实战:猿人学爬虫比赛第二题详细题解

缘起

应星友要求,写下此文,哎,有钱能使鬼推磨。

实战地址:

http://match.yuanrenxue.com/match/2

抓包分析

由于谷歌浏览器某些请求不会显示,建议使用火狐浏览器来抓包分析。

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第1张图片

这是一个典型的cookie反爬,最后一个红框处是数据接口。如果不看题目,有经验的人一眼就能看出来。

第一次请求,返回的是一段js代码:

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第2张图片

第二次请求的是同一地址,这时候带上了cookie:

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第3张图片

根据经验,第二次请求的cookie,肯定是第一次请求计算的,因为第一次请求结果中并没有直接设置cookie。

注意记录此时的 cookie 值:

m : "9c8b2ba4362ff5a7023d8db7041dcd04|1603517539000"

|后面明显是个时间戳

AST化简代码,静态分析

将第一次请求的js代码保存为encode.js,去掉 script标签。

保存后,使用一键解ob混淆工具还原代码,命令:

node decode_obfuscator.js encode.js

打开保存后的结果 decode_result.js,代码很短,一下就看到了 cookie设置的地方:

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第4张图片

是在 _0x721154 这个函数里设置的,后面直接就调用了,实参是 函数 _0x5015e3 ,它的返回值是一个 时间戳,也就是 |后面的值

把 _0x721154 这个函数改造一下,去掉DOM相关的操作,让它直接返回结果:

function _0x721154(_0x1243f6, _0x167ea5) {
    return "m" + _0x3445fe() + "=" + _0x531a93(_0x1243f6) + "|" + _0x1243f6 + "; path=/";
  }

现在实参明确了,返回结果也明确了,先来看 _0x3445fe 这个函数

去除垃圾代码

_0x3445fe 这个函数 定义如下:

function _0x3445fe(_0x22e65, _0x533ac1) {
    var _0x537cb8 = 0;


    var _0x385b2c = _0x2d77f8(this, function () {
      var _0x245ea0 = function () {
        var _0x56862a = _0x245ea0["constructor"]("return /\" + this + \"/")()["compile"]("^([^ ]+( +[^ ]+)+)+[^ ]}");


        return !_0x56862a["test"](_0x385b2c);
      };


      return _0x245ea0();
    });


    _0x385b2c();


    _0x449be1();


    qz = [10, 99, 111, 110, 115, 111, 108, 101, 32, 61, 32, 110, 101, 119, 32, 79, 98, 106, 101, 99, 116, 40, 41, 10, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 115, 41, 32, 123, 10, 32, 32, 32, 32, 119, 104, 105, 108, 101, 32, 40, 49, 41, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 102, 111, 114, 40, 105, 61, 48, 59, 105, 60, 49, 49, 48, 48, 48, 48, 48, 59, 105, 43, 43, 41, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 104, 105, 115, 116, 111, 114, 121, 46, 112, 117, 115, 104, 83, 116, 97, 116, 101, 40, 48, 44, 48, 44, 105, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 125, 10, 10, 125, 10, 99, 111, 110, 115, 111, 108, 101, 46, 116, 111, 83, 116, 114, 105, 110, 103, 32, 61, 32, 39, 91, 111, 98, 106, 101, 99, 116, 32, 79, 98, 106, 101, 99, 116, 93, 39, 10, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 46, 116, 111, 83, 116, 114, 105, 110, 103, 32, 61, 32, 39, 402, 32, 116, 111, 83, 116, 114, 105, 110, 103, 40, 41, 32, 123, 32, 91, 110, 97, 116, 105, 118, 101, 32, 99, 111, 100, 101, 93, 32, 125, 39, 10];
    eval(_0x19be63(qz));


    try {
      if (global) {
        console["log"]("\u4EBA\u751F\u82E6\u77ED\uFF0C\u4F55\u5FC5python\uFF1F");
      } else {
        while (1) {
          console["log"]("\u4EBA\u751F\u82E6\u77ED\uFF0C\u4F55\u5FC5python\uFF1F");
          debugger;
        }
      }
    } catch (_0x1d08b1) {
      return navigator["vendorSub"];
    }
  }

来看 _0x385b2c 这个函数:

var _0x385b2c = _0x2d77f8(this, function () {
      var _0x245ea0 = function () {
        var _0x56862a = _0x245ea0["constructor"]("return /\" + this + \"/")()["compile"]("^([^ ]+( +[^ ]+)+)+[^ ]}");


        return !_0x56862a["test"](_0x385b2c);
      };


      return _0x245ea0();
    });


    _0x385b2c();

这个函数在声明之后马上就执行了,没有实参,虽然有返回值,但是并没有赋值给其他变量。 再看函数体,没有对任何全局变量进行操作,因此,可以直接进行删除!

按照这个思路,后面的 _0x449be1(); 调用同样可以删除,没啥用。

再看 try语句:

    try {
      if (global) {
        console["log"]("\u4EBA\u751F\u82E6\u77ED\uFF0C\u4F55\u5FC5python\uFF1F");
      } else {
        while (1) {
          console["log"]("\u4EBA\u751F\u82E6\u77ED\uFF0C\u4F55\u5FC5python\uFF1F");
          debugger;
        }
      }
    } catch (_0x1d08b1) {
      return navigator["vendorSub"];
    }

浏览器环境没有 global 这个对象,那就会执行 catch语句,返回了:

navigator["vendorSub"]

这个值,直接在控制台运行得出它是一个空值。因此,直接替换成这样就好:

return "";

这样就清爽多了:

function _0x3445fe(_0x22e65, _0x533ac1) {
    var _0x537cb8 = 0;
    qz = [10, 99, 111, 110, 115, 111, 108, 101, 32, 61, 32, 110, 101, 119, 32, 79, 98, 106, 101, 99, 116, 40, 41, 10, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 115, 41, 32, 123, 10, 32, 32, 32, 32, 119, 104, 105, 108, 101, 32, 40, 49, 41, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 102, 111, 114, 40, 105, 61, 48, 59, 105, 60, 49, 49, 48, 48, 48, 48, 48, 59, 105, 43, 43, 41, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 104, 105, 115, 116, 111, 114, 121, 46, 112, 117, 115, 104, 83, 116, 97, 116, 101, 40, 48, 44, 48, 44, 105, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 125, 10, 10, 125, 10, 99, 111, 110, 115, 111, 108, 101, 46, 116, 111, 83, 116, 114, 105, 110, 103, 32, 61, 32, 39, 91, 111, 98, 106, 101, 99, 116, 32, 79, 98, 106, 101, 99, 116, 93, 39, 10, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 46, 116, 111, 83, 116, 114, 105, 110, 103, 32, 61, 32, 39, 402, 32, 116, 111, 83, 116, 114, 105, 110, 103, 40, 41, 32, 123, 32, 91, 110, 97, 116, 105, 118, 101, 32, 99, 111, 100, 101, 93, 32, 125, 39, 10];
    eval(_0x19be63(qz));
    
    return "";
  }

这个函数后面是一个 定时器语句,因为后面有调用 _0x3445fe 这个函数,因此这行调用也可以删除:

setInterval(_0x3445fe(), 500);

定你妹呀!

运行得到结果

好了,经过上面的操作,似乎可以运行了。注意加打印并传入实参,方便比对结果:

console.log(_0x721154("1603517539000"));

将上面的代码添加到自执行函数的最好一行,运行报错:

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第5张图片

报错位置在 _0x3445fe 函数的 下面代码处:

eval(_0x19be63(qz));

有个history,估计也是dom相关的,先屏蔽掉,再运行:

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第6张图片

不报错了,比对之前的cookie值,一模一样。

这就出结果了,简直不要太简单。完!

AST反混淆实战:猿人学爬虫比赛第二题详细题解_第7张图片

你可能感兴趣的:(python,js,javascript,java,nginx)