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