DevTools Bypass

Bypass for website restrictions on DevTools with enhanced protection

目前為 2024-12-05 提交的版本,檢視 最新版本

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         DevTools Bypass
// @name:vi      Bỏ Qua Chặn DevTools
// @name:zh-CN   开发工具限制绕过
// @name:ru       Разблокировка DevTools
// @namespace    https://greasyfork.org/vi/users/1195312-renji-yuusei
// @version      3.3
// @description  Bypass for website restrictions on DevTools with enhanced protection
// @description:vi Bỏ qua các hạn chế của trang web về DevTools với bảo vệ nâng cao
// @description:zh-CN 绕过网站对开发工具的限制,具有增强的保护功能
// @description:ru   Разблокировка DevTools с усиленной защитой
// @author       Yuusei
// @match        *://*/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      GPL-3.0-only
// ==/UserScript==

(function() {
    'use strict';

    const CONSTANTS = {
        PREFIX: '[DevTools Bypass]',
        LOG_LEVELS: {
            INFO: 'info',
            WARN: 'warn', 
            ERROR: 'error',
            DEBUG: 'debug'
        },
        TIME_THRESHOLDS: {
            DEBUGGER: 80,
            CACHE: 30000
        }
    };

    const config = {
        debugPatterns: {
            basic: /;\s*(?:debugger|debug(?:ger)?|breakpoint)\s*;?/gi,
            advanced: /(?:debugger|debug(?:ger)?|breakpoint)[\s;]*(?:\{[\s\S]*?\})?/gi,
            timing: /(?:performance|Date)\.now\(\)|new\s+Date(?:\(\))?\.getTime\(\)/gi,
            eval: /eval\(.*?(?:debugger|debug|breakpoint).*?\)/gi,
            devtools: /(?:isDevTools|devtools|debugMode|debug_mode)\s*[=:]\s*(?:true|1)/gi,
            consoleCheck: /console\.[a-zA-Z]+\s*\(.*?\)/gi,
            functionDebug: /function.*?\{[\s\S]*?debugger[\s\S]*?\}/gi,
            sourceMap: /\/\/[#@]\s*source(?:Mapping)?URL=.*?$/gm
        },
        consoleProps: ['log', 'warn', 'error', 'info', 'debug', 'trace', 'dir', 'dirxml', 'table', 'profile'],
        cutoffs: {
            debugger: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE },
            debuggerThrow: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE }
        },
        bypassTriggers: {
            timeThreshold: CONSTANTS.TIME_THRESHOLDS.DEBUGGER,
            stackDepth: 40,
            recursionLimit: 80
        },
        logging: {
            enabled: true,
            prefix: CONSTANTS.PREFIX,
            levels: Object.values(CONSTANTS.LOG_LEVELS),
            detailedErrors: true
        },
        protection: {
            preventDevToolsKeys: true,
            hideStackTraces: true,
            sanitizeErrors: true,
            obfuscateTimers: true
        }
    };

    class Logger {
        static #instance;
        #lastLog = 0;
        #logCount = 0;

        constructor() {
            if (Logger.#instance) {
                return Logger.#instance;
            }
            Logger.#instance = this;
        }

        #shouldLog() {
            const now = Date.now();
            if (now - this.#lastLog > 1000) {
                this.#logCount = 0;
            }
            this.#lastLog = now;
            return ++this.#logCount <= 10;
        }

        #log(level, ...args) {
            if (!config.logging.enabled || !this.#shouldLog()) return;
            const timestamp = new Date().toISOString();
            console[level](
                config.logging.prefix,
                `[${timestamp}]`,
                `(${level.toUpperCase()})`,
                ...args
            );
        }

        info(...args) {
            this.#log(CONSTANTS.LOG_LEVELS.INFO, ...args);
        }

        warn(...args) {
            this.#log(CONSTANTS.LOG_LEVELS.WARN, ...args);
        }

        error(...args) {
            this.#log(CONSTANTS.LOG_LEVELS.ERROR, ...args);
            if (config.logging.detailedErrors) {
                console.trace('Error stack trace:');
            }
        }

        debug(...args) {
            this.#log(CONSTANTS.LOG_LEVELS.DEBUG, ...args);
        }
    }

    const logger = new Logger();

    class OriginalFunctions {
        static defineProperty = Object.defineProperty;
        static getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
        static setTimeout = window.setTimeout;
        static setInterval = window.setInterval;
        static Date = window.Date;
        static now = Date.now;
        static performance = window.performance;
        static Function = window.Function;
        static eval = window.eval;
        static console = {};
        static toString = Function.prototype.toString;
        static preventDefault = Event.prototype.preventDefault;
        static getComputedStyle = window.getComputedStyle;
        static addEventListener = window.addEventListener;
        static removeEventListener = window.removeEventListener;

        static initConsole() {
            config.consoleProps.forEach(prop => {
                if (console[prop]) {
                    this.console[prop] = console[prop].bind(console);
                }
            });
        }
    }

    OriginalFunctions.initConsole();

    class DebuggerDetector {
        static #detectionCache = new Map();

        static isPresent() {
            try {
                const cacheKey = 'debugger_check';
                const cached = this.#detectionCache.get(cacheKey);
                if (cached && Date.now() - cached.timestamp < 1000) {
                    return cached.result;
                }

                const startTime = OriginalFunctions.now.call(Date);
                new Function('debugger;')();
                const timeDiff = OriginalFunctions.now.call(Date) - startTime;
                
                const result = timeDiff > config.bypassTriggers.timeThreshold;
                this.#detectionCache.set(cacheKey, {
                    result,
                    timestamp: Date.now()
                });

                if (result) {
                    logger.warn('Debugger detected:', timeDiff, 'ms');
                }
                return result;
            } catch (e) {
                logger.error('Debugger detection error:', e);
                return false;
            }
        }

        static analyzeStack() {
            try {
                const stack = new Error().stack;
                const frames = stack.split('\n');
                const uniqueFrames = new Set(frames);
                
                const analysis = {
                    depth: frames.length,
                    hasDebugKeywords: frames.some(frame => 
                        Object.values(config.debugPatterns).some(pattern => 
                            pattern.test(frame)
                        )
                    ),
                    isRecursive: uniqueFrames.size < frames.length,
                    suspiciousPatterns: this.#detectSuspiciousPatterns(stack)
                };

                if (analysis.hasDebugKeywords || analysis.isRecursive) {
                    logger.warn('Suspicious stack detected:', analysis);
                }

                return analysis;
            } catch (e) {
                logger.error('Stack analysis error:', e);
                return {
                    depth: 0,
                    hasDebugKeywords: false,
                    isRecursive: false,
                    suspiciousPatterns: []
                };
            }
        }

        static #detectSuspiciousPatterns(stack) {
            const patterns = [
                /eval.*?\(/g,
                /Function.*?\(/g,
                /debugger/g,
                /debug/g
            ];
            
            return patterns
                .map(pattern => pattern.test(stack))
                .filter(Boolean);
        }
    }

    class Protection {
        static applyAll() {
            this.#protectTimers();
            this.#protectTiming();
            this.#protectFunction();
            this.#protectStack();
            this.#protectEval();
            this.#protectConsole();
            this.#setupMutationObserver();
            this.#protectDevToolsKeys();
        }

        static #protectTimers() {
            const wrapTimer = (original) => {
                return function(handler, timeout, ...args) {
                    if (typeof handler !== 'function') return original.apply(this, arguments);
                    
                    const wrappedHandler = function() {
                        try {
                            if (DebuggerDetector.isPresent()) {
                                logger.warn('Timer execution blocked due to debugger');
                                return;
                            }
                            return handler.apply(this, arguments);
                        } catch (e) {
                            if (e.message?.includes('debugger')) {
                                logger.info('Timer debugger bypassed');
                                return;
                            }
                            throw e;
                        }
                    };

                    if (config.protection.obfuscateTimers) {
                        timeout = Math.max(1, timeout + Math.random() * 10 - 5);
                    }

                    return original.call(this, wrappedHandler, timeout, ...args);
                };
            };

            window.setTimeout = wrapTimer(OriginalFunctions.setTimeout);
            window.setInterval = wrapTimer(OriginalFunctions.setInterval);
        }

        static #protectTiming() {
            const timeOffset = Math.random() * 15;
            const safeNow = () => OriginalFunctions.now.call(Date) + timeOffset;

            Object.defineProperty(Date, 'now', {
                value: safeNow,
                configurable: true,
                writable: true
            });

            if (window.performance?.now) {
                Object.defineProperty(window.performance, 'now', {
                    value: safeNow,
                    configurable: true,
                    writable: true
                });
            }
        }

        static #protectFunction() {
            const handler = {
                apply(target, thisArg, args) {
                    if (typeof args[0] === 'string') {
                        args[0] = Protection.#cleanCode(args[0]);
                    }
                    return Reflect.apply(target, thisArg, args);
                },
                construct(target, args) {
                    if (typeof args[0] === 'string') {
                        args[0] = Protection.#cleanCode(args[0]);
                    }
                    return Reflect.construct(target, args);
                }
            };

            window.Function = new Proxy(OriginalFunctions.Function, handler);
            if (typeof unsafeWindow !== 'undefined') {
                unsafeWindow.Function = window.Function;
            }
        }

        static #protectStack() {
            if (!config.protection.hideStackTraces) return;

            const errorHandler = {
                get(target, prop) {
                    if (prop === 'stack') {
                        const stack = target.stack;
                        return Protection.#cleanCode(stack);
                    }
                    return target[prop];
                }
            };

            const originalErrorPrototype = Error.prototype;
            const proxyErrorPrototype = Object.create(originalErrorPrototype);

            Object.defineProperty(proxyErrorPrototype, 'stack', {
                get() {
                    const error = new Error();
                    return Protection.#cleanCode(error.stack);
                },
                configurable: true
            });

            try {
                Error.prototype = proxyErrorPrototype;
            } catch (e) {
                logger.warn('Failed to modify Error.prototype:', e);
            }
        }

        static #protectEval() {
            const safeEval = function(code) {
                if (typeof code === 'string') {
                    if (DebuggerDetector.isPresent()) {
                        logger.warn('Eval blocked due to debugger');
                        return;
                    }
                    return OriginalFunctions.eval.call(this, Protection.#cleanCode(code));
                }
                return OriginalFunctions.eval.apply(this, arguments);
            };

            Object.defineProperty(window, 'eval', {
                value: safeEval,
                configurable: true,
                writable: true
            });

            if (typeof unsafeWindow !== 'undefined') {
                unsafeWindow.eval = safeEval;
            }
        }

        static #protectConsole() {
            const consoleHandler = {
                get(target, prop) {
                    if (!config.consoleProps.includes(prop)) return target[prop];
                    
                    return function(...args) {
                        if (DebuggerDetector.isPresent()) {
                            logger.warn('Console blocked:', prop);
                            return;
                        }
                        return OriginalFunctions.console[prop]?.apply(console, args);
                    };
                },
                set(target, prop, value) {
                    if (config.consoleProps.includes(prop)) {
                        logger.warn('Console modification blocked:', prop);
                        return true;
                    }
                    target[prop] = value;
                    return true;
                }
            };

            window.console = new Proxy(console, consoleHandler);
        }

        static #setupMutationObserver() {
            new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(node => {
                            if (node.tagName === 'SCRIPT') {
                                const originalContent = node.textContent;
                                const cleanedContent = Protection.#cleanCode(originalContent);
                                if (originalContent !== cleanedContent) {
                                    node.textContent = cleanedContent;
                                    logger.info('Cleaned script content');
                                }
                            }
                        });
                    }
                });
            }).observe(document, {
                childList: true,
                subtree: true
            });
        }

        static #protectDevToolsKeys() {
            if (!config.protection.preventDevToolsKeys) return;

            const handler = (e) => {
                const { keyCode, ctrlKey, shiftKey } = e;
                if (
                    (keyCode === 123) || // F12
                    (ctrlKey && shiftKey && keyCode === 73) || // Ctrl+Shift+I
                    (ctrlKey && shiftKey && keyCode === 74) || // Ctrl+Shift+J
                    (ctrlKey && keyCode === 85) // Ctrl+U
                ) {
                    e.preventDefault();
                    logger.warn('DevTools hotkey blocked');
                }
            };

            window.addEventListener('keydown', handler, true);
        }

        static #cleanCode(code) {
            if (typeof code !== 'string') return code;
            
            let cleanCode = code;
            Object.entries(config.debugPatterns).forEach(([key, pattern]) => {
                if (pattern.test(code)) {
                    logger.warn(`Cleaned ${key} pattern`);
                    cleanCode = cleanCode.replace(pattern, '');
                }
            });
            return cleanCode;
        }
    }

    class DevToolsBypass {
        static init() {
            try {
                logger.info('Initializing DevTools Bypass...');
                Protection.applyAll();
                logger.info('Initialization complete');
            } catch (e) {
                logger.error('Initialization failed:', e);
            }
        }

        static checkStatus() {
            return {
                initialized: true,
                debuggerPresent: DebuggerDetector.isPresent(),
                stackAnalysis: DebuggerDetector.analyzeStack(),
                timestamp: new Date().toISOString(),
                version: '3.3'
            };
        }
    }

    window._checkDevToolsBypassStatus = DevToolsBypass.checkStatus;
    DevToolsBypass.init();
})();