某网站JS加密、OB混淆与CSS反爬实战分析

1. 写在前面

  最近一段时间接触了一些小说网站的业务。发现很多的小说网站,甚至一些小站它们的安全防护措施做的都很到位!例如上次说到的的五秒盾也是存在于一个小说小站。今天要讲的这个网站它集JS加密ob混淆CSS反爬于一体

目标站点

aHR0cHM6Ly93d3cuaG9uZ3NodS5jb20vY29udGVudC8xMTM3NzIvMjA1NDI1LTE0NTU1NzIuaHRtbA==

2. 分析

  

这次主要说的就是小说内容这块,打开这个网站的你会发现内容是不允许复制的。其次页面呈现的内容里面有一部分数据是隐藏的,虽然在页面你看它显示都正常,但在原代码中是没有的,而是由类似span标签代替的

这种反爬虫的手段主要先限制我们对页面的一些操作,然后将某些内容进行加密,最后在网页加载的时候通过解密算法进行解密从而渲染加载到网页

首先,碰到这种情况,我们可以先假设它这个页面的内容是经过二次请求,从接口拿到数据在进行渲染的

所以,我们可以先刷新页面看看XHR下的请求,简单的分析一波

在这里插入图片描述

可以看到某个请求的响应中有一个content字段,一看就知道它是经过加密处理的。这个字段的字面意思倒是可以让我们怀疑它是网页需要呈现的内容数据

在JS逆向中,可以记住大致的主流程,当然主流程里面涉及到的各种技巧与手法又可以开枝散叶,本文不做过多的描述。以后慢慢覆盖讲解:
在这里插入图片描述

3. 定位

  这里我们其实已经知道content字段很有可能是经过加密后的小说内容。所以到这一步我们还是有比较多的方法进行定位,可以直接点击Initiator跟一下请求的调用栈

  同样根据你的经验进行一些预判!如全局搜索一下可疑的关键字。这里我们可以看上面的加密肯定与base64有关,加密的数据也必然会走解密decrypt逻辑,这些可疑的关键词对象都可以是我们入手的点
在这里插入图片描述
上述通过分析定位找到可疑的JS代码,可以看到这里大概率是一个解密函数的调用,我们埋一个断点重新刷新请求一下:

在这里插入图片描述
断点到此处后,我们可以看到content字段,就是一串密文,而这段密文依次调用了utf8to16hs_decryptbase64decode这三个函数。这里我们可以大胆的猜想它们就是解密函数

3. 扣代码

现在我们可以去印证我们最开始的猜想,content字段到底是不是解密后的小说内容,我们将上面几个函数的js代码扣出来

function base64decode(str) {
    var c1, c2, c3, c4, base64DecodeChars = new Array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);
    var i, len, out;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        do {
            c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        } while (i < len && c1 == -1);if (c1 == -1)
            break;
        do {
            c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        } while (i < len && c2 == -1);if (c2 == -1)
            break;
        out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
        do {
            c3 = str.charCodeAt(i++) & 0xff;
            if (c3 == 61)
                return out;
            c3 = base64DecodeChars[c3];
        } while (i < len && c3 == -1);if (c3 == -1)
            break;
        out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
        do {
            c4 = str.charCodeAt(i++) & 0xff;
            if (c4 == 61)
                return out;
            c4 = base64DecodeChars[c4];
        } while (i < len && c4 == -1);if (c4 == -1)
            break;
        out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
    }
    return out;
}

function str2long(s, w) {
    var len = s.length;
    var v = [];
    for (var i = 0; i < len; i += 4) {
        v[i >> 2] = s.charCodeAt(i) | s.charCodeAt(i + 1) << 8 | s.charCodeAt(i + 2) << 16 | s.charCodeAt(i + 3) << 24;
    }
    if (w) {
        v[v.length] = len;
    }
    return v;
}

function hs_decrypt(str, key) {
    if (str == "") {
        return "";
    }
    var v = str2long(str, false);
    var k = str2long(key, false);
    var n = v.length - 1;
    var z = v[n - 1]
      , y = v[0]
      , delta = 0x9E3779B9;
    var mx, e, q = Math.floor(6 + 52 / (n + 1)), sum = q * delta & 0xffffffff;
    while (sum != 0) {
        e = sum >>> 2 & 3;
        for (var p = n; p > 0; p--) {
            z = v[p - 1];
            mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            y = v[p] = v[p] - mx & 0xffffffff;
        }
        z = v[n];
        mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
        y = v[0] = v[0] - mx & 0xffffffff;
        sum = sum - delta & 0xffffffff;
    }
    return long2str(v, true);
}


