当前位置: 首页 > news >正文

反无限 Debugger三层防护方案

反无限 Debugger:三层防护方案

文档信息

属性内容
适用场景网页无限debugger断点、动态反调试脚本导致内存暴涨
依赖工具Chrome 浏览器、Tampermonkey 扩展
劫持脚本同级目录resetDebugger.js

一、问题背景

1.1 问题现象

  • 打开某些网站后,F12 开发者工具进入无限debugger断点,无法正常调试
  • Sources 面板中出现大量无真实文件路径的脚本(Chrome 常显示为VM123VM456等编号,仅为调试器里的虚拟脚本名,不是站点上的VM.js文件)
  • 浏览器内存占用持续增长(从几百 MB 涨到几 GB),最终页面卡死或崩溃

1.2 根本原因

网站通过evalnew Function、内联<script>等动态执行代码,不断生成含debugger的脚本(在 Sources 里可能显示为VMxxx,只是展示名),例如:

setInterval(function(){newFunction('debugger')();},10);

为什么单靠黑盒不够?黑盒(Ignore List)只让调试器「别在这些脚本里停断点」,并不能阻止页面继续生成和执行它们,内存仍会累积——能调试了,但内存仍会爆。


二、三层防护架构

层级方法解决问题
第一层Chrome Ignore List(黑盒)调试器不卡在动态脚本的debugger上,提升调试体验
第二层Tampermonkey +resetDebugger.js从运行时阻断反调试逻辑,减少动态脚本持续生成,根治内存问题
第三层浏览器性能优化进一步降低内存和 CPU 占用

核心原理:第二层通过 Tampermonkey 在document-start最早注入,借助@grant unsafeWindow在页面上下文中劫持FunctionsetIntervaleval等原生 API,从运行时阻断反调试逻辑。

2.1 三层分别管什么?(配合关系)

三层同时启用、互相补充,不是「先做完第一层再做第二层」的流水线:

层级何时生效作用对象一句话
第一层你按 F12 打开开发者工具时Chrome DevTools按 Ignore List 规则跳过指定脚本,不在其debugger处停住
第二层页面开始加载时(油猴脚本)页面里的 JavaScript别让new Function('debugger')等代码真正跑起来
第三层浏览器全局设置Chrome 进程降低标签页长期占用的内存和 CPU
  • 第二层治本:拦截动态函数、高频定时器,减少反调试脚本持续生成(解决内存暴涨)。
  • 第一层治标(调试侧):即便仍有少量残留脚本,F12 也不会被无限断点卡住。
  • 第三层辅助:在治本基础上,再压低浏览器自身的资源占用。

三、操作步骤

3.1 第一层:Chrome Ignore List(黑盒)

目的:让开发者工具在匹配到的脚本里自动跳过断点(不解决内存问题,只改善 F12 体验)。

说明:Ignore List 匹配的是 Sources 面板里显示的脚本名称/URL,不是磁盘上的某个固定文件。VM123这类名字是 Chrome 给eval/Function等动态代码起的虚拟编号,并不存在一个叫VM.js的实体文件。

步骤操作
1打开目标网页,按F12Sources
2观察左侧脚本树里反复出现的名称(常见为VM+ 数字,也可能是eval、匿名片段等)
3点击右上角⚙️ 设置Ignore List(旧版名 Blackboxing)
4点击Add pattern,按你在 Sources 里看到的名称添加规则(以下为常用示例,可按需增删)
5勾选Ignore content scripts
6关闭设置并刷新页面

示例 pattern(非必须照抄):

示例规则适用情况
VM\d+脚本名类似VM123VM456(Chrome 动态脚本常见展示名)
^VM名称以VM开头
你在 Sources 里看到的其它名称例如某站显示为chunk-xxx.js或固定前缀,应按实际名称自行添加 pattern

文档中的VM\d+等仅作示例。若站点不用VM前缀,请根据 Sources 面板真实名称配置,不要误以为必须存在VM.js或只需配置这三条。

预期:打开 F12 后,匹配到的脚本不再反复卡在debugger;未匹配的脚本仍可能触发断点,需结合第二层。


3.2 第二层:Tampermonkey + resetDebugger.js

3.2.1 安装 Tampermonkey
  • Chrome:Chrome 网上应用店
  • Edge:Edge 加载项

安装后确认扩展图标为彩色、已启用。

3.2.2 导入劫持脚本
步骤操作
1点击 Tampermonkey 图标 →管理面板
2点击+新建脚本
3删除编辑器默认内容
4打开同级文件resetDebugger.js,完整复制并粘贴
5Ctrl+S(Mac:Command+S)保存
6确认脚本状态为已启用(蓝色开关)
3.2.2.1@match在哪里配置?

