DisableDevtool万能拦截器 - 增强版

全方位拦截disable-devtool,支持多种加载方式,可拖拽悬浮控制面板

// ==UserScript==
// @name         DisableDevtool万能拦截器 - 增强版
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  全方位拦截disable-devtool,支持多种加载方式,可拖拽悬浮控制面板
// @author       MissChina
// @match        *://*/*
// @run-at       document-start
// @grant        none
// @icon         https://github.com/MissChina/anti-disable-devtool/raw/main/icon.png
// ==/UserScript==

(function() {
    'use strict';

    // 扩展的检测关键字(根据源码分析得出的特征)
    const TARGET_PATTERNS = [
        // 文件名模式
        'disable-devtool',
        'anti-debug',
        'devtool-disable',
        'security',
        'protect',

        // 域名模式(常见CDN)
        'cdn.jsdelivr.net',
        'unpkg.com',
        'cdnjs.cloudflare.com',

        // 你遇到的具体案例
        'vf.uujjyp.cn',
        'frameworks'
    ];

    // 代码特征检测(检测内联脚本)
    const CODE_SIGNATURES = [
        'DisableDevtool',           // 核心对象名
        'ondevtoolopen',           // 特征方法名
        'detectors',               // 配置属性
        'RegToString',             // 检测器类型
        'FuncToString',            // 检测器类型
        'clearIntervalWhenDevOpenTrigger', // 特有配置项
    ];

    let interceptCount = 0;
    let statusDiv = null;
    let isExpanded = false;
    let isDragging = false;
    let dragStartX = 0;
    let dragStartY = 0;
    let panelStartX = 0;
    let panelStartY = 0;
    let isMinimized = false;

    // 初始化启动信息
    console.log('%c🛡️ DisableDevtool万能拦截器 - 增强版 v3.1', 'color: #10B981; font-weight: bold; font-size: 14px;');
    console.log('%c👨‍💻 作者: MissChina (GitHub)', 'color: #6B7280; font-size: 12px;');
    console.log('%c🔗 项目地址: https://github.com/MissChina/anti-disable-devtool', 'color: #6B7280; font-size: 12px;');
    console.log('%c⚠️  仅供个人非盈利使用,禁止商用', 'color: #F59E0B; font-size: 12px;');

    // 检查是否为目标脚本
    function isTargetScript(url, content = '') {
        if (!url && !content) return false;

        // 检查URL
        if (url) {
            const urlLower = url.toLowerCase();
            if (TARGET_PATTERNS.some(pattern => urlLower.includes(pattern.toLowerCase()))) {
                return true;
            }
        }

        // 检查代码内容特征
        if (content) {
            const codeSignatureCount = CODE_SIGNATURES.filter(sig =>
                content.includes(sig)
            ).length;

            // 如果包含3个或以上特征,判定为目标脚本
            return codeSignatureCount >= 3;
        }

        return false;
    }

    // 创建可拖拽的状态面板
    function createStatusPanel() {
        if (!document.body) {
            setTimeout(createStatusPanel, 100);
            return;
        }

        statusDiv = document.createElement('div');
        statusDiv.style.cssText = `
            position: fixed;
            top: 15px;
            right: 15px;
            background: rgba(16, 185, 129, 0.9);
            color: white;
            padding: 8px 12px;
            border-radius: 20px;
            font-family: 'Segoe UI', sans-serif;
            font-size: 12px;
            z-index: 999999;
            box-shadow: 0 4px 20px rgba(0,0,0,0.25);
            border: 1px solid rgba(255,255,255,0.2);
            cursor: move;
            transition: all 0.3s ease;
            min-width: 120px;
            text-align: center;
            user-select: none;
            backdrop-filter: blur(10px);
        `;

        // 添加作者信息水印
        const authorInfo = document.createElement('div');
        authorInfo.style.cssText = `
            position: absolute;
            bottom: -20px;
            right: 0;
            font-size: 8px;
            color: rgba(255,255,255,0.6);
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.3s ease;
        `;
        authorInfo.textContent = 'by MissChina';
        statusDiv.appendChild(authorInfo);

        updateStatusPanel();
        document.body.appendChild(statusDiv);

        // 创建控制按钮容器
        const controlButtons = document.createElement('div');
        controlButtons.style.cssText = `
            position: absolute;
            top: -8px;
            right: -8px;
            display: none;
            gap: 4px;
        `;

        // 最小化按钮
        const minimizeBtn = document.createElement('div');
        minimizeBtn.innerHTML = '−';
        minimizeBtn.style.cssText = `
            width: 16px;
            height: 16px;
            background: rgba(255, 193, 7, 0.9);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 10px;
            font-weight: bold;
            color: white;
        `;
        minimizeBtn.onclick = (e) => {
            e.stopPropagation();
            toggleMinimize();
        };

        // 关闭按钮
        const closeBtn = document.createElement('div');
        closeBtn.innerHTML = '×';
        closeBtn.style.cssText = `
            width: 16px;
            height: 16px;
            background: rgba(220, 38, 38, 0.9);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 12px;
            font-weight: bold;
            color: white;
        `;
        closeBtn.onclick = (e) => {
            e.stopPropagation();
            hidePanel();
        };

        controlButtons.appendChild(minimizeBtn);
        controlButtons.appendChild(closeBtn);
        statusDiv.appendChild(controlButtons);

        // 鼠标事件处理
        statusDiv.addEventListener('mousedown', startDrag);
        statusDiv.addEventListener('click', handleClick);
        statusDiv.addEventListener('mouseenter', showControls);
        statusDiv.addEventListener('mouseleave', hideControls);

        // 全局事件监听
        document.addEventListener('mousemove', handleDrag);
        document.addEventListener('mouseup', endDrag);

        // 悬停效果显示作者信息
        statusDiv.addEventListener('mouseenter', () => {
            if (!isDragging) {
                statusDiv.style.background = 'rgba(16, 185, 129, 1)';
                statusDiv.style.transform = 'scale(1.05)';
                authorInfo.style.opacity = '1';
            }
        });

        statusDiv.addEventListener('mouseleave', () => {
            if (!isDragging) {
                statusDiv.style.background = 'rgba(16, 185, 129, 0.9)';
                statusDiv.style.transform = 'scale(1)';
                authorInfo.style.opacity = '0';
            }
        });

        // 5秒后自动半透明,减少干扰
        setTimeout(() => {
            if (statusDiv && !isExpanded && !isMinimized) {
                statusDiv.style.opacity = '0.6';
            }
        }, 5000);

        function startDrag(e) {
            if (e.target === minimizeBtn || e.target === closeBtn) return;

            isDragging = true;
            dragStartX = e.clientX;
            dragStartY = e.clientY;

            const rect = statusDiv.getBoundingClientRect();
            panelStartX = rect.left;
            panelStartY = rect.top;

            statusDiv.style.cursor = 'grabbing';
            statusDiv.style.transition = 'none';
            e.preventDefault();
        }

        function handleDrag(e) {
            if (!isDragging) return;

            const deltaX = e.clientX - dragStartX;
            const deltaY = e.clientY - dragStartY;

            let newX = panelStartX + deltaX;
            let newY = panelStartY + deltaY;

            // 边界检测
            const maxX = window.innerWidth - statusDiv.offsetWidth;
            const maxY = window.innerHeight - statusDiv.offsetHeight;

            newX = Math.max(0, Math.min(newX, maxX));
            newY = Math.max(0, Math.min(newY, maxY));

            statusDiv.style.left = newX + 'px';
            statusDiv.style.top = newY + 'px';
            statusDiv.style.right = 'auto';
            statusDiv.style.bottom = 'auto';
        }

        function endDrag() {
            if (!isDragging) return;

            isDragging = false;
            statusDiv.style.cursor = 'move';
            statusDiv.style.transition = 'all 0.3s ease';
        }

        function handleClick(e) {
            if (e.target === minimizeBtn || e.target === closeBtn) return;
            if (isDragging) return;

            // 延迟执行,避免与拖拽冲突
            setTimeout(() => {
                if (!isDragging) {
                    togglePanel();
                }
            }, 100);
        }

        function showControls() {
            if (!isMinimized) {
                controlButtons.style.display = 'flex';
            }
        }

        function hideControls() {
            controlButtons.style.display = 'none';
        }

        function toggleMinimize() {
            isMinimized = !isMinimized;
            if (isMinimized) {
                statusDiv.innerHTML = '<div style="padding: 2px 6px;">🛡️</div>';
                statusDiv.style.minWidth = 'auto';
                statusDiv.style.width = '24px';
                statusDiv.style.height = '24px';
                statusDiv.style.borderRadius = '50%';
                controlButtons.style.display = 'none';
                // 重新添加控制按钮到最小化状态
                statusDiv.appendChild(controlButtons);
            } else {
                updateStatusPanel();
                statusDiv.style.width = 'auto';
                statusDiv.style.height = 'auto';
                statusDiv.style.borderRadius = '20px';
                statusDiv.appendChild(controlButtons);
            }
        }

        function hidePanel() {
            statusDiv.style.opacity = '0';
            statusDiv.style.transform = 'scale(0.5)';
            setTimeout(() => {
                if (statusDiv) {
                    statusDiv.style.display = 'none';
                }
            }, 300);

            // 10秒后重新显示
            setTimeout(() => {
                if (statusDiv) {
                    statusDiv.style.display = 'block';
                    statusDiv.style.opacity = '0.6';
                    statusDiv.style.transform = 'scale(1)';
                }
            }, 10000);
        }
    }

    // 切换面板状态
    function togglePanel() {
        isExpanded = !isExpanded;
        statusDiv.style.opacity = '1';
        updateStatusPanel();
    }

    // 更新状态显示
    function updateStatusPanel() {
        if (!statusDiv || isMinimized) return;

        if (!isExpanded) {
            const status = interceptCount > 0 ? '🛡️' : '👁️';
            const text = interceptCount > 0 ? `已拦截 ${interceptCount}` : '守护中';

            statusDiv.innerHTML = `
                <span>${status} ${text}</span>
                <div style="position: absolute; bottom: -20px; right: 0; font-size: 8px; color: rgba(255,255,255,0.6); pointer-events: none; opacity: 0; transition: opacity 0.3s ease;">by MissChina</div>
            `;
            statusDiv.style.padding = '6px 10px';

            // 重新添加控制按钮
            const existingControls = statusDiv.querySelector('[data-control-buttons]');
            if (!existingControls) {
                const controlButtons = createControlButtons();
                statusDiv.appendChild(controlButtons);
            }
            return;
        }

        // 详细模式
        const devToolsStatus = testDevTools() ?
            '<span style="color: #86efac;">✅ 控制台可用</span>' :
            '<span style="color: #fca5a5;">❌ 控制台被禁</span>';

        const interceptStatus = interceptCount > 0 ?
            `<span style="color: #86efac;">🛡️ 成功拦截 ${interceptCount}</span>` :
            '<span style="color: #fde68a;">👁️ 持续守护</span>';

        statusDiv.innerHTML = `
            <div style="font-weight: bold; margin-bottom: 4px;">
                🔒 万能拦截器
            </div>
            ${devToolsStatus}<br>
            ${interceptStatus}
            <div style="margin-top: 6px; font-size: 10px; color: rgba(255,255,255,0.8);">
                点击收缩 • 拖拽移动 • by MissChina
            </div>
            <div style="position: absolute; bottom: -20px; right: 0; font-size: 8px; color: rgba(255,255,255,0.6); pointer-events: none; opacity: 0; transition: opacity 0.3s ease;">by MissChina</div>
        `;
        statusDiv.style.padding = '10px 14px';

        // 重新添加控制按钮
        const controlButtons = createControlButtons();
        statusDiv.appendChild(controlButtons);
    }

    // 创建控制按钮
    function createControlButtons() {
        const controlButtons = document.createElement('div');
        controlButtons.setAttribute('data-control-buttons', 'true');
        controlButtons.style.cssText = `
            position: absolute;
            top: -8px;
            right: -8px;
            display: none;
            gap: 4px;
        `;

        // 最小化按钮
        const minimizeBtn = document.createElement('div');
        minimizeBtn.innerHTML = '−';
        minimizeBtn.style.cssText = `
            width: 16px;
            height: 16px;
            background: rgba(255, 193, 7, 0.9);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 10px;
            font-weight: bold;
            color: white;
        `;
        minimizeBtn.onclick = (e) => {
            e.stopPropagation();
            toggleMinimize();
        };

        // 关闭按钮
        const closeBtn = document.createElement('div');
        closeBtn.innerHTML = '×';
        closeBtn.style.cssText = `
            width: 16px;
            height: 16px;
            background: rgba(220, 38, 38, 0.9);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 12px;
            font-weight: bold;
            color: white;
        `;
        closeBtn.onclick = (e) => {
            e.stopPropagation();
            hidePanel();
        };

        controlButtons.appendChild(minimizeBtn);
        controlButtons.appendChild(closeBtn);

        return controlButtons;
    }

    // 最小化切换
    function toggleMinimize() {
        isMinimized = !isMinimized;
        if (isMinimized) {
            statusDiv.innerHTML = '<div style="padding: 2px 6px; cursor: pointer;">🛡️</div>';
            statusDiv.style.minWidth = 'auto';
            statusDiv.style.width = '24px';
            statusDiv.style.height = '24px';
            statusDiv.style.borderRadius = '50%';
            statusDiv.style.opacity = '0.8';
        } else {
            statusDiv.style.minWidth = '120px';
            statusDiv.style.width = 'auto';
            statusDiv.style.height = 'auto';
            statusDiv.style.borderRadius = '20px';
            statusDiv.style.opacity = '1';
            updateStatusPanel();
        }
    }

    // 隐藏面板
    function hidePanel() {
        statusDiv.style.opacity = '0';
        statusDiv.style.transform = 'scale(0.5)';
        setTimeout(() => {
            if (statusDiv) {
                statusDiv.style.display = 'none';
            }
        }, 300);

        // 10秒后重新显示为最小化状态
        setTimeout(() => {
            if (statusDiv) {
                statusDiv.style.display = 'block';
                statusDiv.style.opacity = '0.6';
                statusDiv.style.transform = 'scale(1)';
                isMinimized = true;
                toggleMinimize(); // 设为最小化状态
            }
        }, 10000);
    }

    // 测试开发者工具
    function testDevTools() {
        try {
            return typeof console !== 'undefined' && typeof console.log === 'function';
        } catch(e) {
            return false;
        }
    }

    // 拦截脚本核心函数
    function interceptScript(scriptElement, method) {
        const src = scriptElement.src || scriptElement.getAttribute('src') || '';
        const content = scriptElement.textContent || scriptElement.innerHTML || '';

        if (isTargetScript(src, content)) {
            interceptCount++;
            console.log(`🛡️ [万能拦截器] ${method}方式拦截成功:`, src || '内联脚本');
            updateStatusPanel();

            // 创建无害的替代脚本
            const dummyScript = document.createElement('script');
            dummyScript.textContent = `
                // DisableDevtool 已被万能拦截器安全移除
                console.log('🛡️ 检测到反调试脚本,已安全拦截');

                // 提供兼容性支持,防止页面报错
                window.DisableDevtool = function() {
                    return { success: false, reason: 'intercepted by universal blocker' };
                };
            `;
            return dummyScript;
        }
        return null;
    }

    // 劫持各种脚本加载方式
    const originalAppendChild = Element.prototype.appendChild;
    Element.prototype.appendChild = function(child) {
        if (child && child.tagName === 'SCRIPT') {
            const replacement = interceptScript(child, 'appendChild');
            if (replacement) {
                return originalAppendChild.call(this, replacement);
            }
        }
        return originalAppendChild.call(this, child);
    };

    const originalInsertBefore = Element.prototype.insertBefore;
    Element.prototype.insertBefore = function(newNode, referenceNode) {
        if (newNode && newNode.tagName === 'SCRIPT') {
            const replacement = interceptScript(newNode, 'insertBefore');
            if (replacement) {
                return originalInsertBefore.call(this, replacement, referenceNode);
            }
        }
        return originalInsertBefore.call(this, newNode, referenceNode);
    };

    const originalCreateElement = Document.prototype.createElement;
    Document.prototype.createElement = function(tagName) {
        const element = originalCreateElement.call(this, tagName);

        if (tagName && tagName.toLowerCase() === 'script') {
            let realSrc = '';

            Object.defineProperty(element, 'src', {
                get: function() { return realSrc; },
                set: function(value) {
                    if (value && isTargetScript(value)) {
                        interceptCount++;
                        console.log(`🛡️ [万能拦截器] createElement拦截:`, value);
                        updateStatusPanel();
                        return; // 阻止设置src
                    }
                    realSrc = value;
                    element.setAttribute('src', value);
                }
            });

            // 劫持textContent设置(拦截内联脚本)
            const originalTextContentSetter = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent').set;
            Object.defineProperty(element, 'textContent', {
                get: function() {
                    return this._textContent || '';
                },
                set: function(value) {
                    if (value && isTargetScript('', value)) {
                        interceptCount++;
                        console.log('🛡️ [万能拦截器] 内联脚本拦截成功');
                        updateStatusPanel();
                        this._textContent = '// 内联反调试脚本已被拦截';
                        return;
                    }
                    this._textContent = value;
                    originalTextContentSetter.call(this, value);
                }
            });
        }

        return element;
    };

    // 全局保护
    Object.defineProperty(window, 'DisableDevtool', {
        get: function() {
            console.log('🛡️ [万能拦截器] DisableDevtool对象访问被拦截');
            return function() {
                return { success: false, reason: 'blocked by universal interceptor' };
            };
        },
        set: function() {
            console.log('🛡️ [万能拦截器] 禁止设置DisableDevtool');
        }
    });

    // 初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createStatusPanel);
    } else {
        setTimeout(createStatusPanel, 100);
    }

    // 检查已存在脚本
    setTimeout(() => {
        document.querySelectorAll('script').forEach(script => {
            const src = script.src;
            const content = script.textContent || script.innerHTML;

            if (isTargetScript(src, content)) {
                console.log('🛡️ [万能拦截器] 发现并移除已存在脚本:', src || '内联脚本');
                if (script.parentNode) {
                    script.parentNode.removeChild(script);
                    interceptCount++;
                    updateStatusPanel();
                }
            }
        });
    }, 500);

    console.log('🛡️ DisableDevtool万能拦截器已启动 - 适配全网站');

})();