function long2str(v, w) {
    var vl = v.length;
    var sl = v[vl - 1] & 0xffffffff;
    for (var i = 0; i < vl; i++) {
        v[i] = String.fromCharCode(v[i] & 0xff, v[i] >>> 8 & 0xff, v[i] >>> 16 & 0xff, v[i] >>> 24 & 0xff);
    }
    if (w) {
        return v.join('').substring(0, sl);
    } else {
        return v.join('');
    }
}


function utf8to16(str) {
    var out, i, len, c;
    var char2, char3;
    out = "";
    len = str.length;
    i = 0;
    while (i < len) {
        c = str.charCodeAt(i++);
        switch (c >> 4) {
        case 0:
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
            out += str.charAt(i - 1);
            break;
        case 12:
        case 13:
            char2 = str.charCodeAt(i++);
            out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
            break;
        case 14:
            char2 = str.charCodeAt(i++);
            char3 = str.charCodeAt(i++);
            out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
            break;
        }
    }
    return out;
}

function decrypt(content,key){
    return utf8to16(hs_decrypt(base64decode(content), key))
}

decrypt函数是自己定义的,为了更好的调用上面的解密函数。同时可以看到,解密函数调用的时候不光是传了content参数,还有一个key,那么我们现在需要分析key这个值是什么

回到接口请求分析那一步,之前第一次分析的时候,就把所有的请求都看了一遍,自然有发现这个key也是在XHR请求响应中的
在这里插入图片描述
现在,我们只需要从这两个接口当中请求拿到contentkey值就能调用我们的JS解密算法进行解密

这里我们可以在Console控制台把js代码放进去再构造一下contentkey即可验证,得到的结果正如我们猜想,它就是加密后的内容,如下是解密后的数据:

在这里插入图片描述

4. CSS反爬绕过

  现在我们已经得到了页面加密的内容、解密算法。但是可以看到解密出来后的数据是不完整的,被隐藏了。现在要做的就是拿到被隐藏的那些文字内容,这样整个页面的内容才是全的

我们再回过头看之前的请求分析,这个图里面除了content字段还有一个ohter字段的值貌似也是经过加密处理的
在这里插入图片描述
不要考虑那么多,现在为止我们只要这么想,所有的数据在呈现到前端页面时,必定是有迹可循的。不管是后端加密还是前端加密,都是一样,我们直接用解密函数在控制台看看,如下:
在这里插入图片描述
很可惜这并不是所缺失的那些内容,倒像是一段JS代码,不过好像也并不是正常的JS代码,显然经过了混淆。但是好在结构还算清晰

我们不用管这里面的具体加密与解密算法是怎样的,只需要分析它的加密跟解密的逻辑最后调用即可

ob混淆里面有一个大数组

var _0x09d1 = ['uJtRs', 'RyzMW', 'wkLRG', 'prototype', 'endWith', 'GIvaF', 'test', 'enc', '0767CD2FAAF99E58', 'window', 'location', 'href', 'Latin1', 'parse', 'D063F0602455077D', '146385F634C9CB00', 'ZeroPadding', 'toString', 'Utf8', 'split', 'createElement', 'style', 'setAttribute', 'type', 'async', 'head', 'cqrvw', 'link', 'nNyBj', 'length', 'parentNode', 'insertBefore', 'appendChild', '4|1|5|3|2|0', 'fromCharCode', 'tMLmi', 'OAWFf', 'KUtvo', 'KgGRl', 'qBUPd', 'addRule', '.context_kw', '::before', 'styleSheets', 'cssRules', 'pad', 'ZFebI', 'clamp', 'qVUkH', 'sigBytes', 'words', 'xsbdD', 'ouYQw']

这个数组基本是明文的,很多大厂网站这个数组全是跟变量_0x09d1一样全是十六进制的,其实经过分析就能发现words就是被打散隐藏的缺失内容

