本文分析的是某国外的cdn,如果国内有使用该cdn的站点,请与本人联系立即删除,谢谢。

遵纪守法是每个公民应尽的义务和责任。

要分析的网站如下:

"aHR0cHM6Ly9ib29raW5nLnZvbG90ZWEuY29tLw=="

需要着重分析的是类似下面的url:

"aHR0cHM6Ly9ib29raW5nLnZvbG90ZWEuY29tL19JbmNhcHN1bGFfUmVzb3VyY2U/U1dKSVlMV0E9NzE5ZDM0ZDMxYzhlM2E2ZTZmZmZkNDI1ZjdlMDMyZjMmbnM9MSZjYj0xNjc2OTgyNzkx"

它返回的是一段JavaScript代码:

puppeteer显示浏览器 puffin浏览器怎么用不了_javascript

由于代码太长,我就放个截图吧,它是一个自执行的函数,首先定义了很长的一串字符串,然后再进行 String.fromCharCode操作,最后eval运行,会生成一个  ___utmvc 字段的cookie,你可以在这个页面看到它:

https://booking.volotea.com/_Incapsula_Resource?SWKMTFSR=1&e=0.2468114615986583

在这里将 此时的cookie值贴出来吧,方便后续比对:

/oA8Rt/lbDWHcnO+WZ9if+CgW5rbtdD3irqpowAoj5MJToS4myQS2GcV9HW6xuOedImC3ad9KdD+i/yBIlqIYg70N55pPqfjMGZxG14QELhFn6o/KiGkX6A2452xoqbG5wvQxvkygVIoNV/mGJNTPgTQquXxlJ7cyjRmTHxteDWrFTf6A+y5sOeARNLKCMSlhDTYIpYcvI8wajq8Z4h4Xbtkoz+JUehzO+oO+z7UmIihfVig/d/Pz0PHOLDyRHVBwWG/GCkBf7oYGVPJF2xCncm1KA88e5Et6YAhusA2NJq0gA4Yrg+Vqky3roJp2lbPZVyaOSQ34QFf32mxTkgIwWC4esVHANil2vwuIckM/L6HA+xqZQTyB9Br8fGdD/21oY7g/U6iAaxU+sJGnvIPRqz7nDTPOdgyDF+HA1FqJ8O0fDtsi9JEHb+ZJ6waVGYGg9U8Mq2dyj3iPgqevNOK8X/mHojutQlRIkWlzsvANvq7KI03YaBsKEKPdzM5o5//Y7VI0t7lOSHpCep5cIMyM7LbT/8CHumKzPfDpXiLbeV/Q7YvhXOuDX61VZ25MVxnC2YGVj4AIiTLL1qRwsmMqIZHcLu0X3/svtrWNuFvjlP7ia2NffWHZegGDfz++oodPVOIqP9HVnKvYtXSt5MtDtAMb/cSr5TLmIamo5LdXqokFZBVwur6kg27YXAI9Xf5nD6CdScdczAVVwJs1zfc7xZfOv82gxU1fn5Aby8eqr3TEc+JNLD+n7vPHdJSB30P12aEYciw/ngKV4J+yJqKRHFxmpO2GN3GotHq986P260rCmVxv0AuIVptdr9fXEa0mTXkGGsP/smC5zle3AqP/lHHvEsGrjqGsaInrC3S3rWEHgFsZOKyo6g3LTWGeIGwV/1WGsSzV3GvXlpZQNSqxmniqKNuJIXUQGm/HVFV6zg8ZJbOR0nTll5fYm2aGI/+2+52PMaK6wvO4FCTJme4/h2nd0byxGLIQz+3V7Lc7LWPVSuRwhVrtBGvP5X5lwqolwZADItxGjPkNfARgO25T5oeh2BxRvHpWF2Ufr4dJdiTy95KfKnx9fOfc3FATOK/ROBpyjDXFgcr+qAOl3/XNlqV5oXv1trw156LKN5dfps7tArFug7jPT9/lrRnLxwXAzKVWdV6RCmV3l2DjYcsw4qMlUlh6Tbcwvun7nBKS1xyVyFTaUeb7Yw0ZiOEV9BwlkJFRMNf8CiPxUcbhojPFk+YoDnhzxGKj9TaQvNZeQpQHBTJSNrWiaXCFyb9Cr3Gheg4JE111jRZyOmGEQuJLcoEULMxRWA84iE3AIu9h9n1UlCF/K64LyMuQemHwwksSMffux/LPrCs+2V3XwKq8uNjejkvZ2oKR0E8nwSh/4eVZ2oqYbv54V8wl2Rn19gTRkf5s/ADqkIyyQd6XMigAlBO9CYA7wA+TpZmXDd+kN/PyPDscDmeW0Ade1J++e3jjw+6XJZCBpaG88rJevJx64F+VmclgZ4ahRzEEw10vhUjKVf1Lf9gxM8QEtuyS6tgPjYTQmPjLt2kR3MfDtc0meMDuWQS4HJcWLAmlM8A/iEo/0+kISFCOWba6XgCS43N+HUuqCa9yFtUfXTIqtumYAvvtCxkaWdlc3Q9MTEyNzk0LHM9ODQ4OTdhYWU5ZjgyYTg4MjdhOGE4OTc5ODE5ZTgyNjc4ZDlhNzU3M2E1NzBhZTZiNjVhMzc2NzlhNDg4NjBhMzdiYWRhZDZiN2Y3MjZmNzQ=

