Anti Anti-debugger

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

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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');
    })();