在这里插入图片描述
到了这一步,我们第一种方案则可以根据span标签里的class的数字索引去取words数组里面的值了
在这里插入图片描述

5. 内容还原

当然还有另外一种还原内容的方案,这里我建议使用JS注入然后渲染

html = '<!DOCTYPE html><html><head><meta charset="UTF-8"><script>'+js+'</script></head><body>'+content+'</body></html>'
    r = HTML(html=html)
    script_code = '''
        var span_list = document.getElementsByTagName("span");
        for (var i=0; i<span_list.length; i++) {
            var content = window.getComputedStyle(
                span_list[i], ':before'
            ).getPropertyValue('content');
            span_list[i].innerText = content.replace('"', '').replace('"', '');
        }
    '''
    r.render(script=script_code, reload=False)
    print(r.find('body', first=True).text)

上面的font color=#ff0033 size=3>ohter就是我们解密出来的那段js混淆代码,也就是用来还原缺失内容的关键函数,它就是我们上面的js参数,然后content就是我们上面还原的第一阶段解密出来的content

解密后的网页内容
在这里插入图片描述

解密后的JS混淆代码
在这里插入图片描述

注入后拿到的页面完整内容
在这里插入图片描述

  好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/36133.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【javaEE面试题(四)线程不安全的原因】【1. 修改共享数据 2. 操作不是原子性 3. 内存可见性 4. 代码顺序性】