如图:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_02

将这段源代码复制到浏览器控制台,把eval 换成 console.log,回车,再把结果美化下,如图:

puppeteer显示浏览器 puffin浏览器怎么用不了_js_03

很多很多这样的十六进制字符串,用之前介绍的方法,将其转换为 人能识别的字符串,还原后成这样:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_04

又变成了我们非常熟悉的ob混淆了,这种代码之前有介绍过怎么处理,通过操作AST将加密的函数调用解密成字符串:

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_05

这比之前的代码又要清爽很多,我们注意到有几个这样变量:

var _0x1df825 = {.....}

这种也是可以进行处理的,论坛上有相关的处理方法,请进行参考。处理后的部分代码:

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_06

同一位置 的截图如上,而这种 while--- switch语句也是可以通过操作AST来进行(请参考论坛上的文章)处理的,处理后的部分代码截图:

puppeteer显示浏览器 puffin浏览器怎么用不了_js_07

大致还原成这样就差不多了。我们在代码中直接搜索 ___utmvc 试试:

function _0x33d434(_0x62c6cd) {
    var _0x2f2a79;
    var _0x2cecf5 = _0x288ca0();
    var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);
    for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
      _0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
    }
    _0x50089f();
    var _0x518d86 = "\x94\xF6|V\xF2\x87\xDA~";
    var _0x5c5427 = _0x518d86["substr"](0x0, 0x5);
    var _0x4116fd = _0x518d86["substr"](0x5);
    var _0x39af56 = "\xEF\xF7q\x05\xE2\x12\xBE@";
    var _0xeda3f4 = 0x3;
    while (--_0xeda3f4) {
      _0x39af56 = _0x39af56["substr"](0x1) + _0x39af56[0x0];
    }
    var _0xfd1c9f = _0x39af56;
    var _0x3cc1e3 = _0x39af56["length"] - 0x1;
    while (--_0x3cc1e3) {
      _0xfd1c9f = _0xfd1c9f["substr"](0x1) + _0xfd1c9f[0x0];
    }
    var _0xf84116 = _0x27b9("0x65", _0x5c5427 + _0x4116fd);
    var _0xca7a76 = _0x3dacec["join"]();
    var _0x5bc98e = "";
    for (var _0x59635d = 0x0; _0x59635d < _0xf84116["length"]; _0x59635d++) {
      _0x5bc98e += (_0xf84116["charCodeAt"](_0x59635d) + _0xca7a76["charCodeAt"](_0x59635d % _0xca7a76["length"]))["toString"](0x10);
    }
    _0x50089f();
    _0x7b92["push"](btoa(_0x62c6cd));
    _0x2f2a79 = btoa(_0x27b9(_0x7b92["length"] - 0x1, _0xf84116["substr"](0x0, 0x5)) + ",digest=" + _0xca7a76 + ",s=" + _0x5bc98e);
    _0x7b92["pop"]();
    _0x28e982("___utmvc", _0x2f2a79, 0x14);
  }

原来调用了 _0x28e982 这个函数:

function _0x28e982(_0x491bc3, _0x4bb0d1, _0x5dfe4c) {
    var _0x35bb3c = "";
    if (_0x5dfe4c) {
      var _0x4a870b = new _0x2ca1a3["Date"]();
      _0x4a870b["setTime"](_0x4a870b["getTime"]() + _0x5dfe4c * 0x3e8);
      var _0x35bb3c = "; expires=" + _0x4a870b["toGMTString"]();
    }
    _0x240c09["cookie"] = _0x491bc3 + "=" + _0x4bb0d1 + _0x35bb3c + "; path=/";
  }

逻辑很清晰,就是一个cookie赋值的功能,并且这个值是通过 参数传递进来的,也就是上面代码中的 _0x2f2a79,它是在这里赋值的:

_0x2f2a79 = btoa(_0x27b9(_0x7b92["length"] - 0x1, _0xf84116["substr"](0x0, 0x5)) + ",digest=" + _0xca7a76 + ",s=" + _0x5bc98e)

原来值是经过 base64编码的,我们将之前保存的cookie值解码看看:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_08

以及:

puppeteer显示浏览器 puffin浏览器怎么用不了_js_09

解码后看到了 digest=112794, 以及

s=84897aae9f82a8827a8a8979819e82678d9a7573a570ae6b65a37679a48860a37badad6b7f726f74;

 再一次佐证了生成的位置。前面是一段看不懂的字符串,我们从这个 digest 作为爆破点,它的值是  _0xca7a76,最后的生成位置在这里:

var _0xca7a76 = _0x3dacec["join"]();

继续往上追 _0x3dacec:

function _0x33d434(_0x62c6cd) {
    var _0x2f2a79;


    var _0x2cecf5 = _0x288ca0();


    var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);


    for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
      _0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
    }

首先看  _0x288ca0 这个函数:

function _0x288ca0() {
    var _0x1b9363 = new _0x2ca1a3["Array"]();
    var _0x5f24c8 = new _0x2ca1a3["RegExp"]("^\\s?incap_ses_");
    var _0x30d99e = _0x240c09["cookie"]["split"](";");
    for (var _0x59e2f0 = 0x0; _0x59e2f0 < _0x30d99e["length"]; _0x59e2f0++) {
      var _0x5c56a9 = _0x30d99e[_0x59e2f0]["substr"](0x0, _0x30d99e[_0x59e2f0]["indexOf"]("="));
      var _0x727f65 = _0x30d99e[_0x59e2f0]["substr"](_0x30d99e[_0x59e2f0]["indexOf"]("=") + 0x1, _0x30d99e[_0x59e2f0]["length"]);
      if (_0x5f24c8["test"](_0x5c56a9)) {
        _0x1b9363[_0x1b9363["length"]] = _0x727f65;
      }
    }
    _0x50089f();
    return _0x1b9363;
  }

是一段对 cookie操作的代码,这段代码拿到浏览器肯定是无法运行的,而且cookie的值早已改变,因此需要将这个响应是的cookie代码复制下来进行同样的操作:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_10

我这里用的是火狐浏览器,因为我在谷歌浏览器上安装了插件,后面的代码有检测这个插件信息,会有干扰,处理起来不是很方便,因此我直接上一个没有插件的浏览器:

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_11

又因为直接在浏览器上进行cookie赋值,然后进行操作,会有错误:

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_12

这里只 split 出了一个字符串,肯定是不对的,我们直接搞一个普通的字符串split试试:

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_13

将这段代码替换进去即可:

function _0x288ca0() {
    var _0x1b9363 = new _0x2ca1a3["Array"]();
    var _0x5f24c8 = new _0x2ca1a3["RegExp"]("^\\s?incap_ses_");
    var _0x30d99e = [ "dtSa=-", " dtCookie=1$E7FB92CBB7FBB1FC5FF8E30DD2DCE492|ea7c4b59f27d43eb|1", " ASP.NET_SessionId=4q44krz2ez3elmj0yzj33a3y", " geoInfo=%7b%22geoIp%22%3a2742941454%2c%22ip%22%3a%22163.125.247.14%22%2c%22continent%22%3a%22AS%22%2c%22country%22%3a%22CN%22%2c%22city%22%3a%22Shenzhen%22%2c%22currencyCode%22%3a%22CNY%22%2c%22displayCurrency%22%3a%22USD%22%2c%22latitude%22%3a22.5333%2c%22longitude%22%3a114.1333%2c%22nearestStation%22%3a%22ARK%22%2c%22selectedCurrency%22%3a%22USD%22%2c%22proposedCurrency%22%3a%22USD%22%2c%22multicurrency%22%3a%22true%22%7d", " tokenInfo=YTc2M2I2NmMtYWY5ZS00NTQ5LWI5ZTEtMjAyMDA1MTAxMDQzNDAtNHE0NGtyejJlejNlbG1qMHl6ajMzYTN5", " skysales=!Kq7SB+PO/C+ceuc8ac0SZ4cwGUzSo+xRgVJyWqQ7MsPUeBrSLwP47Kc3G60M05gvdg3Mzc6esR915ac=", " ak_bmsc=ED4CEB49C8564F5CFA51B7676AB804647D38DA39853B0000DCDAB75E9FE4491C~plWLTGb0mCa7xbnSj4xNL/P8qL4FEjWlb3wclYRkPpR6hTbMOBK+LLao1UcYIXYlNmX69xEtt8PfNm+Dz5i8+Yi6JXhzq1TME/Z3HFD4VhJQfJl7Qe7mRr6Sg3bVkFrGX3v07Ha7ZEkRSvVQ7RhvfL/kB8LcsZ26cri5aFVytSQFZqrhlaMA3zea2Awp1wToZM/2ROY/7wcfE+NcF5hOb1EWcZ8Fikj3XWfNH2yJLYmDo=", " visid_incap_1895301=Tt1zbPCESTK/r3VzH4f95trat14AAAAAQUIPAAAAAABVDXqy6Jps1PJU7XM/v/8h", " nlbi_1895301=BNMkF3yZniMb8R43DWaj5AAAAAD/pJINRErKpKVBTRoo1P4k", " incap_ses_895_1895301=2/MZXcUNA3/iTzGHy65rDNzat14AAAAAi5FxnRPDlgGDDChRiyp6WA==" ];
    for (var _0x59e2f0 = 0x0; _0x59e2f0 < _0x30d99e["length"]; _0x59e2f0++) {
      var _0x5c56a9 = _0x30d99e[_0x59e2f0]["substr"](0x0, _0x30d99e[_0x59e2f0]["indexOf"]("="));


      var _0x727f65 = _0x30d99e[_0x59e2f0]["substr"](_0x30d99e[_0x59e2f0]["indexOf"]("=") + 0x1, _0x30d99e[_0x59e2f0]["length"]);


      if (_0x5f24c8["test"](_0x5c56a9)) {
        _0x1b9363[_0x1b9363["length"]] = _0x727f65;
      }
    }
    _0x50089f();
    return _0x1b9363;
  }

最后,新开一个标签页,将代码复制进去,然后缺啥补啥,让这个函数得以运行起来,注意,_0x50089f 这个函数没有什么用,可以直接删除,我得到的结果是这样的:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_14

这样,这三行代码得以解决:

var _0x2f2a79;
   var _0x2cecf5 = _0x288ca0();
   var _0x3dacec = new _0x2ca1a3["Array"](_0x2cecf5["length"]);

直接在浏览器上运行即可,再看下面的这个for循环:

for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
      _0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
    }

运行报错,

_0x1efa58 is not defined

这是一个函数,直接复制到控制台运行:

function _0x1efa58(_0x94e2f4) {
    var _0x22b242 = 0x0;
    for (var _0x1856e7 = 0x0; _0x1856e7 < _0x94e2f4["length"]; _0x1856e7++) {
      _0x22b242 += _0x94e2f4["charCodeAt"](_0x1856e7);
    }
    //_0x50089f();
    return _0x22b242;
  }

再次运行后,继续报错,

_0x62c6cd is not defined