不在本文档里,也不在Chrome / 油猴「设置」页面。规则写在脚本文件最上方// ==UserScript==// ==/UserScript==元数据块中。

在油猴管理面板里打开本脚本(或编辑仓库里的resetDebugger.js),可见类似:

// ==UserScript==// @name 终极反无限Debugger - ...// @match *://*/* ← 所有网站都生效(无需再写具体域名)// @exclude-match *://*/*frame* ← 排除 URL 含 frame 的页面// @noframes ← 不在 iframe 里运行// ==/UserScript==

只让个别站点生效时:删掉或注释@match *://*/*,只保留目标域名,例如:

// 先删掉或注释:// @match *://*/*// @match *://*.example.com/*

改完后在油猴编辑器里Ctrl+S保存,再刷新目标网页。

3.2.3 脚本拦截点(resetDebugger.js)

使用@inject-into page在页面上下文直接安装 Hook(@grant none,不用unsafeWindow/GM_addElement,避免油猴addElement: failed to find created element等桥接错误)。

控制台日志前缀:[终极方案]。拦截逻辑:

序号拦截点行为
1Function(Proxy)函数体含debugger时返回空函数;保留原生Function.prototype
2setInterval/setTimeout仅当回调明确含debugger时返回0
3requestAnimationFrame回调含debugger时返回0
4DOM 清理每 10 秒移除短小的含debugger内联<script>
5window.gc(可选)浏览器暴露gc时每 60 秒回收一次

元数据要点:@inject-into page@grant none@noframes@exclude-match *frame*;默认@match *://*/*已移除eval劫持


3.3 第三层:浏览器性能优化

目的:降低长期打开问题页时的资源占用。

优化项操作路径建议
禁用不必要扩展chrome://extensions/调试时临时关闭无关扩展
内存节省模式chrome://settings/performance开启「内存节省程序」等选项
清理缓存Ctrl+Shift+Delete时间范围「所有时间」,勾选缓存后清除

四、验证方法

4.1 验证步骤

步骤操作预期结果
1打开问题网页页面正常加载,无明显卡顿
2F12不进入无限debugger断点
3查看Console出现[终极方案] 脚本已加载所有反调试拦截器已部署完成;触发拦截时会有对应阻止日志
4Shift+Esc打开任务管理器观察该标签页内存
5保持页面 10–30 分钟内存稳定在约 200–500 MB,不持续飙涨

4.2 效果对比

状态调试体验内存趋势
无任何措施❌ 无限断点,无法操作🔴 涨至数 GB 后崩溃
仅第一层(黑盒)✅ 可调试🟡 仍持续增长
仅第二层(油猴)✅ 可调试🟢 稳定可控
三层全开(推荐)✅ 流畅🟢 稳定可控

五、故障排查