4. 多线程带来的的风险-线程安全 (重点) 4.1 观察线程不安全 static class Counter {public int count 0;void increase() {count;} } public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();Thread t1 new Thread(()…

王道考研计算机网络第五章知识点汇总

5.1.1 传输层概述 复用&#xff1a;好比家里面每个人都要写信&#xff0c;向信箱里面投入信件&#xff0c;然后由邮递员取走。 分用&#xff1a;就是每个人都收到了各自的回信&#xff0c;然后从信箱中取走各自的信 5.2 UDP协议 注意&#xff1a;用户数据报和检验和都是指的整…

深度剖析线上应用节点流量隔离技术

作者&#xff1a;谢文欣&#xff08;风敬&#xff09; 为什么要做流量隔离 源于一个 EDAS 客户遇到的棘手情况&#xff1a;他们线上的一个 Pod CPU 指标异常&#xff0c;为了进一步诊断问题&#xff0c;客户希望在不重建此 Pod 的情况下保留现场&#xff0c;但诊断期间流量还…

chatGPT如何开启 Browsing 功能,实现即时联网查询?

Openai 为每一个 chatGPT Plus 用户都开放了 Browsing 和 plugins 功能。 前者可以在 ChatGPT 觉得有必要的时候&#xff08;比如你问它今天的新闻&#xff09;&#xff0c;自动联网查询&#xff0c;后者是第三方开发者开发的插件&#xff0c;数量繁多&#xff0c;可以解决各种…

【Distributed】分布式ELK日志文件分析系统

文章目录 一、ELK 概述1. 为什么要使用 ELK2. 完整日志系统基本特征3. ELK 简介3.1 ElasticSearch&#xff08;ES&#xff09;3.2 Kiabana3.3 Logstash3.4 其它组件Filebeat缓存/消息队列Fluentd 4. ELK 的工作原理5. Linux 系统内核日志消息的优先级别 二、 部署 ELK 集群服务…

使用python调用ChatGPT API 简单示例

如果你已经获得了OpenAI的API密钥&#xff0c;并且想要使用Python发起ChatGPT对话&#xff0c;你可以使用OpenAI的Python SDK来实现。下面是一个简单的示例代码&#xff1a; 首先&#xff0c;你需要确保已安装OpenAI的Python SDK。你可以使用pip来安装&#xff1a; pip insta…

BaGet做了一个Nuget私有服务器,Nginx代理之后还是会请求被代理得地址

Nuget搭建和使用可以参考官网得文档 https://loic-sharma.github.io/BaGet/installation/docker/ 这是我用Nginx代理之后出现得问题&#xff0c;观察请求url和响应回来得配置。配置中得ip地址得url是我被代理得下游地址&#xff0c;所以是无法访问的。 我原本以为是要去server…

【案例教程】GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践实践技术

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

79. 单词搜索

79. 单词搜索 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 79. 单词搜索 https://leetcode.cn/problems/word-search/ 完成情况&#xff1a; 解题思路&#xff1a; 参考代码&#xff1a; package 西湖算法题…

微信小程序使用animation.css

animation.css是一款纯css动画库&#xff0c;其中提供了丰富的动画效果 我们直接下载animation.css&#xff0c;即可使用其中的样式 其官网为&#xff1a;Animate.css | A cross-browser library of CSS animations. 1.下载 使用npm下载animation.css&#xff1a; npm inst…

构建工具——webpack、vite

文章目录 构建工具Webpack使用步骤配置文件&#xff08;webpack.config.js&#xff09;插件&#xff08;plugin&#xff09; ViteVite 也是前端的构建工具使用命令构建配置文件&#xff1a;vite.config.js 构建工具 当我们习惯了在 node 中编写代码的方式后&#xff0c;在回到…

python怎么获取免费代理IP

什么是免费代理IP 免费代理IP是指可以免费使用的代理服务器的IP地址。代理服务器充当客户端和目标服务器之间的中间人&#xff0c;通过转发请求和响应来实现网络数据的传输。使用代理IP可以隐藏真实的客户端IP地址&#xff0c;实现匿名访问网络资源。 免费代理IP通常由个人或组…

实战:k8s证书续签-2023.6.19(测试成功)

实战&#xff1a;k8s证书续签-2023.6.19(测试成功) 目录 推荐文章 https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》 1、前言 k8s集群核心的证书有2套&#xff0c;还有1套非核心的(即使出问题也问题不大)。 ⚠️ 如果是kubeadm搭建的k8s集群&#xff0c;其有效期为…

Spring系列4 -- Bean的作用域和生命周期

目录 1. 案例 2. 作用域定义 2.1 Bean的6种作用域 2.2 设置作用域 3. Sring的执行流程 4. Bean的生命周期 思考: 为什么不是先进行初始化然后再进行设置属性呢? 1. 案例 假设现在有⼀个公共的 Bean&#xff0c;提供给 A ⽤户和 B ⽤户使⽤&#xff0c;然⽽在使⽤的途中…

HTML5中一些酷炫又有趣的新特性代码整理汇总

HTML5中一些酷炫又有趣的新特性代码整理汇总 文章目录 HTML5中一些酷炫又有趣的新特性代码整理汇总前言一、详情标签< details>二、内容可编辑三、标记内容< mark>四、data-* 属性五、输出标签六、数据列表< datalist>七、Meter八、Inputs 前言 HTML5 是 Hy…

Apikit 自学日记:测试数据集

测试数据集 添加数据集的变量 在测试用例详情页面中&#xff0c;您可以点击上方的 测试数据 标签&#xff0c;进入用例的数据管理页面。在这里您可以添加多组测试数据&#xff0c;以及每组测试数据的变量。 在添加数据集前&#xff0c;我们需要设置数据集中存在什么变量。可以…

IPV6使用越来越广,您会配置吗?

前面针对IPv6写过一篇文章&#xff0c;但是好多网友反映没有读懂&#xff0c;今天再给大家把内容浓缩一下&#xff0c;教给大家如何配置。 IPV6的推出主要是为了解决地址空间的不足&#xff0c;从而进一步的促进互联网的发展。IPV6地址空间大到惊人&#xff0c;有人比喻地球上…

Springboot设置并访问静态资源目录

目录​​​​​​​ 静态文件 application设置方法 配置详解 编写配置 优缺点 设置配置类方法 配置详解 编写配置 优缺点 总结 静态文件 静态资源&#xff0c;一般是网页端的&#xff1a;HTML文件、JavaScript文件和图片。尤其是设置图片的静态资源&#xff0c;尤其重…

BERT论文解读及实现(一)

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 1 论文解读 1.1 模型概览 There are two steps in our framework: pre-training and fine-tuning. bert由预训练模型微调模型组成。 ① pre-training, the model is trained on unlabele…

基于simulink处理监控视频以选择包含运动的帧(附源码)

一、前言 此示例演示如何处理监控视频以选择包含运动的帧。安全问题要求使用摄像机对重要位置进行持续监控。为了有效地记录、查看和存档这些海量数据&#xff0c;您可以减小视频帧大小或减少录制的视频帧总数。此示例说明了后一种方法。在其中&#xff0c;相机视野中的运动会…