反反调试

阻止大多数 JavaScript 混淆器实现的反调试,并阻止控制台日志被自动清除。

当前为 2024-10-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Anti Anti-debugger
// @name:vi      Chống Anti-debugger
// @name:zh-CN   反反调试
// @namespace    https://greasyfork.org/vi/users/1195312-renji-yuusei
// @version      1.0
// @description  Stops most anti-debugging implementations by JavaScript obfuscators and stops the console logs from being automatically cleared.
// @description:vi Ngăn chặn hầu hết các triển khai anti-debugging bằng JavaScript obfuscators và ngăn chặn việc xóa nhật ký console tự động.
// @description:zh-CN  阻止大多数 JavaScript 混淆器实现的反调试,并阻止控制台日志被自动清除。
// @author       Yuusei
// @match        *://*/*
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// @license      GPL-3.0-only
// ==/UserScript==

(function() {
    'use strict';

    const config = {
        excludedDomains: ['vidstream.pro', 'mcloud.to'],
        debugKeywords: ['debugger', 'debug', 'debugging', 'breakpoint'],
        consoleProps: ['log', 'warn', 'error', 'info', 'debug', 'assert', 'dir', 'dirxml', 'trace', 'group', 'groupCollapsed', 'groupEnd', 'time', 'timeEnd', 'profile', 'profileEnd', 'count'],
        enabled: true,  // Can be toggled at runtime
    };

    // Load user settings
    const loadSettings = () => {
        config.enabled = GM_getValue('antiAntiDebuggerEnabled', true);
        config.excludedDomains = GM_getValue('excludedDomains', config.excludedDomains);
    };

    loadSettings();

    const isExcludedDomain = () => config.excludedDomains.some(domain => 
        new RegExp(`(^|\.)${domain.replace(/\./g, '\\.')}$`, 'i').test(location.hostname)
    );

    if (isExcludedDomain()) return;

    const safeEval = (code, context = {}) => {
        const safeContext = { ...context, console: { log: () => {} } };
        const safeCode = `
            'use strict';
            const alert = () => {};
            const confirm = () => {};
            const prompt = () => {};
            ${Object.keys(safeContext).map(key => `const ${key} = this.${key};`).join('\n')}
            return (${code});
        `;
        try {
            return new Function(safeCode).call(safeContext);
        } catch (error) {
            console.warn('Failed to evaluate code:', error);
            return null;
        }
    };

    const modifyFunction = (func) => {
        if (typeof func !== 'function') return func;

        let funcStr = func.toString();
        if (config.debugKeywords.some(keyword => funcStr.includes(keyword))) {
            funcStr = funcStr.replace(new RegExp(`\\b(${config.debugKeywords.join('|')})\\b`, 'g'), '/* removed */');
            return safeEval(funcStr) || func;
        }
        return func;
    };

    const wrapConsole = () => {
        const originalConsole = { ...console };
        const consoleProxy = new Proxy(console, {
            get: (target, prop) => {
                if (config.consoleProps.includes(prop) && typeof target[prop] === 'function') {
                    return new Proxy(target[prop], {
                        apply: (targetFunc, thisArg, args) => {
                            if (!config.enabled) return Reflect.apply(targetFunc, thisArg, args);
                            args = args.map(modifyFunction);
                            return Reflect.apply(targetFunc, thisArg, args);
                        }
                    });
                }
                return Reflect.get(target, prop);
            },
            set: () => false // Prevent overwriting console properties
        });

        Object.defineProperty(unsafeWindow, 'console', {
            get: () => consoleProxy,
            set: () => false,
            configurable: false
        });
    };

    const antiAntiDebugger = () => {
        const proxyHandler = {
            apply: (target, thisArg, args) => {
                if (!config.enabled) return Reflect.apply(target, thisArg, args);
                args = args.map(modifyFunction);
                return Reflect.apply(target, thisArg, args);
            },
            construct: (target, args) => {
                if (!config.enabled) return Reflect.construct(target, args);
                args = args.map(modifyFunction);
                return Reflect.construct(target, args);
            }
        };

        Function.prototype.constructor = new Proxy(Function.prototype.constructor, proxyHandler);
        
        // Protect eval and Function constructor
        unsafeWindow.eval = new Proxy(unsafeWindow.eval, proxyHandler);
        unsafeWindow.Function = new Proxy(unsafeWindow.Function, proxyHandler);
    };

    const preventDebugging = () => {
        const noop = () => {};
        const protectedProps = [...config.debugKeywords, 'pause', 'alert', 'confirm', 'prompt'];
        
        protectedProps.forEach(prop => {
            Object.defineProperty(unsafeWindow, prop, {
                get: () => config.enabled ? noop : unsafeWindow[prop],
                set: () => {},
                configurable: false
            });
        });

        // Override common debugging-related Date methods
        const originalDate = unsafeWindow.Date;
        unsafeWindow.Date = new Proxy(originalDate, {
            construct: (target, args) => {
                const date = new target(...args);
                date.getTime = new Proxy(date.getTime, {
                    apply: (target, thisArg, args) => {
                        if (config.enabled && new Error().stack.includes('debugger')) {
                            return 0; // Return a fake timestamp
                        }
                        return Reflect.apply(target, thisArg, args);
                    }
                });
                return date;
            }
        });
    };

    const setupToggleShortcut = () => {
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey && e.key === 'D') {
                config.enabled = !config.enabled;
                GM_setValue('antiAntiDebuggerEnabled', config.enabled);
                console.log(`Anti Anti-debugger ${config.enabled ? 'enabled' : 'disabled'}`);
            }
        });
    };

    wrapConsole();
    antiAntiDebugger();
    preventDebugging();
    setupToggleShortcut();

    console.log('Anti Anti-debugger is active');
    })();