问题可能原因解决方案
脚本未生效Tampermonkey 未启用确认扩展已安装并启用,硬刷新页面
页面功能异常@match过宽误伤改为具体域名@match
仍有少量内存增长站点使用非常规反调试在 Sources 中确认脚本显示名后,向 Ignore List追加对应 pattern(非固定VM规则)
bind/apply/intrinsic错误用普通函数整体替换了Function,破坏原型链使用仓库最新resetDebugger.js(Proxy 包装),硬刷新
addListener(仅油猴content.jsiframe 中扩展 API 不可用确认脚本含@noframes,关闭油猴「在所有框架中注入」
addElement: failed to find created elementGM_addElement误把parentNode写在 attributes 里使用最新脚本(@inject-into page,不再用GM_addElement
控制台其它报错与其他扩展冲突临时禁用其他扩展逐个排查
更新脚本后不生效缓存清缓存后重新加载

六、常见问题(FAQ)

Q1:脚本会影响所有网站吗?
默认@match *://*/*匹配全部站点。若只需特定站,改为例如@match *://*.example.com/*

Q2:会被网站检测到吗?
脚本在document-start注入并 Hook 原生方法;站点自身的检测逻辑通常也依赖这些方法,实践中难以单独识别本脚本。

Q3:为什么第一层不能根治内存?
Ignore List 只影响「调试器要不要停断点」,不阻止页面执行动态反调试代码;高频setInterval+new Function('debugger')仍会消耗 CPU 与内存,需依赖第二层拦截。

Q4:window.gc是什么?
Chrome 可选暴露的强制垃圾回收 API。需用启动参数开启后,脚本才会进入第 7 项逻辑(每 60 秒调用一次):

chrome.exe --js-flags="--expose-gc"

未开启时脚本前 6 项拦截与 DOM 清理仍正常工作,只是不会周期性调用gc

Q5:脚本能加载,但 pageSpy 等库报Cannot read properties of undefined (reading 'bind')
说明脚本曾用普通函数整体替换Function,导致Function.prototype.bind失效。当前脚本已改为Proxy 包装原生Function,仅在代码含debugger时拦截;请用仓库最新resetDebugger.js覆盖油猴中的脚本后硬刷新。

Q6:content.jsCannot read properties of undefined (reading 'addListener')
这是油猴在iframe(如__migu_web_refactor_frame)里注入的桥接脚本;该环境下chrome.runtime.onMessage常为undefined。若脚本使用unsafeWindowdocument-start改页面 API,会加重 iframe 桥接异常。当前脚本使用@inject-into page+@grant none,在页面上下文直接 Hook,并配合@noframes。若仍报错,请在油猴设置 → 常规关闭「在所有框架中注入」,保存后硬刷新。

Q7:addElement: failed to find created elementindexOf called on null
常见错误写法:GM_addElement('script', { parentNode: document, textContent: ... })——parentNode不能写在 attributes 里。正确写法为GM_addElement(parent, 'script', { textContent })或省略 parent 由油猴挂到head。当前脚本已改为@inject-into page,不再使用GM_addElement

七、配合使用脚本 resetDebugger.js

// ==UserScript==// @name 终极反无限Debugger - 内存优化+源头阻断// @namespace http://tampermonkey.net/// @description 彻底解决动态脚本无限debugger导致的内存暴涨问题// @author Ultimate Solution//// --- 生效范围(在油猴编辑器里改这里,不是在 Chrome 设置里)---// @match *://*/*// 在哪些网址运行;*://*/* 表示所有 http/https 页面。// 若只想对部分站点生效:注释掉本行,改为例如 @match https://***.com/*// @exclude-match *://*/*frame*// @exclude-match *://*/*Frame*// @exclude-match *://*/*iframe*// 排除 URL 路径中含 frame/iframe 的页面,减少在嵌入页里触发油猴桥接报错。// @noframes// 不在 iframe 子框架中运行本脚本(反调试一般在顶层页;可减轻 content.js 类报错)。//// --- 注入时机与方式 ---// @inject-into page// 在「页面上下文」执行,才能直接 Hook 页面的 Function / 定时器等;// 不用 unsafeWindow / GM_addElement,避免 addElement、addListener 等桥接错误。// @run-at document-start// 尽早注入,抢在站点反调试脚本之前安装 Hook。// @grant none// 不申请 GM_* 权限;与 @inject-into page 配合,脚本即页面代码。// ==/UserScript==(function(){"use strict";if(window!==window.top){return;}if(window.__RESET_DEBUGGER_HOOKED__){return;}window.__RESET_DEBUGGER_HOOKED__=true;console.log("[终极方案] 脚本已加载,开始拦截所有反调试行为");functionhasDebugger(code){if(typeofcode!=="string")returnfalse;constt=code.trim();returnt==="debugger"||t==="debugger;"||/\bdebugger\s*;/.test(code);}functionnoopFn(){}constOriginalFunction=Function;constPatchedFunction=newProxy(OriginalFunction,{apply(target,thisArg,argArray){constbody=argArray[argArray.length-1]||"";if(hasDebugger(body)){console.log("[终极方案] 已阻止debugger函数生成");returnnoopFn;}returnReflect.apply(target,thisArg,argArray);},construct(target,argArray,newTarget){constbody=argArray[argArray.length-1]||"";if(hasDebugger(body)){console.log("[终极方案] 已阻止debugger函数生成");returnnoopFn;}returnReflect.construct(target,argArray,newTarget);},});window.Function=PatchedFunction;constoriginalSetInterval=window.setInterval;window.setInterval=function(handler,timeout,...args){if(typeofhandler==="function"&&hasDebugger(Function.prototype.toString.call(handler))){console.log("[终极方案] 已阻止反调试 setInterval");return0;}if(typeofhandler==="string"&&hasDebugger(handler)){console.log("[终极方案] 已阻止反调试 setInterval(字符串)");return0;}returnoriginalSetInterval.call(this,handler,timeout,...args);};constoriginalSetTimeout=window.setTimeout;window.setTimeout=function(handler,timeout,...args){if(typeofhandler==="function"&&hasDebugger(Function.prototype.toString.call(handler))){console.log("[终极方案] 已阻止反调试 setTimeout");return0;}if(typeofhandler==="string"&&hasDebugger(handler)){console.log("[终极方案] 已阻止反调试 setTimeout(字符串)");return0;}returnoriginalSetTimeout.call(this,handler,timeout,...args);};constoriginalRAF=window.requestAnimationFrame;if(typeoforiginalRAF==="function"){window.requestAnimationFrame=function(callback){if(typeofcallback==="function"&&hasDebugger(Function.prototype.toString.call(callback))){console.log("[终极方案] 已阻止动画帧中的debugger");return0;}returnoriginalRAF.call(this,callback);};}constoriginalSetIntervalForCleanup=originalSetInterval;constscriptCleanupInterval=originalSetIntervalForCleanup.call(window,()=>{constscripts=document.querySelectorAll("script");letcleaned=0;scripts.forEach((script)=>{constcontent=script.textContent||script.innerText;if(content&&hasDebugger(content)&&content.length<500){script.remove();cleaned++;}});if(cleaned>0){console.log("[终极方案] 已清理",cleaned,"个残留的debugger脚本");}},10000,);window.addEventListener("beforeunload",()=>{window.clearInterval(scriptCleanupInterval);});if(window.gc){originalSetIntervalForCleanup.call(window,()=>{window.gc();},60000,);}console.log("[终极方案] 所有反调试拦截器已部署完成");})();
http://www.gsyq.cn/news/1469632.html

相关文章:

  • 2026年观光船厂家推荐:新能源电动/画舫仿古/双层豪华/玻璃钢钢质铝合金定制厂商深度解析与选购指南 - 品牌企业推荐师(官方)
  • 2026年沈阳庭院灯厂家TOP5:工期短质量优,谁是你的最佳选择?
  • 藏家福音!京顺斋天津上门回收,足不出户盘活手中藏品 - 深鉴新闻
  • 2026年华南成品风管实力厂家排行:5家头部供应商实测解析 - 奔跑123
  • 国家级智能车竞赛获奖方案:原理图+PCB+驱动源码全开源
  • 2026 北京上门回收字画排行榜,六家正规机构详细介绍 - 品牌排行榜单
  • Python之stringsim包语法、参数和实际应用案例
  • 实战指南:基于快马平台构建企业级oh my opencode开源生态平台
  • A股指数不上涨不赚钱的原因
  • 快速构建前端工具库原型:用快马一键生成小宇工具库完整项目框架
  • 如何用快马平台将markdown文档秒变可运行网站原型
  • DataX从入门到精通 第2课 ETL之DataX 安装datax-web
  • 海牙认证去哪办?网上办公证和海牙认证流程与注意事项 - GrowthUME
  • 2026低代码双榜杀:IDC份额+信通院技术,谁在裸泳?
  • 目标检测调参实战:用CIOU Loss在YOLOv5/v8上提升mAP的完整流程
  • 3步掌握:如何用Detect-It-Easy构建自动化文件指纹分析流水线?
  • 告别锐捷客户端:WinSCP+抓包工具,给Padavan路由器‘植入’校园网认证的完整指南
  • 三步突破:重新定义Dell G15散热控制的轻量革命
  • 八目蛛网络(免费工具网站导航)
  • 新手福音:用快马平台生成burpsuite安装交互教程,三步完成安全工具部署
  • 苏州购宠避坑指南|姑苏+虎丘双店明轩猫犬舍,江南本地繁育健康萌宠优选 - 萌宠俱乐部
  • 77GHz FMCW雷达二维SAR成像全流程Matlab实现:含距离-多普勒处理、运动误差补偿与方位压缩
  • 类器官培养新选择:InSphero Gri3D水凝胶微腔板如何实现标准化3D细胞培养与高通量研究?
  • AI智能体项目的开发流程
  • 深入解析昇腾开发工具集 asc-tools:架构设计与应用实践
  • LayaAir里直接拖选Unity粒子.lh文件,实时预览+自由转视角
  • 2026上海顶尖MBA学费全览:安泰领衔,五校学制与择校指南
  • 2026嘉兴防水补漏哪家好?住建实地测评权威榜单TOP5|卫生间免砸砖/阳台屋顶/厨卫漏水维修(6月嘉兴专项调研) - 苏易修缮
  • 2026年 磁致伸缩位移/液位传感器厂家推荐榜:精准沉降检测与耐用技术标杆之选 - 品牌企业推荐师(官方)
  • 飞书 PPAP Audit Agent:汽车供应链质量审核的智能化落地方案