// ==UserScript== // @name 防切屏暂停视频 + 解除复制限制 + 解除F12拦截 // @namespace ckxgzxa // @version 1.0 // @description 拦截页面可见性检测,伪装网页始终处于前台;解除网页复制限制;解除F12及开发者工具拦截 // @author ckxgzxa // @match *://*.excnpc.com/* // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; // ==================== 核心:重写页面可见性相关属性 ==================== const overrideVisibilityProps = () => { Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'webkitVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'webkitHidden', { get: () => false, configurable: true }); }; const blockVisibilityEvents = () => { const eventNames = [ 'visibilitychange', 'webkitvisibilitychange', 'mozvisibilitychange', 'msvisibilitychange' ]; eventNames.forEach(eventName => { const originalAddListener = document.addEventListener; document.addEventListener = function(type, listener, options) { if (type === eventName) return; return originalAddListener.call(this, type, listener, options); }; const originalDispatch = document.dispatchEvent; document.dispatchEvent = function(event) { if (event.type === eventName) return true; return originalDispatch.call(this, event); }; }); }; const fakePageActive = () => { Object.defineProperty(document, 'hasFocus', { value: () => true, configurable: true }); const originalAddWinListener = window.addEventListener; window.addEventListener = function(type, listener, options) { if (type === 'blur' || type === 'focusout') return; return originalAddWinListener.call(this, type, listener, options); }; window.addEventListener('blur', () => { window.focus(); }, { once: false }); }; // ==================== 新增:解除复制限制 ==================== const unblockCopy = () => { // 1. 拦截并放行 copy / cut / contextmenu / selectstart / dragstart 事件 const blockedEvents = ['copy', 'cut', 'contextmenu', 'selectstart', 'dragstart', 'mousedown', 'mouseup']; blockedEvents.forEach(evtName => { window.addEventListener(evtName, (e) => { e.stopPropagation(); // 确保事件能继续传递到真正需要的地方 }, true); }); // 2. 重写 document.execCommand,放行 copy const originalExecCommand = document.execCommand; document.execCommand = function(command, showUI, value) { const cmd = command.toLowerCase(); if (cmd === 'copy' || cmd === 'cut') { // 尝试执行原生复制 try { return originalExecCommand.call(this, command, showUI, value); } catch (e) { return false; } } return originalExecCommand.call(this, command, showUI, value); }; // 3. 移除 body 及常见元素的 oncopy / oncut / oncontextmenu / onselectstart 等内联事件 const clearInlineHandlers = () => { const attrs = ['oncopy', 'oncut', 'oncontextmenu', 'onselectstart', 'ondragstart']; const allElements = document.querySelectorAll('*'); allElements.forEach(el => { attrs.forEach(attr => { if (el.hasAttribute(attr)) { el.removeAttribute(attr); } }); }); // 同时清理 body 和 document if (document.body) { attrs.forEach(attr => document.body.removeAttribute(attr)); } if (document.documentElement) { attrs.forEach(attr => document.documentElement.removeAttribute(attr)); } }; // 4. 注入 CSS 强制允许选择和复制 const style = document.createElement('style'); style.textContent = ` * { -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; -webkit-touch-callout: default !important; } `; (document.head || document.documentElement).appendChild(style); // 页面加载完成后清理内联事件 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', clearInlineHandlers); } else { clearInlineHandlers(); } // 定时清理,应对动态生成的内容 setInterval(clearInlineHandlers, 2000); }; // ==================== 新增:解除 F12 / 开发者工具拦截 ==================== const unblockDevTools = () => { // 1. 拦截 keydown,放行 F12、Ctrl+Shift+I/J、Ctrl+U 等 const blockedKeys = [ { key: 'F12', code: 'F12', keyCode: 123 }, { ctrlKey: true, shiftKey: true, key: 'I', code: 'KeyI', keyCode: 73 }, { ctrlKey: true, shiftKey: true, key: 'J', code: 'KeyJ', keyCode: 74 }, { ctrlKey: true, shiftKey: true, key: 'C', code: 'KeyC', keyCode: 67 }, { ctrlKey: true, key: 'U', code: 'KeyU', keyCode: 85 } ]; window.addEventListener('keydown', (e) => { for (const combo of blockedKeys) { let match = true; if (combo.key !== undefined && e.key !== combo.key) match = false; if (combo.code !== undefined && e.code !== combo.code) match = false; if (combo.keyCode !== undefined && e.keyCode !== combo.keyCode) match = false; if (combo.ctrlKey !== undefined && e.ctrlKey !== combo.ctrlKey) match = false; if (combo.shiftKey !== undefined && e.shiftKey !== combo.shiftKey) match = false; if (match) { e.stopPropagation(); return; } } }, true); // 2. 拦截 beforeunload(防止按 F12 时页面自动跳转或关闭) window.addEventListener('beforeunload', (e) => { // 阻止通过 beforeunload 干扰开发者工具 e.stopImmediatePropagation(); }, true); // 3. 屏蔽常见的 debugger 反调试(通过定时器覆盖) const originalSetInterval = window.setInterval; const originalSetTimeout = window.setTimeout; // 重写 setInterval,过滤掉只包含 debugger 的函数 window.setInterval = function(func, delay, ...args) { if (typeof func === 'function') { const funcStr = func.toString(); if (funcStr === 'function(){debugger}' || funcStr === '()=>{debugger}' || /^\s*function\s*\(\)\s*\{\s*debugger\s*;?\s*\}\s*$/.test(funcStr)) { return -1; // 返回一个假的 timer ID } } return originalSetInterval.call(this, func, delay, ...args); }; window.setTimeout = function(func, delay, ...args) { if (typeof func === 'function') { const funcStr = func.toString(); if (funcStr === 'function(){debugger}' || funcStr === '()=>{debugger}' || /^\s*function\s*\(\)\s*\{\s*debugger\s*;?\s*\}\s*$/.test(funcStr)) { return -1; } } return originalSetTimeout.call(this, func, delay, ...args); }; // 4. 拦截 eval 中的 debugger(简单处理) const originalEval = window.eval; window.eval = function(code) { if (typeof code === 'string') { // 移除单独的 debugger 语句 code = code.replace(/\bdebugger\b\s*;?/g, ''); } return originalEval.call(this, code); }; // 5. 屏蔽通过 window.outerWidth - window.innerWidth 检测开发者工具 // 重写 console.log / console.warn 等(部分网站通过重写 console 检测) // 保留原生 console 功能,但阻止页面清空 console const originalClear = console.clear; console.clear = function() { // 可以选择性放行,这里直接屏蔽页面的自动 clear console.log('[UserScript] 页面尝试清空控制台,已拦截'); }; // 6. 拦截开发者工具打开检测(部分网站使用性能检测法) // 覆盖 performance.now 的异常检测 const originalNow = performance.now.bind(performance); // 正常放行,不做修改,避免影响页面功能 }; // ==================== 执行所有逻辑 ==================== overrideVisibilityProps(); blockVisibilityEvents(); fakePageActive(); unblockCopy(); unblockDevTools(); // 动态监测重置 setInterval(() => { overrideVisibilityProps(); fakePageActive(); }, 1000); })();