常见反爬策略整理——浏览器反调试


前言

常见反爬策略整理专栏全面整理了常见的反爬策略,包括headers反爬、js加密、CSS加密、浏览器反调试、数据传输格式、IP封禁等,还介绍了如HTTP2、TLS指纹、WebSocket、请求规律、蜜罐陷阱等其他反爬手段及相应的解决方案。这篇文章是专栏的系列之一,主要讲解浏览器反调试的一些特征及解决方案,看汇总版的可以移步 常见反爬策略整理


一、禁用控制台

1.1 浏览器控制打开方式

一般打开浏览器控制台有以下几种方式

  • 鼠标右键 -> 检查
  • 键盘快捷键
    • F12
    • command + option + i/j/c(Mac 系统)
    • ctrl + shift + i(Win 系统)
  • 浏览器右上角 三个点 -> 更多工具 -> 开发者工具

1.2 绕过禁用的打开控制台

  • 在进入目标网页前,打开新的标签页,先打开控制台,再进入目标网址
  • 浏览器右上角 三个点 -> 更多工具 -> 开发者工具

二、检测调试行为

2.1 检测控制台是否打开

常见的是通过 js 检测窗口大小来判断是否有打开控制台.
在这里插入图片描述

  • 解决方案

    • 控制台采用 undock into seperate window 模式展示可以绕过
    • 调试 js 定位检测位置,删掉检测代码
    • 有些是通过定时任务去定时检测,可以 hook 掉定时任务或 clearInterval / clearTimeout 清除定时器
      // hook 定时任务的创建逻辑
      window.setTimeout = function setTimeout(code, delay) {};
      window.setInterval = function setInterval(code, delay) {};
      // 清除已有定时任务
      (
        function() {
            function clearAllInterval(){
                let max_interval_id = setInterval(() => {}, 1000);
                for (let i = 0; i <= max_interval_id; i++){
                    clearInterval(i);
                }
            }
            setInterval(clearAllInterval, 3000);
        }
      )
      

2.2 检测代码是否阻塞

在调试模式下,某些操作(如断点调试、步进执行)会导致代码执行时间异常延长,可以利用这些特性检测调试行为。

// 示例:使用 `setInterval` 检测代码是否被阻塞
let start = Date.now();
setInterval(() => {
    const duration = Date.now() - start;
    if (duration > 100) {
        console.log('Debugger detected!');
        // 执行反调试操作
    }
    start = Date.now();
}, 50);
  • 解决方案
    • 调试 js 定位检测位置,删掉检测代码
    • 提前 hook 定时任务(参考上面)
    • clearInterval / clearTimeout 清除定时器(参考上面)

2.3 利用 console

通过覆盖 console 的方法,导致调用 console 时会进入开发者自定义的反爬逻辑中,误导调试效果,进入到错误逻辑中,甚至引起内存崩溃无法调试。
在这里插入图片描述
利用浏览器正常模式和开发者模式下 console.log 的执行区别实现

  • 开发者工具模式下,console.log 为了展示更多信息,会尝试调用对象的 toString 方法,即使变量是字符串或简单对象。因此在打开开发者模式时,不管变量是什么类型,一律会调用它的 toStirng 方法。
  • 浏览器正常运行情况下,cnosole.log 会根据值的类型选择最合适的输出方式。对于字符串直接输出原始值,对于其他类型的对象则可能会试图调用 toString 方法来生成字符串表示,或输出对象的原始结构。
    在这里插入图片描述
  • 解决方案
    • 提前保存原始的 console 对象为 myconsole,禁用原来的 console,后续使用 myconsole 来打印信息
      在这里插入图片描述
    • 结合 override 调试 js,定位到覆盖 console 位置,删除代码

三、debugger 地狱

在代码调试过程中,频繁中断在 debugger 语句,陷入一个频繁暂停的“地狱”状态,难以继续正常调试和执行代码。

3.1 构建 debugger 的方式

3.1.1 普通 debugger

html 中的 script 标签只要有 debugger,程序都会在该语句暂停,属于最直接的 debugger 干扰。

  1. 静态构建 debugger
    在 html 中可直接看到 debugger 关键字,或者被混淆的 debugger
    在这里插入图片描述
    • 解决方案
      • 借助浏览器工具,在 debugger 暂停行,鼠标右键 => Never pause here,即可绕过
        在这里插入图片描述
      • override 源文件,删掉 debugger 代码
        override 使用步骤,首先将目标文件覆写到本地,使其可编辑
        在这里插入图片描述
        打开 Enable Local Overrides,我们覆写的文件有个紫色小点标记,意味着我们可以直接编辑文件删掉里面所有的 debugger,后续请求该 URL 时,就会执行我们编辑后的这个 html
        在这里插入图片描述
  2. 动态构建 debugger
    区别于上述直白的 debugger 形式,有时会通过 appendChild 在代码执行过程中对 html 的文档结构进行修改,动态增加嵌入了dubugger 关键字的节点,并且隐藏关键字和修改文档的位置。
    在这里插入图片描述
    • 解决方案
      • 调试 js 定位到修改 html 结构的位置,删掉代码
      • 提前 hook appendChild 解决
            // 解决 appendChild debugger
            _appendChild = Node.prototype.appendChild;
            Node.prototype.appendChild = function () {
                if (arguments[0].innerHTML && arguments[0].innerHTML.indexOf('debugger') != -1) {
                    arguments[0].innerHTML = '';
                };
                return _appendChild.apply(this, arguments);
            }
        

3.1.2 Eval 构建 debugger

