// ==UserScript== // @name 广告终结者 // @namespace http://tampermonkey.net/ // @version 3.9.5.1 // @description 一个手机端via浏览器能用的强大的广告拦截器 // @match *://*/* // @license MIT // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @grant GM_getResourceText // @grant GM_addStyle // @run-at document-start // ==/UserScript== (function() { 'use strict'; const originalMutationObserver = window.MutationObserver; window.MutationObserver = class extends originalMutationObserver { observe(target, options) { const sanitizedOptions = { childList: options.childList ? true : false, attributes: options.attributes ? [...(options.attributeFilter || []), 'data-ad-', 'src', 'id', 'style', 'class'].some(f => f) : false, subtree: true, characterData: false }; return super.observe(target, Object.assign({}, options, sanitizedOptions)); } takeRecords() { return []; } }; const HOSTNAME = location.hostname.replace(/\./g, '\\.'); const REGEX = { dynamicId: /^(?:([0-9a-f]{4}-?){4,}|[0-9a-f]{16,}|[\dA-F-]{18,}|[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12})$/i, jsAdPattern: /(?:window\.open|document\.write|createElement\(['"]script['"]|location\.replace|setTimeout\s*\([^)]*?\b(?:window\.open|document\.write)|eval\s*\(|new\s+Function\s*\(|appendChild\s*\(.*?\bscript\b|on(?:click|load)\s*=\s*['"]?\s*(?:window\.open))/i, adAttributes: /(推广|广告|gg|sponsor|推荐|guanggao|syad|bfad|弹窗|悬浮|葡京|banner|pop(?:up|under)|track(?:ing)?)(?:$|[_-])/i, thirdParty: new RegExp(`^(https?:\\/\\/(.*\\.)?${HOSTNAME}(\\/|$)|data:|about:blank)`, 'i'), obfuscationPatterns: [ { type: '字符串混淆属性访问', pattern: /\["\\x73\\x72\\x63"\]\s*=/i, riskLevel: 3 }, { type: 'Math长属性访问', pattern: /^Math\[\s*["']\w{5,}["']\s*\]/im, riskLevel: 2 }, { type: '连续十六进制转义', pattern: /\\x[0-9a-f]{2}(?:\\x[0-9a-f]{2}){5,}/gi, riskLevel: 3 }, { type: '连续Unicode转义', pattern: /\\u[0-9a-f]{4}(?:\\u[0-9a-f]{4}){3,20}/gi, riskLevel: 3 }, { type: '数组数值操作', pattern: /\[(?:0x[0-9a-f]+|\d+),?(?:0x[0-9a-f]+|\d+)\]\.(?:join|reverse|map)/i, riskLevel: 2 }, { type: '动态代码执行', pattern: /(eval|(?:new\s+Function\s*\(\s*(["'])\s*)\1|set(?:Timeout|Interval|Immediate)\s*\(\s*function\s*\(\)\s*{)/i, riskLevel: 4 }, { type: 'Window动态属性访问', pattern: /window\[\s*["']\w{5,}["']\s*\]/im, riskLevel: 3 }, { type: 'Document动态属性访问', pattern: /document\[\s*(["'])\w{5,}\1\s*\]/im, riskLevel: 3 }, { type: '长哈希值属性', pattern: /\b[\w$]+\s*:\s*"(?:\\x[0-9a-f]{2}){16,}|[\da-f]{32}"/i, riskLevel: 3 }, { type: '短变量名长字符串赋值', pattern: /\b[a-z\d]{1,3}\s*=\s*["']\w{16,}["']/gi, riskLevel: 2 }, { type: '可疑变量初始化', pattern: /(var|let|const)\s+([a-z\d]{2,4})\s*=\s*(?:\d{8,}|"(?:[0-9a-f]{16,}|[\w+/=]{20,})")/gi, riskLevel: 3 }, { type: '隐藏iframe创建', pattern: /document\.createElement\(['"]iframe['"]\)\.setAttribute\(['"]style['"],\s*['"]display:\s*none/im, riskLevel: 4 }, { type: '空白iframe加载', pattern: /document\.createElement\(['"]iframe['"]\)\.src\s*=\s*['"]about:blank['"]/im, riskLevel: 3 }, { type: '多重属性混淆', pattern: /\[(?:'src'|"data-rc")\]\.forEach\(function\(a\)\{this\.setAttribute\(a,/im, riskLevel: 4 }, { type: '定时器混淆链', pattern: /setTimeout\(function\(\)\{setInterval\(function\(\)\{new\s+Function\(.*?\)/gim, riskLevel: 5 }, { type: '双重编码payload', pattern: /eval\(atob\(['"][A-Za-z0-9+/=]{40,}['"]\)\)/gi, riskLevel: 5 }, { type: '长Base64字符串', pattern: /[A-Za-z0-9+/=]{60,}/gi, riskLevel: 3 }, { type: '异常try-catch结构', pattern: /try\s*\{[^}]*\}\s*catch\s*\(e\)\s*\{[^}]*eval\(e\.message\)/gim, riskLevel: 4 }, { type: '多级属性访问', pattern: /window\[\w{5,}\]\[\w{5,}\]\[\w{5,}\]/gim, riskLevel: 3 }, { type: '异常函数调用链', pattern: /function\s+\w{1,3}\(\)\{return\s+\w{1,3}\(\)\.call\(this\)\}\(\).apply\(window\)/gim, riskLevel: 4 }, { type: '隐藏样式注入', pattern: /document\.body\.style\.\w+\s*=\s*['"]none['"]/gim, riskLevel: 2 }, { type: '多层数组混淆', pattern: /\[\[\[['"]\w+['"]\]\]\]/gim, riskLevel: 3 }, { type: '异常事件监听', pattern: /addEventListener\(['"]click['"],\s*function\(\)\{[^}]*eval\(/gim, riskLevel: 4 }, { type: '十六进制混淆', pattern: /\\x[0-9a-f]{2}(?:\\x[0-9a-f]{2}){5,}/gi, riskLevel: 4 }, { type: 'Unicode混淆', pattern: /\\u[0-9a-f]{4}(?:\\u[0-9a-f]{4}){3,}/gi, riskLevel: 3 }, { type: '多层数组混淆', pattern: /\[\[\[['"]\w+['"]\]\]\]/gim, riskLevel: 3 }, { type: '动态属性拼接', pattern: /\[(['"])\w+\1\s*\+\s*['"]\w+['"]\]/g, riskLevel: 4 }, { type: '环境检测代码', pattern: /navigator\.\w+\s*===?\s*undefined/g, riskLevel: 5 }, { type: '时间戳混淆', pattern: /0x[a-f0-9]{8}(?:\+0x[a-f0-9]{8}){3,}/g, riskLevel: 3 }, { type: '反格式化字符串', pattern: /%[0-9a-f]{2}%[0-9a-f]{2}%[0-9a-f]{2}/g, riskLevel: 3 }, { type: '异常函数链', pattern: /function\s+\w{1,3}\(\)\{return\s+\w{1,3}\(\)\.call\(this\)\}\(\)\.apply\(window\)/gim, riskLevel: 4 } ], }; const DEFAULT_MODULES = { main: false, dynamicSystem: false, layoutSystem: false, frameSystem: false, mediaSystem: false, thirdPartyBlock: false, floating: false, specialUA: true }; const DEFAULT_CSP_RULES = [ { id: 1, name: '仅允许同源脚本', rule: "script-src 'self'", enabled: true }, { id: 2, name: '禁止内联脚本', rule: "script-src 'unsafe-inline'", enabled: false }, { id: 3, name: '禁止eval执行', rule: "script-src 'unsafe-eval'", enabled: false }, { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false }, { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false }, { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true }, { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false }, { id: 8, name: '禁止媒体资源加载', rule: "media-src 'none'", enabled: false }, { id: 9, name: '禁止对象嵌入', rule: "object-src 'none'", enabled: false } ]; const CONFIG = { modules: { ...DEFAULT_MODULES }, protectionRules: { dynamicIdLength: 32, zIndexThreshold: 100, maxFrameDepth: 4 }, csp: { enabled: false, rules: DEFAULT_CSP_RULES.map(rule => ({ ...rule })) }, mobileOptimizations: { lazyLoadImages: true, removeImagePlaceholders: true }, moduleNames: { dynamicSystem: '动态检测系统', layoutSystem: '布局检测系统', frameSystem: '框架过滤系统', mediaSystem: '媒体检测系统', thirdPartyBlock: '第三方拦截', floating: '浮动广告检测', specialUA: '添加特殊UA' }, modulePriority: [ 'thirdPartyBlock', 'specialUA', 'frameSystem', 'dynamicSystem', 'mediaSystem', 'layoutSystem', 'floating' ], performance: { highRiskModules: ['thirdPartyBlock', 'frameSystem'], visibleAreaPriority: true, styleCacheTimeout: 600000, mutationProcessLimit: 40, mutationProcessTimeLimit: 10, idleCallbackTimeout: 2500, throttleScrollDelay: 200, debounceMutationDelay: 300, longTaskThreshold: 500, degradeThreshold: 5 }, originalUA: { userAgent: navigator.userAgent, platform: navigator.platform } }; const Logs = { logs: [], maxLogs: 30, add(moduleKey, element, reason) { const getElementInfo = (el) => { try { return { tag: el.tagName || '未知标签', id: el.id ? `#${el.id}` : '', class: el.className ? `.${el.className.split(/\s+/).join('.')}` : '', src: el.src || el.href || el.dataset.src || '', attributes: Array.from(el.attributes) .filter(attr => ['id', 'class', 'src', 'href'].includes(attr.name)) .map(attr => `${attr.name}="${attr.value.slice(0,30)}"`) } } catch(e) { return { error: '元素解析失败' } } }; const elementInfo = element ? getElementInfo(element) : {}; const content = element ? this.getContent(element) : '动态创建元素'; this.logs.push({ module: CONFIG.moduleNames[moduleKey] || moduleKey, element: `${elementInfo.tag}${elementInfo.id}${elementInfo.class}`, content: content, context: { src: elementInfo.src?.slice(0, 200) || '', attributes: elementInfo.attributes || [], parent: element?.parentElement?.tagName || '无父节点' }, reason: { type: reason.type || '自动拦截', detail: reason.detail || '', pattern: reason.regex ? reason.regex.map(r => r.source || r) : [], stack: new Error().stack.split('\n').slice(2,5) }, timestamp: Date.now() }); if (this.logs.length > this.maxLogs) this.logs = this.logs.slice(-this.maxLogs); }, getContent(element) { try { return element.outerHTML .replace(/\s+/g, ' ') .replace(//g, '') .slice(0, 200) + (element.outerHTML.length > 200 ? '...' : ''); } catch(e) { return '[安全限制无法获取内容]'; } }, clear() { this.logs = []; GM_notification('日志已清空'); } }; const perf = { pendingTasks: [], isProcessing: false, processed: new WeakSet(), adElements: new Set(), styleCache: new Map(), lastStyleCacheClear: Date.now(), longTaskCount: 0, degraded: true }; const ModuleManager = { modules: {}, register(name, module) { this.modules[name] = module; }, init() { Object.values(this.modules).forEach(module => { if (typeof module.init === 'function') { module.init(); } }); }, run(moduleName, element) { if (!CONFIG.modules.main || !CONFIG.modules[moduleName]) return false; const module = this.modules[moduleName]; if (module && typeof module.check === 'function') { return module.check(element); } return false; } }; const AdUtils = { processedScripts: new WeakSet(), originalAppendChild: Node.prototype.appendChild, originalCreateElement: document.createElement, isDynamicSystemActive: false, initScriptObserver: function() { const originalAppend = Node.prototype.appendChild; const originalCreate = document.createElement; Node.prototype.appendChild = function(node) { if (AdUtils.isDynamicSystemActive && node.tagName === 'SCRIPT') { AdUtils.processScriptElement(node); return node; } return originalAppend.call(this, node); }; document.createElement = function(tagName) { const el = originalCreate.call(document, tagName); if (AdUtils.isDynamicSystemActive && tagName.toLowerCase() === 'script') { return AdUtils.createScriptProxy(el); } return el; }; AdUtils.observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1 && node.tagName === 'SCRIPT') { AdUtils.processScriptElement(node); } }); }); }); if (AdUtils.isDynamicSystemActive) { AdUtils.observer.observe(document.documentElement, { childList: true, subtree: true }); } }, enableDynamicSystem() { if (!this.isDynamicSystemActive) { this.isDynamicSystemActive = true; this.initScriptObserver(); } }, disableDynamicSystem() { if (this.isDynamicSystemActive) { this.isDynamicSystemActive = false; Node.prototype.appendChild = this.originalAppendChild; document.createElement = this.originalCreateElement; if (this.observer) { this.observer.disconnect(); } } }, createScriptProxy: function(scriptElement) { return new Proxy(scriptElement, { set: (target, prop, value) => { if (prop === 'src' || prop === 'innerHTML') { AdUtils.processScriptElement(target); } target[prop] = value; return true; } }); }, processScriptElement: function(scriptElement) { if (this.processedScripts.has(scriptElement)) return; if (ModuleManager.modules.dynamicSystem?.enabled) { this.nuclearRemove(scriptElement, 'dynamicSystem', { type: '动态脚本防御', detail: '拦截动态创建的脚本元素' }); ['src', 'onload', 'onerror', 'integrity'].forEach(prop => { try { delete scriptElement[prop]; } catch(e) {} }); } }, nuclearRemove: function(scriptElement, moduleKey, reason) { if (scriptElement.dataset.adNuclearRemoved) return false; try { if (scriptElement.parentNode) { scriptElement.parentNode.removeChild(scriptElement); } const unsafeAttributes = ['src', 'onload', 'onerror', 'integrity']; unsafeAttributes.forEach(attr => { scriptElement.removeAttribute(attr); }); if (scriptElement.src) { const abort = new AbortController(); fetch(scriptElement.src, { signal: abort.signal, priority: 'low' }).catch(() => {}); abort.abort(); } scriptElement.dataset.adNuclearRemoved = true; this.processedScripts.add(scriptElement); Logs.add(moduleKey, scriptElement, { ...reason, detail: `[原子级移除] ${reason.detail}`, nuclear: true }); return true; } catch(e) { console.error('终极移除失败:', e); return false; } }, safeRemove: function(element, moduleKey, reason) { if (element.tagName === 'SCRIPT') { return this.nuclearRemove(element, moduleKey, reason); } try { Logs.add(moduleKey, element, reason); element.style.display = 'none'; element.style.visibility = 'hidden'; element.setAttribute('data-ad-removed', 'true'); return true; } catch(e) { return false; } } }; const StyleManager = { styleElement: null, injectedSheets: new Set(), inject() { if (this.injectedSheets.size > 0) return; const sheet = new CSSStyleSheet(); sheet.replaceSync(` [data-ad-cleaned] { all: initial !important; display: none !important; visibility: hidden !important; opacity: 0 !important; position: absolute !important; z-index: -9999 !important; width: 0 !important; height: 0 !important; pointer-events: none !important; } [data-ad-cleaned] * { all: unset !important; } `); document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; this.injectedSheets.add(sheet); const style = document.createElement('style'); style.textContent = ` @layer adBlocker { :not([data-ad-cleaned]) { contain: style !important; } [data-ad-cleaned] { animation: none !important; transition: none !important; } } `; style.setAttribute('data-ad-permanent', 'true'); document.documentElement.prepend(style); const checkStyles = () => { if (!document.contains(style)) { document.documentElement.prepend(style); } if (!document.adoptedStyleSheets.includes(sheet)) { document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; } requestAnimationFrame(checkStyles); }; checkStyles(); }, remove() { this.injectedSheets.forEach(sheet => { document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== sheet); }); document.querySelectorAll('style[data-ad-permanent]').forEach(s => s.remove()); }, toggle(enable) { enable ? this.inject() : this.remove(); } }; const CSPManager = { currentPolicy: null, generatePolicy() { const merged = {}; CONFIG.csp.rules.filter(rule => rule.enabled).forEach(rule => { const [directive, ...values] = rule.rule.split(/\s+/); merged[directive] = (merged[directive] || []).concat(values); }); return Object.entries(merged) .map(([k, v]) => `${k} ${[...new Set(v)].join(' ')}`) .join('; '); }, inject() { this.remove(); if (!CONFIG.csp.enabled) return; const policy = this.generatePolicy(); this.currentPolicy = policy; const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = policy; if (document.head) { document.head.appendChild(meta); } else { document.documentElement.insertBefore(meta, document.documentElement.firstChild); } }, remove() { const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); if (meta) meta.remove(); }, toggleRule(ruleId, enable) { const rule = CONFIG.csp.rules.find(r => r.id === ruleId); if (rule) rule.enabled = enable; }, injectInitialCSP() { if (!CONFIG.csp.enabled) return; const initialRules = CONFIG.csp.rules .filter(rule => [1, 6].includes(rule.id) && rule.enabled) .map(rule => rule.rule) .join('; '); if (initialRules) { const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = initialRules; if (document.head) { document.head.appendChild(meta); } else { document.documentElement.insertBefore(meta, document.documentElement.firstChild); } } } }; const Detector = { getCachedStyle(el) { if (Date.now() - perf.lastStyleCacheClear > CONFIG.performance.styleCacheTimeout) { perf.styleCache.clear(); perf.lastStyleCacheClear = Date.now(); } const key = `${el.nodeName}#${el.id}.${Array.from(el.classList).join('.')}`; if (!perf.styleCache.has(key)) { try { perf.styleCache.set(key, getComputedStyle(el)); } catch (e) { console.warn("Error getting computed style:", e); return {}; } } return perf.styleCache.get(key); }, isVisible(el) { const style = getComputedStyle(el); if (style.display === 'none' || style.visibility === 'hidden') return false; const rect = el.getBoundingClientRect(); return !( rect.width === 0 || rect.height === 0 || (rect.top >= window.innerHeight || rect.bottom <= 0) || (rect.left >= window.innerWidth || rect.right <= 0) ); } }; const Processor = { collectAds() { const treeWalker = document.createTreeWalker( document.documentElement, NodeFilter.SHOW_ELEMENT, null, false ); perf.adElements.clear(); while (treeWalker.nextNode()) { const element = treeWalker.currentNode; if (perf.processed.has(element)) continue; let modulePriority = CONFIG.modulePriority; if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) { modulePriority = [...CONFIG.performance.highRiskModules, ...modulePriority.filter(m => !CONFIG.performance.highRiskModules.includes(m))]; } for (const moduleKey of modulePriority) { if (ModuleManager.run(moduleKey, element)) { perf.adElements.add(element); perf.processed.add(element); break; } } } }, processElements() { if (perf.degraded) return; let startTime = performance.now(); let elementCount = 0; for (let element of perf.adElements) { if (elementCount >= CONFIG.performance.mutationProcessLimit || performance.now() - startTime > CONFIG.performance.mutationProcessTimeLimit) { perf.pendingTasks.push(() => Processor.processElements()); break; } if (perf.processed.has(element)) continue; let modulePriority = CONFIG.modulePriority; if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) { modulePriority = [...CONFIG.performance.highRiskModules, ...modulePriority.filter(m => !CONFIG.performance.highRiskModules.includes(m))]; } for (const moduleKey of modulePriority) { if (ModuleManager.run(moduleKey, element)) { perf.processed.add(element); break; } } elementCount++; } perf.isProcessing = false; if (perf.pendingTasks.length > 0) { scheduleProcess(); } } }; const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === 8 && node.data.includes('Removed ad')) { node.parentNode.removeChild(node); } }); } }); mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (node.matches('[data-ad-permanent], [data-ad-removed], [data-ad-cleaned]')) { AdUtils.safeRemove(node, 'mutationClean', { type: 'DOM清理', detail: '检测到被移除元素的重新插入' }); return; } const walker = document.createTreeWalker( node, NodeFilter.SHOW_ELEMENT, { acceptNode: (n) => { return n.hasAttribute('data-ad-permanent') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } } ); let currentNode; while ((currentNode = walker.nextNode())) { AdUtils.safeRemove(currentNode, 'mutationClean', { type: 'DOM深度清理', detail: '检测到被移除元素的子元素' }); } } }); } else if (mutation.type === 'attributes') { if (mutation.target.hasAttribute('data-ad-permanent') && mutation.attributeName !== 'data-ad-permanent') { mutation.target.removeAttribute(mutation.attributeName); } } }); let startTime = performance.now(); let mutationCount = 0; let nodeCount = 0; for (let mutation of mutations) { if (mutationCount >= CONFIG.performance.mutationProcessLimit || nodeCount >= 50 || performance.now() - startTime > CONFIG.performance.mutationProcessTimeLimit) { debouncedScheduleProcess(); return; } if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (!perf.processed.has(node)) { perf.adElements.add(node); nodeCount++; } } }); } else if (mutation.type === 'attributes') { if (!perf.processed.has(mutation.target)) { perf.adElements.add(mutation.target); nodeCount++; } } mutationCount++; } scheduleProcess(); }); const scheduleProcess = (() => { const tasks = []; let isScheduled = false; function runTasks(deadline) { while (tasks.length && (deadline.timeRemaining() > 2 || deadline.didTimeout)) { const task = tasks.shift(); try { task(); } catch(e) { console.error('Task error:', e); } } if (tasks.length) { requestIdleCallback(runTasks, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { isScheduled = false; } } return function(task) { tasks.push(task); if (!isScheduled) { isScheduled = true; if (typeof requestIdleCallback === 'function') { requestIdleCallback(runTasks, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { setTimeout(() => runTasks({ timeRemaining: () => 50, didTimeout: true }), 50); } } }; })(); const debouncedScheduleProcess = debounce(scheduleProcess, CONFIG.performance.debounceMutationDelay); const UIController = { init() { this.loadConfig(); this.registerMainSwitch(); this.registerModuleSwitches(); this.registerLogCommands(); this.registerCSPCommands(); this.registerResetCommand(); StyleManager.toggle(CONFIG.modules.main); }, registerMainSwitch() { GM_registerMenuCommand( `🔘 主开关 [${CONFIG.modules.main ? '✅' : '❌'}]`, () => this.toggleMain() ); }, registerModuleSwitches() { Object.entries(CONFIG.moduleNames).forEach(([key, name]) => { GM_registerMenuCommand( `${name} [${CONFIG.modules[key] ? '✅' : '❌'}]`, () => this.toggleModule(key, name) ); }); }, registerLogCommands() { GM_registerMenuCommand('📜 查看拦截日志', () => this.showLogs()); GM_registerMenuCommand('🧹 清空日志', () => Logs.clear()); }, registerCSPCommands() { GM_registerMenuCommand('🛡️ CSP策略管理', () => this.manageCSP()); }, registerResetCommand() { GM_registerMenuCommand('🔄 重置设置', () => this.resetSettings()); }, toggleMain() { const newState = !CONFIG.modules.main; Object.keys(CONFIG.modules).forEach(key => { if (key !== 'main') CONFIG.modules[key] = newState; }); CONFIG.modules.main = newState; this.saveConfig(); StyleManager.toggle(newState); GM_notification(`主开关已${newState ? '启用' : '禁用'}`, `所有模块${newState ? '✅ 已激活' : '❌ 已停用'}`); location.reload(); }, toggleModule(key, name) { CONFIG.modules[key] = !CONFIG.modules[key]; CONFIG.modules.main = Object.values(CONFIG.modules) .slice(1).some(v => v); this.saveConfig(); StyleManager.toggle(CONFIG.modules.main); GM_notification(`${name} ${CONFIG.modules[key] ? '✅ 已启用' : '❌ 已禁用'}`); location.reload(); }, showLogs() { const logText = Logs.logs.map(log => { const parts = []; if (log.reason.type) parts.push(`类型: ${log.reason.type}`); if (log.reason.detail) parts.push(`详细: ${log.reason.detail}`); if (log.reason.pattern?.length) parts.push(`匹配规则: ${log.reason.pattern.join(', ')}`); return [ `模块: ${log.module}`, `元素: ${log.element}`, `内容: ${log.content}`, `上下文: ${log.context.src ? '来源:' + log.context.src : ''} ${log.context.parent ? '父节点:' + log.context.parent : ''}`, ...parts ].join('\n'); }).join('\n\n'); alert(`广告拦截日志(最近${Logs.maxLogs}条):\n\n${logText || '暂无日志'}`); }, manageCSP() { const rulesDisplay = CONFIG.csp.rules .map(r => `${r.id}. ${r.name} (${r.enabled ? '✅' : '❌'})`) .join('\n'); let input = prompt( `当前CSP策略状态: ${CONFIG.csp.enabled ? '✅ 已启用' : '❌ 已禁用'}\n\n` + "当前策略规则:\n" + rulesDisplay + "\n\n" + "输入选项:\n1. 开启策略 (输入 'enable')\n2. 关闭策略 (输入 'disable')\n" + "修改规则示例:输入 '1on' 启用规则1\n输入 '23off' 禁用规则2和规则3\n\n"+ "请输入你的选择:", "" ); if (input === null) return; input = input.trim().toLowerCase(); switch (input) { case 'enable': CONFIG.csp.enabled = true; this.saveConfig(); CSPManager.inject(); alert("CSP策略已启用"); location.reload(); break; case 'disable': CONFIG.csp.enabled = false; this.saveConfig(); CSPManager.remove(); alert("CSP策略已禁用"); location.reload(); break; default: if (/^\d+(?:on|off)$/i.test(input)) { this.modifyCSPRules(input); } else { alert("无效输入"); } } }, modifyCSPRules(actionInput) { const matches = actionInput.match(/^(\d+)(on|off)$/i); if (matches) { const ruleIds = matches[1].split('').map(Number); const enable = matches[2].toLowerCase() === 'on'; let updatedRules = []; ruleIds.forEach(id => { const rule = CONFIG.csp.rules.find(r => r.id === id); if (rule) { rule.enabled = enable; updatedRules.push(id); } }); this.saveConfig(); CSPManager.inject(); alert(`规则 ${updatedRules.join(',')} 已更新为 ${enable ? '启用' : '禁用'}`); location.reload(); } else { alert("输入格式错误,请按示例输入如'1on'或'123off'"); } }, resetSettings() { if (confirm("确定要重置所有设置吗?")) { Object.assign(CONFIG.modules, DEFAULT_MODULES); CONFIG.csp.enabled = false; CONFIG.csp.rules = DEFAULT_CSP_RULES.map(rule => ({ ...rule })); this.saveConfig(); CSPManager.remove(); alert("设置已重置为默认值"); location.reload(); } }, saveConfig() { GM_setValue(`adblockConfig_${location.hostname}`, JSON.stringify({ modules: CONFIG.modules, csp: CONFIG.csp })); }, loadConfig() { try { const savedConfig = JSON.parse(GM_getValue(`adblockConfig_${location.hostname}`, '{}')); if (savedConfig.modules) { Object.keys(CONFIG.modules).forEach(key => { if (savedConfig.modules.hasOwnProperty(key)) { CONFIG.modules[key] = savedConfig.modules[key]; } }); } if (savedConfig.csp) { Object.assign(CONFIG.csp, savedConfig.csp); } } catch (e) { console.error("加载配置失败:", e); } } }; function init() { UIController.init(); CSPManager.injectInitialCSP(); ModuleManager.init(); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'data-ad', 'src'] }); Processor.collectAds(); scheduleProcess(() => Processor.processElements()); window.addEventListener('scroll', throttle(() => { Processor.collectAds(); scheduleProcess(() => Processor.processElements()); }, CONFIG.performance.throttleScrollDelay)); window.addEventListener('resize', debounce(() => { Processor.collectAds(); scheduleProcess(() => Processor.processElements()); }, 150)); if (CONFIG.mobileOptimizations.lazyLoadImages) { document.addEventListener('DOMContentLoaded', () => { const images = document.querySelectorAll('img[data-src]'); images.forEach(img => { img.src = img.dataset.src; img.removeAttribute('data-src'); }); }); } if (CONFIG.mobileOptimizations.removeImagePlaceholders) { document.addEventListener('DOMContentLoaded', () => { const placeholders = document.querySelectorAll('.image-placeholder'); placeholders.forEach(placeholder => { placeholder.parentNode.removeChild(placeholder); }); }); } } function throttle(fn, delay) { let last = 0, timer = null; return function(...args) { const now = Date.now(); if (now - last >= delay) { fn.apply(this, args); last = now; } else { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); last = now; }, delay - (now - last)); } }; } function debounce(fn, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { try { fn.apply(this, args); } catch(e) { Logs.add('error', this, { type: 'debounceError', detail: e.message }); } }, delay); }; } const ASTAnalyzer = (function() { const tokenTypes = { IDENTIFIER: 'identifier', STRING: 'string', KEYWORD: 'keyword', OPERATOR: 'operator' }; return { getObfuscationRules: function(minLevel = 2) { return CONFIG.obfuscationPatterns.filter(r => r.riskLevel >= minLevel); }, checkObfuscation: function(code, minLevel = 2) { const rules = this.getObfuscationRules(minLevel); for (let i = 0; i < rules.length; i++) { try { if (rules[i].pattern.test(code)) { return true; } } catch (e) { console.error('[AST] 规则执行错误:', rules[i].type, e); } } return false; }, detectBase64: function(code) { const rule = CONFIG.obfuscationPatterns.find(r => r.type === '长Base64字符串'); return rule ? rule.pattern.test(code) : false; }, checkLexicalPatterns: function(code) { const tokens = this.tokenize(code); return this.checkLongIdentifiers(tokens) || this.checkHexEscapes(tokens) || this.checkObfuscation(code, 2); }, checkDynamicBehavior: function(code) { return this.checkObfuscation(code, 3) || this.checkDynamicElementCreation(code); }, checkDynamicElementCreation: function(code) { const rules = this.getObfuscationRules(3).filter( r => r.type.includes('iframe') || r.type.includes('元素创建') ); return rules.some(r => r.pattern.test(code)); }, tokenize: function(code) { const tokens = []; const regex = /(\b[a-zA-Z_$][\w$]*\b)|(['"])(?:(?!\2).|\\.)*?\2|([{}()[\];,])|(\S)/g; let match; while ((match = regex.exec(code)) !== null) { if (match[1]) { tokens.push({ type: tokenTypes.IDENTIFIER, value: match[1] }); } else if (match[2]) { tokens.push({ type: tokenTypes.STRING, value: match[0] }); } else if (match[3]) { tokens.push({ type: tokenTypes.OPERATOR, value: match[3] }); } else { tokens.push({ type: 'other', value: match[4] }); } } return tokens; }, checkLongIdentifiers: function(tokens) { return tokens.some(t => t.type === tokenTypes.IDENTIFIER && t.value.length > CONFIG.protectionRules.dynamicIdLength ); }, checkHexEscapes: function(tokens) { let hexCount = 0; for (const t of tokens) { if (t.type === tokenTypes.STRING) { const matches = t.value.match(/\\x[0-9a-f]{2}/gi) || []; hexCount += matches.length; if (hexCount >= 3) return true; } } return false; } }; })(); ModuleManager.register('thirdPartyBlock', { originals: { createElement: document.createElement, setAttribute: Element.prototype.setAttribute, appendChild: Node.prototype.appendChild, fetch: window.fetch, xhrOpen: XMLHttpRequest.prototype.open, documentWrite: document.write, documentWriteln: document.writeln, insertAdjacentHTML: Element.prototype.insertAdjacentHTML }, blockedUrls: new Set(), enabled: false, hostCache: new Map(), abortControllers: new WeakMap(), init() { if (!CONFIG.modules.thirdPartyBlock) return this.disable(); this.enable(); }, enable() { if (this.enabled) return; this.enabled = true; document.createElement = this.wrapElementCreation.bind(this); Element.prototype.setAttribute = this.wrapSetAttribute.bind(this); Node.prototype.appendChild = this.wrapAppendChild.bind(this); window.fetch = this.wrapFetch.bind(this); XMLHttpRequest.prototype.open = this.wrapXHROpen.bind(this); document.write = this.wrapDocumentWrite.bind(this); document.writeln = this.wrapDocumentWriteln.bind(this); Element.prototype.insertAdjacentHTML = this.wrapInsertAdjacentHTML.bind(this); document.addEventListener('beforescriptexecute', this.handleBeforeScriptExecute.bind(this)); }, disable() { if (!this.enabled) return; this.enabled = false; this.abortControllers.forEach(controller => controller.abort()); this.abortControllers.clear(); document.createElement = this.originals.createElement; Element.prototype.setAttribute = this.originals.setAttribute; Node.prototype.appendChild = this.originals.appendChild; window.fetch = this.originals.fetch; XMLHttpRequest.prototype.open = this.originals.xhrOpen; document.write = this.originals.documentWrite; document.writeln = this.originals.documentWriteln; Element.prototype.insertAdjacentHTML = this.originals.insertAdjacentHTML; document.removeEventListener('beforescriptexecute', this.handleBeforeScriptExecute); }, handleInterception(target, url) { if (this.blockedUrls.has(url)) return; this.blockedUrls.add(url); if (target.tagName === 'SCRIPT') { const abort = new AbortController(); this.abortControllers.set(target, abort); if (target.src) { fetch(target.src, { signal: abort.signal }) .catch(() => {}); abort.abort(); } const success = AdUtils.nuclearRemove(target, 'thirdPartyBlock', { type: '第三方脚本拦截', detail: `原子级移除脚本: ${url.slice(0, 80)}` }); Object.defineProperty(target, 'src', { get() { return ''; }, set() {}, configurable: false }); return success; } return this.blockElement(target); }, blockElement(element) { if (!element?.parentNode) return false; element.dataset.tpiBlocked = Date.now().toString(36); element.style.setProperty('display', 'none', 'important'); element.style.setProperty('visibility', 'hidden', 'important'); setTimeout(() => { try { if (element.parentNode) { element.parentNode.removeChild(element); } } catch(e) { console.debug('元素移除失败:', e); } }, 100); return true; }, isThirdParty(url) { try { const currentHost = new URL(location.href).hostname; const resourceHost = new URL(url, location.href).hostname; return !(resourceHost.endsWith(`.${currentHost}`) || resourceHost === currentHost); } catch(e) { return true; } }, wrapSetAttribute(name, value, element = this) { if (name === 'src' && this.isThirdParty(value)) { if (element.tagName === 'SCRIPT') { return this.handleInterception(element, value); } return this.blockElement(element); } return this.originals.setAttribute.call(element, name, value); }, wrapAppendChild(node, parent = this) { if (node.nodeType === 1) { const src = node.src || node.getAttribute('data-src') || ''; if (this.isThirdParty(src)) { if (node.tagName === 'SCRIPT') { this.handleInterception(node, src); return null; } return this.blockElement(node); } } return this.originals.appendChild.call(parent, node); }, wrapElementCreation(tagName) { const element = this.originals.createElement.call(document, tagName); const lowerTag = tagName.toLowerCase(); if (['script', 'iframe', 'img'].includes(lowerTag)) { return new Proxy(element, { set: (target, prop, value) => { if (prop === 'src' && this.isThirdParty(value)) { if (lowerTag === 'script') { this.handleInterception(target, value); } else { this.blockElement(target); } } target[prop] = value; return true; } }); } return element; }, wrapFetch(input, init) { const url = typeof input === 'string' ? input : input?.url; if (this.isThirdParty(url)) { Logs.add('thirdPartyBlock', document.createElement('fetch'), { type: 'Fetch请求拦截', detail: `URL: ${url?.slice(0,150) || 'unknown'}`, context: { method: init?.method || 'GET', mode: init?.mode || 'no-cors' } }); return Promise.reject(new Error('TPI: 第三方请求被拦截')); } return this.originals.fetch(input, init); }, wrapXHROpen(method, url) { if (this.isThirdParty(url)) { Logs.add('thirdPartyBlock', document.createElement('xhr'), { type: 'XHR请求拦截', detail: `${method} ${url.slice(0,150)}`, context: { async: this.originals.xhrOpen.arguments[2] || true } }); this._blocked = true; return; } return this.originals.xhrOpen.call(this, method, url); }, handleHTMLInsertion(html, insertionType, originalFunction) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; Array.from(tempDiv.querySelectorAll('script, iframe, img')).forEach(el => { const src = el.src || el.getAttribute('src') || ''; if (this.isThirdParty(src)) { Logs.add('thirdPartyBlock', el.cloneNode(), { type: '动态插入拦截', detail: `插入方式: ${insertionType}`, context: { originalHTML: html.slice(0, 200) + (html.length > 200 ? '...' : '') } }); this.handleInterception(el.cloneNode(), src); } }); return originalFunction(html); }, wrapDocumentWrite(content) { return this.handleHTMLInsertion(content, 'document.write', this.originals.documentWrite); }, wrapDocumentWriteln(content) { return this.handleHTMLInsertion(content, 'document.writeln', this.originals.documentWriteln); }, wrapInsertAdjacentHTML(position, html) { return this.handleHTMLInsertion(html, 'insertAdjacentHTML', (html) => this.originals.insertAdjacentHTML.call(this, position, html)); }, handleBeforeScriptExecute(e) { const script = e.target; const src = script.src || script.getAttribute('data-src') || ''; if (this.isThirdParty(src)) { e.preventDefault(); e.stopImmediatePropagation(); requestIdleCallback(() => { AdUtils.nuclearRemove(script, 'thirdPartyBlock', { type: '预执行脚本拦截', detail: `脚本类型: ${script.type || '常规脚本'}` }); }, { timeout: 50 }); } }, check(el) { if (!CONFIG.modules.thirdPartyBlock) return false; const src = el.src || el.getAttribute('data-src') || ''; if (this.isThirdParty(src)) { const tagName = el.tagName; if (tagName === 'SCRIPT') { return AdUtils.nuclearRemove(el, 'thirdPartyBlock', { type: '静态脚本拦截', detail: `源: ${src.slice(0, 80)}` }); } return this.blockElement(el); } return false; } }); ModuleManager.register('frameSystem', { check(el) { if (!CONFIG.modules.frameSystem) return false; if (el.tagName === 'IFRAME') { let depth = 0, parent = el; while ((parent = parent.parentElement) && depth <= CONFIG.protectionRules.maxFrameDepth) { if (parent.tagName === 'IFRAME') depth++; } if (depth > CONFIG.protectionRules.maxFrameDepth) { return AdUtils.safeRemove(el, 'frameSystem', { type: '深层嵌套框架', detail: `嵌套层级: ${depth}` }); } } return false; } }); ModuleManager.register('dynamicSystem', { checkedScripts: new WeakSet(), originalCreateElement: null, scriptProxies: new WeakMap(), abortControllers: new WeakMap(), patternCache: new Map(), dangerKeywords: ['adservice', 'tracking', 'popunder', 'adsystem'], throttleQueue: new Set(), observer: null, enabled: false, init() { this.originalCreateElement = document.createElement.bind(document); if (CONFIG.modules.dynamicSystem) { this.enable(); AdUtils.enableDynamicSystem(); } else { this.disable(); AdUtils.disableDynamicSystem(); } this.observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (node.tagName === 'SCRIPT') { this.throttleCheck(node); } else { this.checkStaticElements(node); } } }); }); }); if (this.enabled) { this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src'] }); } this.processQueue(); this.startRecoveryCheck(); }, enable() { this.enabled = true; document.createElement = this.wrapCreateElement.bind(this); if (this.observer) { this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src'] }); } AdUtils.enableDynamicSystem(); }, disable() { this.enabled = false; document.createElement = this.originalCreateElement; if (this.observer) { this.observer.disconnect(); } AdUtils.disableDynamicSystem(); }, checkObfuscatedAttributes(attrs) { const SHORT_VAR_PATTERN = /^[a-z\d]{1,3}$/i; const SIGNATURE_PATTERN = /^(?:[\da-f]{32}|[\w+/=]{40,})$/i; const ALPHANUM_ALTERNATE = /(?:[a-z]\d|\d[a-z]){5,}/gi; return attrs.some(attr => { const isShortVar = SHORT_VAR_PATTERN.test(attr.name); const isObfuscatedValue = SIGNATURE_PATTERN.test(attr.value) || attr.value.match(ALPHANUM_ALTERNATE)?.length > 3; const isDynamicName = /\[(?:0x[a-f\d]+|\d+)\]/.test(attr.name); return (isShortVar && isObfuscatedValue) || attr.name.length > 20 || isDynamicName || (attr.value.includes(';') && attr.value.length > 60); }); }, processQueue() { requestIdleCallback(() => { const startTime = performance.now(); const iterator = this.throttleQueue.values(); let entry = iterator.next(); while (!entry.done && performance.now() - startTime < 8) { const element = entry.value; if (element.tagName === 'SCRIPT') { this.checkScript(element); } else { this.checkStaticElements(element); } this.throttleQueue.delete(entry.value); entry = iterator.next(); } if (this.throttleQueue.size > 0) this.processQueue(); }, { timeout: 50 }); }, throttleCheck(element) { if (this.throttleQueue.has(element)) return; this.throttleQueue.add(element); if (this.throttleQueue.size === 1) this.processQueue(); }, checkStaticElements(el) { if (el.id && ( el.id.length > CONFIG.protectionRules.dynamicIdLength || REGEX.dynamicId.test(el.id) )) { AdUtils.safeRemove(el, 'dynamicSystem', { type: '可疑动态ID', detail: `ID特征: ${el.id.slice(0, 50)}` }); return true; } if (el.tagName === 'A') { const href = el.getAttribute('href') || ''; if (href.startsWith('javascript:') && REGEX.jsAdPattern.test(href)) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '可疑JS链接广告', detail: `执行代码: ${href.slice(0, 100)}`, regex: [REGEX.jsAdPattern.source] }); } } return false; }, wrapCreateElement(tagName) { const element = this.originalCreateElement.call(document, tagName); if (this.enabled && tagName.toLowerCase() === 'script') { return this.createScriptProxy(element); } return element; }, createScriptProxy(element) { const proxy = new Proxy(element, { set: (target, prop, value) => { if (prop === 'src') { if (this.quickDetect(target)) { this.blockImmediately(target); return true; } } target[prop] = value; return true; }, get: (target, prop) => { if (prop === 'src' && target.hasAttribute('data-ad-blocked')) { return ''; } return target[prop]; } }); this.scriptProxies.set(element, proxy); return proxy; }, quickDetect(scriptEl) { if (!this.enabled) return false; const attrs = Array.from(scriptEl.attributes); if (this.checkObfuscatedAttributes(attrs)) { Logs.add('dynamicSystem', scriptEl, { type: '混淆属性检测', detail: `属性列表: ${attrs.map(a => a.name).join(', ')}` }); return true; } if (!scriptEl.src && scriptEl.textContent) { const content = scriptEl.textContent.slice(0, 200); return this.dangerKeywords.some(k => content.includes(k)) || REGEX.obfuscationPatterns.some(p => p.pattern.test(content)); } return false; }, checkScript(scriptEl) { if (!this.enabled || this.checkedScripts.has(scriptEl)) return; this.checkedScripts.add(scriptEl); if (this.quickDetect(scriptEl)) { this.blockImmediately(scriptEl); return; } if (scriptEl.src) { this.analyzeRemoteScript(scriptEl); } else { this.analyzeInlineScript(scriptEl); } }, async analyzeRemoteScript(scriptEl) { const controller = new AbortController(); this.abortControllers.set(scriptEl, controller); try { const response = await fetch(scriptEl.src, { signal: controller.signal, priority: 'low' }); const reader = response.body.getReader(); let chunk = '', isSafe = true; while (true) { const { done, value } = await reader.read(); if (done) break; chunk += new TextDecoder().decode(value); if (this.detectInChunk(chunk)) { isSafe = false; reader.cancel(); break; } if (chunk.length > 4096) break; } if (!isSafe) { this.blockImmediately(scriptEl); Logs.add('dynamicSystem', scriptEl, { type: '流式特征阻断', detail: `检测到前4KB风险内容` }); } } catch (e) { if (e.name !== 'AbortError') console.warn('脚本分析失败:', e); } finally { this.abortControllers.delete(scriptEl); } }, detectInChunk(chunk) { return REGEX.obfuscationPatterns.some(({ pattern }) => pattern.test(chunk)); }, analyzeInlineScript(scriptEl) { const content = scriptEl.textContent; const dynamicVarPatterns = [ /(?:var|let|const)\s+([a-z\d]{1,4})\s*=\s*(?:"[\\x0-9a-f]{20,}"|[\d+/*\-()]{8,})/gi, /\.(?:setAttribute|style)\s*\(\s*["']\w{1,4}["']/gi ]; if (dynamicVarPatterns.some(p => p.test(content))) { this.blockImmediately(scriptEl); Logs.add('dynamicSystem', scriptEl, { type: '动态变量赋值', detail: `检测到可疑的变量初始化`, regex: dynamicVarPatterns.map(p => p.source) }); } let dangerLevel = 0; dangerLevel += content.includes('eval(') ? 2 : 0; dangerLevel += content.includes('Function(') ? 1 : 0; dangerLevel += this.detectInChunk(content.slice(0, 500)) ? 3 : 0; if (dangerLevel >= 3) { this.blockImmediately(scriptEl); Logs.add('dynamicSystem', scriptEl, { type: '内联脚本风险', detail: `危险级别: ${dangerLevel}` }); } if (ASTAnalyzer.detectMalicious(content)) { this.blockImmediately(scriptEl); Logs.add('dynamicSystem', scriptEl, { type: 'AST检测阻断', detail: `可疑代码: ${content.slice(0, 100)}...` }); } }, blockImmediately(scriptEl) { if (!scriptEl.parentNode) return; scriptEl.setAttribute('data-ad-blocked', 'true'); scriptEl.style.setProperty('display', 'none', 'important'); requestIdleCallback(() => { try { scriptEl.parentNode.removeChild(scriptEl); } catch(e) { console.warn('移除失败:', e); } }, { timeout: 200 }); }, check(el) { if (!this.enabled) return false; const attrs = Array.from(el.attributes); if (this.checkObfuscatedAttributes(attrs)) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '混淆属性检测', detail: `属性列表: ${attrs.map(a => a.name).join(', ')}` }); } if (el.tagName === 'SCRIPT') { this.throttleCheck(el); return true; } return false; }, startRecoveryCheck() { setInterval(() => { document.querySelectorAll('script:not([data-ad-checked])').forEach(script => { this.throttleCheck(script); }); }, 500); } }); ModuleManager.register('layoutSystem', { check(el) { if (!CONFIG.modules.layoutSystem) return false; const style = Detector.getCachedStyle(el); const zIndex = parseInt(style.zIndex, 10); if (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '高Z轴元素', detail: `z-index: ${zIndex}` }); } if (style.overflow === 'hidden' && el.scrollHeight > el.clientHeight) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '隐藏溢出内容的容器' }); } if (style.visibility === 'hidden' || style.display === 'none') { return AdUtils.safeRemove(el, 'layoutSystem', { type: '不可见容器' }); } if (el.children.length === 0 && el.textContent.trim() === '' && style.width === '0px' && style.height === '0px') { return AdUtils.safeRemove(el, 'layoutSystem', { type: '空的不可见容器' }); } if (el.children.length > 0 && Array.from(el.children).every(child => child.style.display === 'none' || child.style.visibility === 'hidden')) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '子元素的不可见容器' }); } return false; } }); ModuleManager.register('mediaSystem', { check(el) { if (!CONFIG.modules.mediaSystem) return false; if (el.tagName === 'IMG' && (el.src.includes('ad') || el.src.match(/\.(gif|webp)(\?|$)/))) { return AdUtils.safeRemove(el, 'mediaSystem', { type: '图片广告', detail: `图片源: ${el.src.slice(0, 50)}` }); } const style = Detector.getCachedStyle(el); const rect = el.getBoundingClientRect(); if (['fixed', 'sticky'].includes(style.position)) { const isTopBanner = rect.top < 50; const isBottomBanner = rect.bottom > window.innerHeight - 50; if (isTopBanner || isBottomBanner) { return AdUtils.safeRemove(el, 'mediaSystem', { type: '浮动广告', detail: `位置: ${rect.top}px` }); } } return false; } }); ModuleManager.register('floating', { check(el) { if (!CONFIG.modules.floating) return false; const style = Detector.getCachedStyle(el); if (style.position === 'fixed' || style.position === 'sticky') { return AdUtils.safeRemove(el, 'floating', { type: '浮动元素检测', detail: `Position: ${style.position}` }); } return false; } }); ModuleManager.register('specialUA', { navigatorProxy: null, originalNavigator: Object.create(navigator), fakeUA: 'NokiaE7-00/5.0 UCWEB/2.0 Mozilla/5.0 (Symbian/3; Series60/5.2; Windows Phone 10.0; Android 14; Microsoft; Lumia 950 XL Dual SIM; Java) Gecko/131 Firefox/131 SearchCraft/3.10.2 (Baidu; P1 13) baiduboxapp/4.3.0.10', fakePlatform: 'Win32', fakeAppVersion: 'Mozilla/5.0 (Linux; Android 12; LIO-AN00 Build/HUAWEILIO-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36', init() { this.createProxy(); if (CONFIG.modules.specialUA) this.enable(); }, createProxy() { this.navigatorProxy = new Proxy(navigator, { get: (target, prop) => { if (!CONFIG.modules.specialUA) return Reflect.get(target, prop); switch(prop) { case 'userAgent': return this.fakeUA; case 'platform': return this.fakePlatform; case 'appVersion': return this.fakeAppVersion; case 'vendor': return 'Google Inc.'; default: return Reflect.get(target, prop); } } }); Object.defineProperty(window, 'navigator', { value: this.navigatorProxy, configurable: true, writable: false }); }, enable() { CONFIG.modules.specialUA = true; }, disable() { CONFIG.modules.specialUA = false; Object.defineProperty(window, 'navigator', { value: this.originalNavigator, configurable: true, writable: false }); }, check() { return false; } }); ModuleManager.register('performanceOptimizer', { mutationQueue: new Set(), pendingFrames: 0, lastProcessTime: 0, init() { this.optimizeMutationObserver(); this.setupPerformanceMonitor(); }, optimizeMutationObserver() { const originalObserve = MutationObserver.prototype.observe; MutationObserver.prototype.observe = function(target, options) { const optimizedOptions = { childList: options.childList, attributes: options.attributes, subtree: options.subtree && !options.subtreeOptimized, attributeFilter: ['id', 'class', 'data-ad', 'src'] }; originalObserve.call(this, target, optimizedOptions); }; }, setupPerformanceMonitor() { const checkLongTasks = () => { if (performance.now() - this.lastProcessTime > CONFIG.performance.longTaskThreshold) { perf.longTaskCount++; if (perf.longTaskCount >= CONFIG.performance.degradeThreshold) { this.activateDegradedMode(); } } requestAnimationFrame(checkLongTasks); }; checkLongTasks(); }, activateDegradedMode() { perf.degraded = true; observer.disconnect(); document.removeEventListener('DOMNodeInserted', captureInsertion); console.warn('进入性能降级模式,停止动态监测'); }, scheduleFrame(callback) { if (this.pendingFrames < 2) { this.pendingFrames++; requestAnimationFrame(() => { callback(); this.pendingFrames--; }); } else { setTimeout(callback, 16); } } }); const captureInsertion = (function() { const handler = function(e) { if (perf.degraded) return; const element = e.target; if (!perf.processed.has(element)) { perf.adElements.add(element); scheduleProcess(() => Processor.processElements()); } }; document.addEventListener('DOMNodeInserted', handler, true); return handler; })(); (function ensureDOMInjection() { const originalCreateElement = document.createElement; document.createElement = function(tagName) { const element = originalCreateElement.call(document, tagName); if (tagName.toLowerCase() === 'meta' && !document.head) { document.documentElement.insertBefore(element, document.documentElement.firstChild); } return element; }; const originalAppendChild = Node.prototype.appendChild; Node.prototype.appendChild = function(node) { if (node.nodeType === 1 && node.tagName === 'STYLE' && !document.head) { return document.documentElement.insertBefore(node, document.documentElement.firstChild); } return originalAppendChild.call(this, node); }; })(); window.addEventListener('load', () => { if (!perf.degraded) { Processor.collectAds(); ModuleManager.modules.performanceOptimizer.scheduleFrame(() => { Processor.processElements(); }); } }); init(); })();