Anti Anti-debugger

Stops most anti-debugging implementations by JavaScript obfuscators and stops the console logs from being automatically cleared.

目前為 2024-10-07 提交的版本,檢視 最新版本

// ==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');
    })();