看代码  function _0x33d434(_0x62c6cd) { 这个原来是参数,我们需要找到它的实参,即看 _0x33d434 这个函数是在哪里调用的:

puppeteer显示浏览器 puffin浏览器怎么用不了_js_15

又追到了这里,实参是 _0x3b88bc(_0x466892),这时我们得计算出它的结果,先看 _0x466892 ,有定义和更新:

var _0x466892 = [["navigator", "exists"], ["navigator.vendor", "value"], ["navigator.appName", "value"], ["navigator.plugins.length==0", "value"], ["navigator.platform", "value"], ["navigator.webdriver", "value"], ["platform", "plugin_extentions"], ["ActiveXObject", "exists"], ["webkitURL", "exists"], ["_phantom", "exists"], ["callPhantom", "exists"], ["chrome", "exists"], ["yandex", "exists"], ["opera", "exists"], ["opr", "exists"], ["safari", "exists"], ["awesomium", "exists"], ["puffinDevice", "exists"], ["__nightmare", "exists"], ["domAutomation", "exists"], ["domAutomationController", "exists"], ["_Selenium_IDE_Recorder", "exists"], ["document.__webdriver_script_fn", "exists"], ["document.$cdc_asdjflasutopfhvcZLmcfl_", "exists"], ["process.version", "exists"], ["global.require", "exists"], ["global.process", "exists"], ["WebAssembly", "exists"], ["window.toString()", "value"], ["navigator.cpuClass", "exists"], ["navigator.oscpu", "exists"], ["navigator.connection", "exists"], ["navigator.language=='C'", "value"], ["window.outerWidth==0", "value"], ["window.outerHeight==0", "value"], ["window.WebGLRenderingContext", "exists"], ["window.constructor.toString()", "value"], ["document.documentMode", "value"], ["eval.toString().length", "value"]];
_0x466892["push"](["'v4aead2a1deb1b76130ffbb35218fe2b608e5e059756393a0b06378a23b50336a'.toString()", "value"]);

看这个值,都是与浏览器相关的特征,也拷贝到控制台运行,

再将 _0x3b88bc 这个函数 同样拷贝到浏览器运行,然后再赋值给 _0x62c6cd

puppeteer显示浏览器 puffin浏览器怎么用不了_puppeteer显示浏览器_16

得到了这么一串与浏览器特征相关的数据,值出来了,再运行这个for循环吧:

for (var _0x59635d = 0x0; _0x59635d < _0x2cecf5["length"]; _0x59635d++) {
      _0x3dacec[_0x59635d] = _0x1efa58(_0x62c6cd + _0x2cecf5[_0x59635d]);
    }

再次运行,结果如下:

puppeteer显示浏览器 puffin浏览器怎么用不了_debug_17

结果出来了,与之前的 值是一样的,我们再来看s的值:

puppeteer显示浏览器 puffin浏览器怎么用不了_js_18

继续将整个值计算出来:

puppeteer显示浏览器 puffin浏览器怎么用不了_javascript_19

只此,在浏览器上面已正确调试出结果。那如果要在node下面运行成功,要该如何呢?

可以看到,与node唯一的差异只是浏览器特征的检测,也就是这个object:

var _0x466892 = [["navigator", "exists"], ["navigator.vendor", "value"], ["navigator.appName", "value"], ["navigator.plugins.length==0", "value"], ["navigator.platform", "value"], ["navigator.webdriver", "value"], ["platform", "plugin_extentions"], ["ActiveXObject", "exists"], ["webkitURL", "exists"], ["_phantom", "exists"], ["callPhantom", "exists"], ["chrome", "exists"], ["yandex", "exists"], ["opera", "exists"], ["opr", "exists"], ["safari", "exists"], ["awesomium", "exists"], ["puffinDevice", "exists"], ["__nightmare", "exists"], ["domAutomation", "exists"], ["domAutomationController", "exists"], ["_Selenium_IDE_Recorder", "exists"], ["document.__webdriver_script_fn", "exists"], ["document.$cdc_asdjflasutopfhvcZLmcfl_", "exists"], ["process.version", "exists"], ["global.require", "exists"], ["global.process", "exists"], ["WebAssembly", "exists"], ["window.toString()", "value"], ["navigator.cpuClass", "exists"], ["navigator.oscpu", "exists"], ["navigator.connection", "exists"], ["navigator.language=='C'", "value"], ["window.outerWidth==0", "value"], ["window.outerHeight==0", "value"], ["window.WebGLRenderingContext", "exists"], ["window.constructor.toString()", "value"], ["document.documentMode", "value"], ["eval.toString().length", "value"]];
_0x466892["push"](["'v4aead2a1deb1b76130ffbb35218fe2b608e5e059756393a0b06378a23b50336a'.toString()", "value"]);

以及这个函数检测的结果:

_0x3b88bc(_0x466892)

因此在node环境下面只需要将这些特征补上即可,以

["navigator", "exists"], ["navigator.vendor", "value"]

这两个为例,一个是判断是否存在,一个是计算出它的值,

puppeteer显示浏览器 puffin浏览器怎么用不了_javascript_20

因此,你需要这样去补:

navigator = {};
navigator.vendor = "";

补特征是个细心的过程,做的多了,也就很容易了。