Greasy Fork is available in English.

DevTools Bypass

Bypass for website restrictions on DevTools with improved protection

// ==UserScript==
// @name         DevTools Bypass
// @name:vi      Bỏ Qua Chặn DevTools
// @name:zh-CN   开发工具限制绕过
// @namespace    https://greasyfork.org/vi/users/1195312-renji-yuusei
// @version      3.0
// @description  Bypass for website restrictions on DevTools with improved protection
// @description:vi Bỏ qua các hạn chế của trang web về DevTools với bảo vệ được cải tiến
// @description:zh-CN 绕过网站对开发工具的限制,具有增强的保护功能
// @author       Yuusei
// @match        *://*/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      GPL-3.0-only
// ==/UserScript==

(function() {
    'use strict';

    // Add initialization logging
    console.log('[DevTools Bypass] Script initialized at:', new Date().toISOString());

    const config = {
        debugPatterns: {
            basic: /;\s*(?:debugger|debug(?:ger)?|breakpoint)\s*;?/g,
            advanced: /(?:debugger|debug(?:ger)?|breakpoint)[\s;]*(?:\{[\s\S]*?\})?/g,
            timing: /performance\.now\(\)|Date\.now\(\)/g,
            eval: /eval\(.*?debugger.*?\)/g
        },
        consoleProps: ['log', 'warn', 'error', 'info', 'debug', 'trace'],
        cutoffs: {
            debugger: { amount: 50, within: 60000 },
            debuggerThrow: { amount: 50, within: 60000 }
        },
        bypassTriggers: {
            timeThreshold: 100,
            stackDepth: 50,
            recursionLimit: 100
        },
        logging: {
            enabled: false,
            prefix: '[DevTools Bypass]',
            levels: ['info', 'warn', 'error']
        }
    };

    // Enhanced logging utility
    const logger = {
        info: (...args) => {
            if (config.logging.enabled) {
                console.log(config.logging.prefix, '(INFO)', ...args);
            }
        },
        warn: (...args) => {
            if (config.logging.enabled) {
                console.warn(config.logging.prefix, '(WARN)', ...args);
            }
        },
        error: (...args) => {
            if (config.logging.enabled) {
                console.error(config.logging.prefix, '(ERROR)', ...args);
            }
        }
    };

    const originals = {
        defineProperty: Object.defineProperty,
        getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor,
        setTimeout: window.setTimeout,
        setInterval: window.setInterval,
        Date: window.Date,
        now: Date.now,
        performance: window.performance,
        Function: window.Function,
        eval: window.eval,
        console: {},
        toString: Function.prototype.toString,
        preventDefault: Event.prototype.preventDefault,
        getComputedStyle: window.getComputedStyle
    };

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

    const isDebuggerPresent = () => {
        try {
            const startTime = originals.now.call(Date);
            const testFunc = new Function('debugger;')();
            const timeDiff = originals.now.call(Date) - startTime;
            if (timeDiff > config.bypassTriggers.timeThreshold) {
                logger.warn('Debugger detected! Time difference:', timeDiff, 'ms');
                return true;
            }
            return false;
        } catch (e) {
            logger.error('Error in debugger detection:', e);
            return false;
        }
    };

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

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

            return analysis;
        } catch (e) {
            logger.error('Error analyzing stack:', e);
            return { depth: 0, hasDebugKeywords: false, isRecursive: false };
        }
    };

    const enhancedAntiDebugger = () => {
        try {
            let protectionCount = 0;

            const createSafeTimer = (original) => {
                return function(handler, timeout, ...args) {
                    if (typeof handler === 'function') {
                        const wrappedHandler = function() {
                            try {
                                return handler.apply(this, arguments);
                            } catch (e) {
                                if (e.message?.includes('debugger')) {
                                    logger.info('Caught and bypassed debugger in timer');
                                    return undefined;
                                }
                                throw e;
                            }
                        };
                        return original.call(this, wrappedHandler, timeout, ...args);
                    }
                    return original.apply(this, arguments);
                };
            };

            const protectTiming = () => {
                const timeOffset = Math.random() * 10;
                const safeNow = function() {
                    return originals.now.call(Date) + timeOffset;
                };

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

                    if (window.performance && window.performance.now) {
                        Object.defineProperty(window.performance, 'now', {
                            value: safeNow,
                            configurable: true,
                            writable: true
                        });
                    }
                    protectionCount++;
                    logger.info('Timing protection applied with offset:', timeOffset);
                } catch (e) {
                    logger.error('Failed to protect timing:', e);
                }
            };

            const protectFunction = () => {
                const handler = {
                    apply(target, thisArg, args) {
                        const code = args[0];
                        if (typeof code === 'string') {
                            let cleanCode = code;
                            let detectedPatterns = [];
                            Object.entries(config.debugPatterns).forEach(([key, pattern]) => {
                                if (pattern.test(code)) {
                                    detectedPatterns.push(key);
                                }
                                cleanCode = cleanCode.replace(pattern, '');
                            });
                            
                            if (detectedPatterns.length > 0) {
                                logger.warn('Cleaned debug patterns from Function:', detectedPatterns);
                            }
                            
                            args[0] = cleanCode;
                        }
                        return Reflect.apply(target, thisArg, args);
                    },
                    construct(target, args) {
                        const code = args[0];
                        if (typeof code === 'string') {
                            let cleanCode = code;
                            Object.values(config.debugPatterns).forEach(pattern => {
                                cleanCode = cleanCode.replace(pattern, '');
                            });
                            args[0] = cleanCode;
                        }
                        return Reflect.construct(target, args);
                    }
                };

                window.Function = new Proxy(originals.Function, handler);
                if (typeof unsafeWindow !== 'undefined') {
                    unsafeWindow.Function = window.Function;
                }
                protectionCount++;
                logger.info('Function protection applied');
            };

            const protectStack = () => {
                try {
                    const errorHandler = {
                        get(target, prop) {
                            if (prop === 'stack') {
                                const stack = target.stack;
                                const cleanedStack = stack.split('\n')
                                    .filter(line => !Object.values(config.debugPatterns)
                                        .some(pattern => pattern.test(line)))
                                    .join('\n');
                                
                                if (cleanedStack.length !== stack.length) {
                                    logger.info('Cleaned suspicious stack trace');
                                }
                                
                                return cleanedStack;
                            }
                            return target[prop];
                        }
                    };

                    const errorProtoHandler = {
                        get(target, prop) {
                            if (prop === 'stack') {
                                const error = new Error();
                                return new Proxy(error, errorHandler).stack;
                            }
                            return Reflect.get(target, prop);
                        }
                    };

                    Error.prototype = new Proxy(Error.prototype, errorProtoHandler);
                    protectionCount++;
                    logger.info('Stack protection applied');
                } catch (e) {
                    logger.error('Failed to protect stack:', e);
                }
            };

            const protectEval = () => {
                const safeEval = function(code) {
                    if (typeof code === 'string') {
                        let cleanCode = code;
                        let detectedPatterns = [];
                        Object.entries(config.debugPatterns).forEach(([key, pattern]) => {
                            if (pattern.test(code)) {
                                detectedPatterns.push(key);
                            }
                            cleanCode = cleanCode.replace(pattern, '');
                        });
                        
                        if (detectedPatterns.length > 0) {
                            logger.warn('Cleaned debug patterns from eval:', detectedPatterns);
                        }
                        
                        return originals.eval.call(this, cleanCode);
                    }
                    return originals.eval.apply(this, arguments);
                };

                try {
                    Object.defineProperty(window, 'eval', {
                        value: safeEval,
                        configurable: true,
                        writable: true
                    });
                    if (typeof unsafeWindow !== 'undefined') {
                        unsafeWindow.eval = safeEval;
                    }
                    protectionCount++;
                    logger.info('Eval protection applied');
                } catch (e) {
                    logger.error('Failed to protect eval:', e);
                }
            };

            const protectConsole = () => {
                const handler = {
                    get(target, prop) {
                        if (config.consoleProps.includes(prop)) {
                            return function(...args) {
                                if (!isDebuggerPresent()) {
                                    return originals.console[prop]?.apply(console, args);
                                }
                                logger.warn('Console method blocked due to debugger presence:', prop);
                                return undefined;
                            };
                        }
                        return target[prop];
                    },
                    set(target, prop, value) {
                        if (config.consoleProps.includes(prop)) {
                            logger.warn('Attempted to modify console method:', prop);
                            return true;
                        }
                        target[prop] = value;
                        return true;
                    }
                };

                window.console = new Proxy(console, handler);
                protectionCount++;
                logger.info('Console protection applied');
            };

            // Apply all protections
            window.setTimeout = createSafeTimer(originals.setTimeout);
            window.setInterval = createSafeTimer(originals.setInterval);
            protectTiming();
            protectFunction();
            protectStack();
            protectEval();
            protectConsole();

            // Enhanced MutationObserver with logging
            const observer = new MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach((node) => {
                            if (node.tagName === 'SCRIPT') {
                                const content = node.textContent;
                                let detectedPatterns = [];
                                
                                Object.entries(config.debugPatterns).forEach(([key, pattern]) => {
                                    if (pattern.test(content)) {
                                        detectedPatterns.push(key);
                                    }
                                });

                                if (detectedPatterns.length > 0) {
                                    logger.warn('Cleaned debug patterns from dynamic script:', detectedPatterns);
                                    node.textContent = content.replace(
                                        new RegExp(Object.values(config.debugPatterns)
                                            .map(p => p.source).join('|'), 'g'),
                                        ''
                                    );
                                }
                            }
                        });
                    }
                }
            });

            observer.observe(document, {
                childList: true,
                subtree: true
            });

            logger.info('All protections applied successfully. Total protections:', protectionCount);

        } catch (e) {
            logger.error('Critical error in enhancedAntiDebugger:', e);
        }
    };

    const init = () => {
        try {
            logger.info('Initializing DevTools bypass...');
            enhancedAntiDebugger();
            logger.info('DevTools bypass initialized successfully');
        } catch (e) {
            logger.error('Failed to initialize DevTools bypass:', e);
        }
    };

    // Add status check function
    window._checkDevToolsBypassStatus = () => {
        try {
            const status = {
                initialized: true,
                debuggerPresent: isDebuggerPresent(),
                stackAnalysis: analyzeStack(),
                timestamp: new Date().toISOString()
            };
            logger.info('Status check:', status);
            return status;
        } catch (e) {
            logger.error('Error checking status:', e);
            return { error: e.message };
        }
    };

    init();
})();