在 JavaScript 中,eval() 函数是一个内置的全局函数,函数将传入的字符串作为 JavaScript 代码解析并在虚拟机中执行,允许动态执行代码。
在这里插入图片描述

  • 解决方案
    提前 hook 掉 eval 函数,替换执行代码字符串中的 debugger 内容
    // Hook eval 函数(示例代码):
    eval_ = eval;
    eval = function () {
        if (arguments[0].indexOf('debugger') == -1) {
            return eval_.apply(this, arguments);
        }
    }
    

3.1.3 Function 构建 debugger

在 JavaScript 中,Function 是一个内置的构造器对象,用于创建新的函数。Function 构造器允许动态地创建函数并传递字符串形式的代码。它的行为类似于 eval(),但比 eval() 更加安全和受限。

Function 构建 debugger 的形式有以下两种:

  1. 直接通过 Function 创建
    a = Function('debugger; console.log("i am in the anonymous;")');
    a();
    
    • 解决方案
      提前 hook 掉 Function,替换掉 debugger 内容。
      MyFunction = Function;
      Function = function () {
        if (arguments[0].indexOf('debugger') != -1) {
          arguments[0] = arguments[0].replace('debugger', '');
        }
        return MyFunction.apply(this, arguments);
      }
      
  2. 调用函数的 constructor 构建 debugger
    function test() {
      console.log("i am in the test");
    }
    test["constructor"]('debugger; console.log("i am in the anonymous;")')['call']();
    
    • 解决方案
      提前 hook 掉 Functionconstructor,替换掉 debugger 内容。
       _Function = Function;
       Function.prototype.constructor = function () {
         if (arguments[0].indexOf('debugger') != -1) {
           arguments[0] = arguments[0].replace('debugger', '');
         }
         return _Function.apply(this, arguments);
       };
      

3.2 无限 debugger 反爬的实现方式

3.2.1 定时任务

定时任务结合上述构建 debugger 的各种方式来达到贯穿全程的 debugger 效果。
在这里插入图片描述

  • 解决方案
    • override 文件,删掉创建定时器的代码
    • 测试下定时器中是否有关业务逻辑,无影响则删除所有定时器
      在这里插入图片描述

3.2.2 递归

通过函数的递归调用实现无限 debugger,如图所示:
在这里插入图片描述

  • 解决方案
    • 分析嵌套函数中 debugger 的构建方式,按照上述 debugger 的解决方案hook 代码
    • 分析递归函数代码,若递归函数中无业务逻辑,可将该递归函数置空
    • 分析递归的流程,可能存在环境检测未通过,进入了不该走的逻辑

四、内存爆破

当程序检测到是爬虫行为时,会触发一些操作,这些操作通过不断消耗浏览器内存资源,使其崩溃或响应缓慢,从而达到阻碍爬虫获取数据的目的。

4.1 实现方式

通过定时任务或者递归操作,不断的进行一些消耗内存的行为,例如:

  1. 死循环

    • 利用 while-true 构造死循环。
    • 函数无限递归,或者 for 循环不断往数组中写入数据,耗尽内存。
    • 需要注意的是,对于函数无限递归的情况,最外层如果有 try-catch,则并不影响程序后续运行。这是因为同步栈溢出(如递归导致的 RangeError)可以被 try-catch 捕获。内存耗尽(如大量数据分配导致的内存溢出)通常无法被 try-catch 捕获,因为这是引擎层级的问题,会直接终止进程。
  2. 大量DOM操作
    不断创建、删除或修改 DOM 元素,触发浏览器频繁的 DOM 重绘和重排,消耗大量内存。

  3. 重写本地数据
    频繁重写或追加 cookie、history 等本地数据,耗尽浏览器资源。

4.2 触发条件

整理了一些常见的内存爆破检测条件,检测未通过则会导致js 代码进入内存爆破的逻辑中。最终导致内存崩溃。

4.2.1 代码格式化检测

伴有关键字 new RegExptesttoString,利用 RegExp 对象的 test 对代码进行是否格式化的检测,如:换行,空格之类的变化,检测到代码被格式化,则进入死循环中。

  • 解决方案
    • 尽量直接扣取不做任何格式化的代码,可以很大程度避免
    • hook 被检测对象的 toString 方法,输出符合检测规则的内容
      在这里插入图片描述

4.2.2 浏览器指纹检测

一般会对浏览器和 Node 常见的 API 进行检测,如:documentlocationnavigatorglobalrequiretoStringsessionStoragelocalStorage 等进行不同程度的检测,检测不通过则进入反爬逻辑中。

  • 解决方案
    解决方案就是 ,分享几个补环境的小技巧,尽可能提高补环境效率。
    • 扣取代码之前,将基本的浏览器对象简单补充,如 windowlocationnavigator 这些
    • 代码混淆严重的情况,可以先 AST 简单解混淆,得到更多的明文,便于调试
    • 出现了明文的情况下,则可以先搜索一些 node 环境检测的关键字,如:globalrequire 等,提前处理掉这些检测
      在这里插入图片描述
    • 测试定时任务是否影响主业务,不影响可以直接在本地程序提前清理掉定时器
    • 仔细认真的调试,然后分阶段补环境

五、总结

文章结合自己经验,列举了一些常见的浏览器反调试的策略,也给出了一些解决方案,大家有更好的方案可以在评论区里提供,我看到后会及时更新到文章,为大家提供更多的解题思路,如有错误和不足之处还请指正。常见反爬策略整理 的汇总篇可以移步 常见反爬策略整理

6、交流群

不会经常刷博客,有需要者可以加本人,搜索 LOVE_SELF_AD_LIFE,进逆向群聊,一起探讨技术
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值