美文网首页大数据 爬虫Python AI SqlIT@程序员猿媛
解密红薯中文网手机站点升级版CSS反爬

解密红薯中文网手机站点升级版CSS反爬

作者: Jkanon | 来源:发表于2019-07-19 23:32 被阅读104次

    今日有网友抛出一个小说网站,说是爬虫遇到了点障碍,兴趣使然我也去凑了个热闹,结果发现这个网站的反爬也是我平生第一次所见,在此记录下详细的破解过程。

    问题

    站点链接:传送门

    • 我们发现最终呈现在浏览器上的页面内容是有些差异,比如页面上的“地皇元年”,而我们在html返回的数据中只能看到“皇元年”,说到这里我们也很容易想到这可能是一个CSS反爬的页面。
    • 打开Elements页签查看源代码,果不其然,事实上确实是通过::before伪元素来实现的CSS渲染,这也是一种常见的反爬手段。
    • 但接下来就发现了事情没有那么简单,那就是竟然无法找到这个伪元素样式定义的源头,通常情况下,我们可以从控制面板看出这些样式是哪里定义的,可能是来自外联CSS,也可能是内联style。然而此处我们却碰壁了,控制面板上只有<style>寥寥数字,点击style跳转到了一个空的style链接,同时我们发现页面返回的源码并不包含此style节点。
      无法找到样式源头
      空的style节点

    思路分析

    • 猜想查找
      在这种情况下我虽然不太清楚怎么实现的,但是第一感觉告诉我这和JS有关,我试着全局搜索如context_kw(class属性前缀), , ::(伪元素前缀),最后终于在页面内的一处JS代码出发现了伪元素相关的字符。
      猜想查找的过程
    • JS解析
      • JS格式化
        为了更好地对代码进行分析,网上找个在线的JS格式化工具进行格式化。


        格式化后的代码一览
      • 粗略解读JS代码
        粗略看过代码之后,我们看到了几个关键词:crypto, decrypted, secWords, words, createElement, text/css
        很快,我们可以猜想这段JS代码必定是与解密相关的,并且操纵了DOM树(这边猜想就是上面那个空的style节点)。
        我们发现,这段代码没有闭包,这意味这段代码中的变量都是全局变量,我们直接在console查看变量的数据,并且可以将其中一些关键代码在console中重新执查看。
        从下图中我们就可看出words就是伪元素中的content

      • 细读JS代码
        1.第一次解密
        _0xa12e数组下标为0xc(即12)的元素,用AES解密成字符串之后,用,分割成数组,最终得到secWords

    ["65291", " 30339", " 12289", " 21015", " 20153", " 19967", " 20181", " 26160", " 19982", " 22311", " 26378", " 20101", " 30527", " 8222", " 8219", " 31167", " 22824", " 19977", " 36948", " 27461", " 20009", " 21518", " 19980", " 22319"]
    

        2.第二次解密
    以下代码就是将secWords解密为最终的文字并保存到words数组中。
    总结起来就是对于secWords的每个元素进行以下操作:
    假设输入n,则输出

    伪代码
    var t = (n+3) % 2 === 0 ? (n+3)-2 : (n+3)-4       ==>    n % 2 === 0 ? n - 1 : n + 1
    String.fromCharCode(t)
    
    输出示例
    secWords[0] => 65291 => 65292(65291+1) => ','(String.fromCharCode('65292'));
    secWords[1] => 30339 => 30340(30339+1) => '的'(String.fromCharCode('30340'));
    
    源代码
    for (var i = 0x0; i < secWords[_0xea12('0x18')]; i++) {
        var _0x5420ee = '3|5|2|4|0|1' [_0xea12('0x17')]('|'),
        _0x9ff9d9 = 0x0;
        while ( !! []) {
            switch (_0x5420ee[_0x9ff9d9++]) {
            case '0':
                _0x423190 = _0x5796d9(_0x423190);
                continue;
            case '1':
                words[i] = String[_0xea12('0x25')](_0x423190);
                continue;
            case '2':
                var _0x5796d9 = function(_0x490c80) {
                    var _0x1532b6 = {
                        'ifLSL': function _0x256992(_0x118bb, _0x36aa09) {
                            return _0x118bb + _0x36aa09;
                        }
                    };
                    return _0x1532b6[_0xea12('0x26')](_0x490c80, 0x3 * +!(typeof document === _0xea12('0x27')));
                };
                continue;
            case '3':
                var _0x423190 = secWords[i];
                continue;
            case '4':
                _0x423190 = _0x3e8e1e(_0x423190);
                continue;
            case '5':
                var _0x3e8e1e = function(_0xd024e1) {
                    var _0x3e40d1 = {
                        'mPDrG': function _0x411e6f(_0xa8939, _0x278c20) {
                            return _0xa8939 % _0x278c20;
                        },
                        'DWwdv': function _0x1e0293(_0x5b15eb, _0x443876) {
                            return _0x5b15eb - _0x443876;
                        }
                    };
                    return _0x3e40d1[_0xea12('0x28')](_0xd024e1, 0x2) ? _0x3e40d1[_0xea12('0x29')](_0xd024e1, 0x2) : _0xd024e1 - 0x4;
                };
                continue;
            }
            break;
        }
    }
    

        3.设置style
    这一步就是样式设置的关键所在了,将根据上一步得到的words数组添加到styleSheets中(就是这一步导致了控制板面板上无法看到样式定义的源头

    源代码
    for (var i = 0x0; i < words[_0xea12('0x18')]; i++) {
        try {
            //document.styleSheets[0].addRule('.context_kw'+i + '::before',  'content:\x20\x22' + words[i] + '\x22')
            document[_0xea12('0x2a')][0x0][_0xea12('0x2b')]('.context_kw' + i + _0xea12('0x2c'), 'content:\x20\x22' + words[i] + '\x22');
        } catch(_0x527f83) {
            document['styleSheets'][0x0]['insertRule'](_0xea12('0x2d') + i + _0xea12('0x2e') + words[i] + '\x22}', document[_0xea12('0x2a')][0x0][_0xea12('0x2f')][_0xea12('0x18')]);
        }
    }
    

    爬虫思路

    换了几个页面之后,我发现这个words列表并不是固定不变的,因此我们得寻找到一种方法得到words数组。

    • 遍历<Script>节点,获取其中包含CryptoJS文本的节点
    • 截取var words =之前的代码,用JS引擎执行得到secWords
    • 用相应的爬虫语言实现secWordswords的转变(上述思路分析已详细讲解)
    • 之后就变成了普通的css伪元素反爬的处理(这一部分不是这篇文章的重点,不再展开)

    PC站点会更加复杂点,需要先通过ajax加载文章内容,然后解密文章文本,余下步骤与手机版一致。

    相关文章

      网友评论

        本文标题:解密红薯中文网手机站点升级版CSS反爬

        本文链接:https://www.haomeiwen.com/subject/ttfelctx.html