// ==UserScript==
// @name DevTools Bypass
// @name:vi Bỏ Qua Chặn DevTools
// @name:zh-CN 开发工具限制绕过
// @name:ru Разблокировка DevTools
// @namespace https://gf.zukizuki.org/vi/users/1195312-renji-yuusei
// @version 2025.06.10.1
// @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==
(() => {
'use strict';
// Constants
const CONSTANTS = {
PREFIX: '[DevTools Bypass]',
LOG_LEVELS: {
INFO: 'info',
WARN: 'warn',
ERROR: 'error',
DEBUG: 'debug'
},
TIME_THRESHOLDS: {
DEBUGGER: 80,
CACHE: 30000
},
CACHE_TTL: {
DEBUGGER_CHECK: 500
}
};
// Configuration
const config = {
antiDebugRegex: new RegExp([
// debugger, debug(), etc.
/[;\s]*(?:debugger|debug(?:ger)?|breakpoint)[\s;]*/,
// new Function('debugger')(), etc.
/(?:eval|Function|setTimeout|setInterval)\s*\(\s*['"`].*?debugger.*?['"`]\s*\)/,
// devtools checks
/(?:isDevTools?|devtools?|debugMode|debug_enabled)\s*[=:]\s*(?:true|1|!0|yes)/,
// console checks
/console\.(?:log|warn|error|info|debug|trace|dir|table)/,
// source map urls
/\/\/[#@]\s*source(?:Mapping)?URL\s*=.*/,
// Known anti-debug library patterns
/FuckDevTools|devtools-detector/
].map(r => r.source).join('|'), 'gi'),
consoleProps: ['log', 'warn', 'error', 'info', 'debug', 'trace', 'dir', 'dirxml', 'table', 'profile', 'group', 'groupEnd', 'time', 'timeEnd'],
cutoffs: {
debugger: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE },
debuggerThrow: { amount: 30, within: CONSTANTS.TIME_THRESHOLDS.CACHE }
},
bypassTriggers: {
timeThreshold: CONSTANTS.TIME_THRESHOLDS.DEBUGGER,
stackDepth: 30,
recursionLimit: 50
},
debuggerDetector: {
cacheTTL: CONSTANTS.CACHE_TTL.DEBUGGER_CHECK,
historyCleanupInterval: 60000 // 1 minute
},
logging: {
enabled: true,
prefix: CONSTANTS.PREFIX,
levels: Object.values(CONSTANTS.LOG_LEVELS),
detailedErrors: true,
monitorAPI: false,
monitorDOM: false
},
protection: {
preventDevToolsKeys: true,
hideStackTraces: true,
sanitizeErrors: true,
obfuscateTimers: true,
preventRightClick: true,
preventViewSource: true,
preventCopy: true,
preventPaste: true,
preventPrint: true,
preventSave: true
}
};
// Logger class
class Logger {
static #instance;
#lastLog = 0;
#logCount = 0;
#logBuffer = [];
constructor() {
if (Logger.#instance) {
return Logger.#instance;
}
Logger.#instance = this;
this.#setupBufferFlush();
}
#setupBufferFlush() {
setInterval(() => {
if (this.#logBuffer.length) {
this.#flushBuffer();
}
}, 1000);
}
#flushBuffer() {
this.#logBuffer.forEach(({level, args}) => {
console[level](config.logging.prefix, ...args);
});
this.#logBuffer = [];
}
#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;
this.#logBuffer.push({ level, 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); }
debug(...args) { this.#log(CONSTANTS.LOG_LEVELS.DEBUG, ...args); }
}
// Original functions store
const OriginalFunctions = {
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,
addEventListener: window.addEventListener,
removeEventListener: window.removeEventListener,
fetch: window.fetch,
XMLHttpRequest: window.XMLHttpRequest,
initConsole() {
config.consoleProps.forEach(prop => {
if (console[prop]) {
this.console[prop] = console[prop].bind(console);
}
});
}
};
OriginalFunctions.initConsole();
// Debugger detector
class DebuggerDetector {
static #detectionCache = new Map();
static #detectionHistory = [];
static #historyCleanupTimer = null;
static isPresent() {
try {
const cacheKey = 'debugger_check';
const cached = this.#detectionCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < config.debuggerDetector.cacheTTL) {
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()
});
this.#detectionHistory.push({
timestamp: Date.now(),
result,
timeDiff
});
// Keep history for 5 minutes
const fiveMinutesAgo = Date.now() - 300000;
this.#detectionHistory = this.#detectionHistory.filter(entry => entry.timestamp > fiveMinutesAgo);
return result;
} catch {
return false;
}
}
static analyzeStack() {
try {
const stack = new Error().stack;
if (!stack) return { depth: 0, hasDebugKeywords: false, isRecursive: false, suspiciousPatterns: [], stackHash: '' };
const frames = stack.split('\n');
const uniqueFrames = new Set(frames);
return {
depth: frames.length,
hasDebugKeywords: config.antiDebugRegex.test(stack),
isRecursive: uniqueFrames.size < frames.length,
suspiciousPatterns: this.#detectSuspiciousPatterns(stack),
stackHash: this.#generateStackHash(stack)
};
} catch {
return {
depth: 0,
hasDebugKeywords: false,
isRecursive: false,
suspiciousPatterns: [],
stackHash: ''
};
}
}
static #detectSuspiciousPatterns(stack) {
const patterns = [
/eval.*?\(/g,
/Function.*?\(/g,
/debugger/g,
/debug/g,
/DevTools/g,
/console\./g,
/chrome-extension/g
];
return patterns.filter(pattern => pattern.test(stack));
}
static #generateStackHash(stack) {
return Array.from(stack).reduce((hash, char) => {
hash = ((hash << 5) - hash) + char.charCodeAt(0);
return hash & hash;
}, 0).toString(36);
}
static getDetectionStats() {
const now = Date.now();
const recentDetections = this.#detectionHistory.filter(entry =>
entry.timestamp > now - 60000
);
return {
total: recentDetections.length,
positive: recentDetections.filter(entry => entry.result).length,
averageTime: recentDetections.reduce((acc, curr) =>
acc + curr.timeDiff, 0) / (recentDetections.length || 1)
};
}
static startHistoryCleanup() {
if (this.#historyCleanupTimer) return;
this.#historyCleanupTimer = setInterval(() => {
const fiveMinutesAgo = Date.now() - 300000;
this.#detectionHistory = this.#detectionHistory.filter(entry => entry.timestamp > fiveMinutesAgo);
}, config.debuggerDetector.historyCleanupInterval);
}
}
// Protection class
class Protection {
static #combinedPattern = null;
static applyAll() {
this.#patchGlobalDebugVariables();
this.#protectTimers();
this.#protectTiming();
this.#protectFunction();
this.#protectStack();
this.#protectEval();
this.#protectConsole();
this.#setupMutationObserver();
this.#protectDeveloperKeys();
this.#protectRightClick();
this.#protectNetwork();
this.#protectStorage();
this.#protectClipboard();
this.#protectPrinting();
this.#protectWebWorkers();
this.#applyDeveloperHelpers();
}
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()) return;
return handler.apply(this, arguments);
} catch (e) {
if (e.message?.includes('debugger')) return;
throw e;
}
};
if (config.protection.obfuscateTimers) {
timeout = Math.max(1, timeout + (Math.random() * 20 - 10));
}
return original.call(this, wrappedHandler, timeout, ...args);
};
};
window.setTimeout = wrapTimer(OriginalFunctions.setTimeout);
window.setInterval = wrapTimer(OriginalFunctions.setInterval);
}
static #protectTiming() {
const timeOffset = Math.random() * 25;
const safeNow = () => OriginalFunctions.now.call(Date) + timeOffset;
Object.defineProperty(Date, 'now', {
value: safeNow,
configurable: false,
writable: false
});
if (window.performance?.now) {
Object.defineProperty(window.performance, 'now', {
value: safeNow,
configurable: false,
writable: false
});
}
}
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;
// V8-specific API for stack trace customization
if ('prepareStackTrace' in Error) {
Error.prepareStackTrace = (error, stack) => {
const stackString = [error.toString(), ...stack.map(frame => ` at ${frame}`)].join('\n');
return Protection.#cleanCode(stackString);
};
return;
}
// Standard-based approach
try {
const originalStackDescriptor = Object.getOwnPropertyDescriptor(Error.prototype, 'stack');
if (originalStackDescriptor?.get) {
Object.defineProperty(Error.prototype, 'stack', {
get() {
const originalStack = originalStackDescriptor.get.call(this);
return Protection.#cleanCode(originalStack);
},
configurable: true,
});
}
} catch (e) {
logger.error('Failed to protect stack traces:', e);
}
}
static #protectEval() {
const safeEval = function(code) {
if (typeof code === 'string') {
if (DebuggerDetector.isPresent()) return;
return OriginalFunctions.eval.call(this, Protection.#cleanCode(code));
}
return OriginalFunctions.eval.apply(this, arguments);
};
Object.defineProperty(window, 'eval', {
value: safeEval,
configurable: false,
writable: false
});
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()) return;
return OriginalFunctions.console[prop]?.apply(console, args);
};
},
set(target, prop, value) {
if (config.consoleProps.includes(prop)) return true;
target[prop] = value;
return true;
}
};
window.console = new Proxy(console, consoleHandler);
}
static #setupMutationObserver() {
new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// Clean script content
if (node.tagName === 'SCRIPT') {
const originalContent = node.textContent;
const cleanedContent = Protection.#cleanCode(originalContent);
if (originalContent !== cleanedContent) {
node.textContent = cleanedContent;
}
}
// Clean attributes
for (const attr of node.attributes) {
if (attr.name.startsWith('on') && Protection.#cleanCode(attr.value) !== attr.value) {
node.removeAttribute(attr.name);
}
}
}
});
});
}).observe(document.documentElement, {
childList: true,
subtree: true,
});
}
static #protectDeveloperKeys() {
if (!config.protection.preventDevToolsKeys && !config.protection.preventViewSource) return;
const handler = e => {
const key = e.key.toUpperCase();
const ctrl = e.ctrlKey;
const shift = e.shiftKey;
const alt = e.altKey;
const isDevToolsKey =
key === 'F12' ||
(ctrl && shift && (key === 'I' || key === 'J' || key === 'C')) ||
(ctrl && key === 'U');
if (isDevToolsKey) {
e.preventDefault();
e.stopPropagation();
}
};
window.addEventListener('keydown', handler, true);
}
static #protectRightClick() {
if (!config.protection.preventRightClick) return;
window.addEventListener('contextmenu', e => {
e.preventDefault();
e.stopPropagation();
return false;
}, true);
}
static #protectNetwork() {
window.fetch = async function(...args) {
if (DebuggerDetector.isPresent()) {
throw new Error('Network request blocked');
}
return OriginalFunctions.fetch.apply(this, args);
};
window.XMLHttpRequest = function() {
const xhr = new OriginalFunctions.XMLHttpRequest();
const originalOpen = xhr.open;
xhr.open = function(...args) {
if (DebuggerDetector.isPresent()) {
throw new Error('Network request blocked');
}
return originalOpen.apply(xhr, args);
};
return xhr;
};
}
static #protectStorage() {
const storageHandler = {
get(target, prop) {
if (DebuggerDetector.isPresent()) return null;
return target[prop];
},
set(target, prop, value) {
if (DebuggerDetector.isPresent()) return true;
target[prop] = value;
return true;
}
};
window.localStorage = new Proxy(window.localStorage, storageHandler);
window.sessionStorage = new Proxy(window.sessionStorage, storageHandler);
}
static #protectClipboard() {
const events = [];
if (config.protection.preventCopy) events.push('copy', 'cut');
if (config.protection.preventPaste) events.push('paste');
if (events.length) {
events.forEach(eventName => {
document.addEventListener(eventName, e => {
e.preventDefault();
e.stopPropagation();
}, true);
});
}
}
static #protectPrinting() {
if (!config.protection.preventPrint) return;
window.addEventListener('beforeprint', e => {
e.preventDefault();
}, true);
window.addEventListener('afterprint', e => {
e.preventDefault();
}, true);
}
static #protectWebWorkers() {
window.Worker = function(scriptURL, options) {
console.log('[Worker Created]', scriptURL);
return new OriginalFunctions.Worker(scriptURL, options);
};
}
static #applyDeveloperHelpers() {
if (config.logging.monitorAPI) {
this.#monitorAPICalls();
}
if (config.logging.monitorDOM) {
this.#monitorDOMEvents();
}
}
static #patchGlobalDebugVariables() {
const noop = () => {};
Object.defineProperties(window, {
'debug': { value: noop, configurable: false, writable: false },
'debugger': { value: noop, configurable: false, writable: false },
'isDebuggerEnabled': { value: false, configurable: false, writable: false }
});
}
static #monitorAPICalls() {
const originalFetch = window.fetch;
window.fetch = async (...args) => {
logger.debug('[API Call]', ...args);
return originalFetch.apply(this, args);
};
}
static #monitorDOMEvents() {
new MutationObserver(mutations => {
mutations.forEach(mutation => {
logger.debug('[DOM Change]', mutation);
});
}).observe(document.documentElement, {
childList: true,
subtree: true,
attributes: true,
characterData: true
});
}
static #cleanCode(code) {
if (typeof code !== 'string') return code;
return code.replace(config.antiDebugRegex, '');
}
}
// Main class
class DevToolsBypass {
static init() {
try {
DebuggerDetector.startHistoryCleanup();
Protection.applyAll();
logger.info('DevTools Bypass initialized successfully');
} catch (e) {
logger.error('Failed to initialize DevTools Bypass:', e);
}
}
}
// Initialize
DevToolsBypass.init();
})();