kills every known method of devtools detection and blocking. window size spoofing, debugger traps, toString getter traps, timing attacks, keyboard/contextmenu blocking, console method abuse, redirect interception, eval-based loops, and more.. anti anti-devtools
// ==UserScript==
// @name no anti-devtools
// @namespace https://minoa.cat/
// @version 1.1.1
// @description kills every known method of devtools detection and blocking. window size spoofing, debugger traps, toString getter traps, timing attacks, keyboard/contextmenu blocking, console method abuse, redirect interception, eval-based loops, and more.. anti anti-devtools
// @author minoa
// @license MIT
// @homepageURL https://greasyfork.org/scripts/no-anti-devtools
// @supportURL https://github.com/M2noa/no-anti-devtools/issues
// @match *://*/*
// @exclude *://*.google.com/*
// @exclude *://*.google.co.uk/*
// @exclude *://*.google.ca/*
// @exclude *://*.google.com.au/*
// @exclude *://*.googleapis.com/*
// @exclude *://*.gstatic.com/*
// @exclude *://*.youtube.com/*
// @exclude *://*.facebook.com/*
// @exclude *://*.instagram.com/*
// @exclude *://*.twitter.com/*
// @exclude *://*.x.com/*
// @exclude *://*.microsoft.com/*
// @exclude *://*.live.com/*
// @exclude *://*.outlook.com/*
// @exclude *://*.office.com/*
// @exclude *://*.apple.com/*
// @exclude *://*.icloud.com/*
// @exclude *://*.github.com/*
// @exclude *://*.gitlab.com/*
// @exclude *://*.paypal.com/*
// @exclude *://*.stripe.com/*
// @exclude *://*.amazon.com/*
// @exclude *://*.twitch.tv/*
// @exclude *://*.discord.com/*
// @exclude *://*.cloudflare.com/*
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
;(function (w) {
'use strict'
// --- 0. internal safe logger & native spoofing helper ---
const _rawConsole = {
log: w.console && w.console.log ? w.console.log.bind(w.console) : () => {},
warn: w.console && w.console.warn ? w.console.warn.bind(w.console) : () => {},
error: w.console && w.console.error ? w.console.error.bind(w.console) : () => {},
}
function reportError(section, err) {
_rawConsole.warn(`[no-anti-devtools] Issue in ${section}:`, err)
}
const NATIVE_FN_MAP = new WeakMap()
function makeNative(fn, name = '') {
try {
const str = `function ${name}() { [native code] }`
NATIVE_FN_MAP.set(fn, str)
fn.toString = () => str
} catch (e) {}
return fn
}
// --- domain blocklist (runtime safety net) ---
const BLOCKED_DOMAINS = [
// google
'google.com', 'google.co.uk', 'google.ca', 'google.com.au',
'googleapis.com', 'gstatic.com', 'accounts.google.com',
'youtube.com',
// meta
'facebook.com', 'instagram.com', 'threads.net',
// twitter / x
'twitter.com', 'x.com',
// microsoft
'microsoft.com', 'live.com', 'outlook.com', 'office.com',
'microsoftonline.com', 'azure.com',
// apple
'apple.com', 'icloud.com',
// dev platforms
'github.com', 'gitlab.com',
// payments
'paypal.com', 'stripe.com',
// shopping
'amazon.com',
// social / comms
'twitch.tv', 'discord.com',
// infra
'cloudflare.com',
]
function isBlockedDomain() {
try {
const host = w.location.hostname
return BLOCKED_DOMAINS.some(d => host === d || host.endsWith('.' + d))
} catch (e) {
reportError('domain blocklist check', e)
return false
}
}
if (isBlockedDomain()) return
const TRAP_PROPS = new Set(['id', 'name', 'stack', 'message', 'nodeName', 'tagName', 'className'])
// --- 1. window size spoofing ---
try {
Object.defineProperty(w, 'outerWidth', { get: () => w.innerWidth, configurable: true })
Object.defineProperty(w, 'outerHeight', { get: () => w.innerHeight, configurable: true })
} catch (e) {
reportError('Section 1 (window size spoofing)', e)
}
// --- 2. keyboard shortcut unblocking ---
try {
const DEVTOOLS_KEYS = new Set(['F12', 'I', 'J', 'C', 'U', 'S', 'K', 'P'])
function isDevtoolsShortcut(e) {
if (e.key === 'F12') return true
if ((e.ctrlKey || e.metaKey) && e.shiftKey && DEVTOOLS_KEYS.has(e.key.toUpperCase())) return true
if ((e.ctrlKey || e.metaKey) && (e.key === 'u' || e.key === 'U')) return true
return false
}
w.addEventListener('keydown', function (e) {
if (isDevtoolsShortcut(e)) {
e.stopImmediatePropagation()
}
}, true)
} catch (e) {
reportError('Section 2 (keyboard shortcuts)', e)
}
// --- 3. context menu unblocking ---
try {
w.addEventListener('contextmenu', function (e) {
e.stopImmediatePropagation()
}, true)
} catch (e) {
reportError('Section 3 (context menu)', e)
}
// --- 4. console method protection ---
try {
const con = w.console
const noop = makeNative(() => {}, '')
con.clear = noop
con.profile = noop
con.profileEnd = noop
con.table = noop
const dangerous = ['log', 'dir', 'warn', 'error', 'info', 'debug']
dangerous.forEach(method => {
if (!con[method]) return
const orig = con[method].bind(con)
const wrapped = function (...args) {
const safe = args.filter(a => {
if (a === null || typeof a !== 'object') return true
try {
for (const prop of TRAP_PROPS) {
const desc = Object.getOwnPropertyDescriptor(a, prop)
if (desc && (typeof desc.get === 'function' || typeof desc.set === 'function')) {
return false
}
}
} catch (e) {
return false
}
return true
})
if (safe.length) orig(...safe)
}
makeNative(wrapped, method)
con[method] = wrapped
})
} catch (e) {
reportError('Section 4 (console protection)', e)
}
// --- 5. Object.defineProperty & Object.defineProperties interception ---
const _defineProperty = Object.defineProperty.bind(Object)
const _defineProperties = Object.defineProperties ? Object.defineProperties.bind(Object) : null
function sanitizeDescriptor(obj, prop, descriptor) {
if (
descriptor &&
typeof descriptor.get === 'function' &&
TRAP_PROPS.has(prop) &&
obj !== w &&
obj !== Object.prototype
) {
return Object.assign({}, descriptor, {
get: () => undefined,
set: undefined,
})
}
return descriptor
}
try {
const patchedDefineProperty = function defineProperty(obj, prop, descriptor) {
try {
descriptor = sanitizeDescriptor(obj, String(prop), descriptor)
} catch (e) {
reportError('defineProperty descriptor sanitizer', e)
}
return _defineProperty(obj, prop, descriptor)
}
makeNative(patchedDefineProperty, 'defineProperty')
_defineProperty(Object, 'defineProperty', {
value: patchedDefineProperty,
writable: true,
configurable: true,
enumerable: false,
})
} catch (e) {
reportError('Section 5 (defineProperty)', e)
}
if (_defineProperties) {
try {
const patchedDefineProperties = function defineProperties(obj, props) {
try {
if (props && typeof props === 'object') {
const sanitizedProps = {}
for (const key of Object.keys(props)) {
sanitizedProps[key] = sanitizeDescriptor(obj, key, props[key])
}
props = sanitizedProps
}
} catch (e) {
reportError('defineProperties descriptor sanitizer', e)
}
return _defineProperties(obj, props)
}
makeNative(patchedDefineProperties, 'defineProperties')
_defineProperty(Object, 'defineProperties', {
value: patchedDefineProperties,
writable: true,
configurable: true,
enumerable: false,
})
} catch (e) {
reportError('Section 5 (defineProperties)', e)
}
}
// --- 6. debugger trap neutralization ---
const _eval = w.eval
const _Function = w.Function
const debuggerOnlyRe = /^\s*debugger\s*;?\s*$/
try {
w.eval = function safeEval(code) {
if (typeof code === 'string' && debuggerOnlyRe.test(code)) return undefined
return _eval.call(w, code)
}
makeNative(w.eval, 'eval')
} catch (e) {
reportError('Section 6 (eval wrapper)', e)
}
try {
w.Function = function safeFunction(...args) {
const body = args[args.length - 1]
if (typeof body === 'string') {
args[args.length - 1] = body.replace(/\bdebugger\b/g, '')
}
return _Function(...args)
}
Object.setPrototypeOf(w.Function, _Function)
w.Function.prototype = _Function.prototype
makeNative(w.Function, 'Function')
} catch (e) {
reportError('Section 6 (Function wrapper)', e)
}
// --- 7. setInterval / setTimeout detection loop killing ---
const _setInterval = w.setInterval
const _setTimeout = w.setTimeout
const DETECTION_SIGS = [
'debugger',
'outerWidth', 'outerHeight',
'devtools', 'DevTools',
'firebug', 'Firebug',
'console.profile', 'profileEnd',
'performance.now',
'DisableDevtool', 'disable-devtool',
]
function looksLikeDetection(fn) {
if (typeof fn !== 'function') return false
try {
const src = _Function.prototype.toString.call(fn)
return DETECTION_SIGS.some(sig => src.includes(sig))
} catch (e) {
return false
}
}
try {
w.setInterval = function safeSetInterval(fn, delay, ...rest) {
if (looksLikeDetection(fn)) {
return _setInterval(() => {}, 99999999)
}
return _setInterval(fn, delay, ...rest)
}
makeNative(w.setInterval, 'setInterval')
} catch (e) {
reportError('Section 7 (setInterval)', e)
}
try {
w.setTimeout = function safeSetTimeout(fn, delay, ...rest) {
if (looksLikeDetection(fn)) {
return _setTimeout(() => {}, 99999999)
}
return _setTimeout(fn, delay, ...rest)
}
makeNative(w.setTimeout, 'setTimeout')
} catch (e) {
reportError('Section 7 (setTimeout)', e)
}
// --- 8. location / navigation redirect interception ---
const _reload = w.location.reload.bind(w.location)
const _replace = w.location.replace.bind(w.location)
const ERROR_PAGE_RE = /(?:404|blocked|disabled|error|403|detect|devtool|acellus\.com)/i
try {
Object.defineProperty(w.location, 'reload', {
get: () => function safeReload() {
if (w.navigator.userActivation && w.navigator.userActivation.isActive) {
_reload()
}
},
configurable: true,
})
} catch (e) {
reportError('Section 8 (location.reload)', e)
}
try {
Object.defineProperty(w.location, 'replace', {
get: () => function safeReplace(url) {
if (typeof url === 'string' && ERROR_PAGE_RE.test(url)) return
_replace(url)
},
configurable: true,
})
} catch (e) {
reportError('Section 8 (location.replace)', e)
}
try {
const _docWrite = document.write.bind(document)
document.write = function safeWrite(...args) {
if (document.readyState === 'loading') return _docWrite(...args)
}
makeNative(document.write, 'write')
} catch (e) {
reportError('Section 8 (document.write)', e)
}
// --- 9. alert / confirm / prompt neutralization ---
const ALERT_BLOCK_RE = /devtools?|inspect|developer\s*tools?|f12|debug/i
const _alert = w.alert.bind(w)
const _confirm = w.confirm.bind(w)
try {
w.alert = function safeAlert(msg) {
if (typeof msg === 'string' && ALERT_BLOCK_RE.test(msg)) return
_alert(msg)
}
makeNative(w.alert, 'alert')
} catch (e) {
reportError('Section 9 (alert)', e)
}
try {
w.confirm = function safeConfirm(msg) {
if (typeof msg === 'string' && ALERT_BLOCK_RE.test(msg)) return true
return _confirm(msg)
}
makeNative(w.confirm, 'confirm')
} catch (e) {
reportError('Section 9 (confirm)', e)
}
// --- 10. firebug / third-party debug lib spoofing ---
const DEBUGLIB_NAMES = [
'firebug', '_firebug',
'eruda', '__eruda',
'vConsole', 'VConsole',
'weinre', '__weinre',
'remoteDebugger',
]
DEBUGLIB_NAMES.forEach(name => {
try {
if (w[name] !== undefined) return
Object.defineProperty(w, name, {
get: () => undefined,
set: () => {},
configurable: true,
})
} catch (e) {
reportError(`Section 10 (${name})`, e)
}
})
try {
if (w.console && w.console.firebug) {
Object.defineProperty(w.console, 'firebug', { get: () => undefined, configurable: true })
}
} catch (e) {
reportError('Section 10 (console.firebug)', e)
}
// --- 11. performance.now() timing attack mitigation ---
const _perfNow = w.performance.now.bind(w.performance)
try {
w.performance.now = function safePerfNow() {
return _perfNow() + (Math.random() * 0.5)
}
makeNative(w.performance.now, 'now')
} catch (e) {
reportError('Section 11 (performance.now)', e)
}
// --- 12. toString / regex detection bypass ---
try {
const _reToString = RegExp.prototype.toString
Object.defineProperty(RegExp.prototype, 'toString', {
get: () => _reToString,
set: () => {},
configurable: false,
})
} catch (e) {
reportError('Section 12 (RegExp.toString)', e)
}
try {
const _fnToString = Function.prototype.toString
const patchedFnToString = function toString() {
if (NATIVE_FN_MAP.has(this)) {
return NATIVE_FN_MAP.get(this)
}
return _fnToString.call(this)
}
makeNative(patchedFnToString, 'toString')
Object.defineProperty(Function.prototype, 'toString', {
get: () => patchedFnToString,
set: () => {},
configurable: false,
})
} catch (e) {
reportError('Section 12 (Function.toString)', e)
}
// --- 13. text selection and drag unblocking ---
try {
const UNBLOCK_EVENTS = ['selectstart', 'copy', 'cut', 'dragstart', 'paste']
UNBLOCK_EVENTS.forEach(type => {
w.addEventListener(type, e => e.stopImmediatePropagation(), true)
})
w.addEventListener('DOMContentLoaded', () => {
try {
if (document.body) {
document.body.style.userSelect = ''
document.body.style.webkitUserSelect = ''
}
} catch (e) {}
})
} catch (e) {
reportError('Section 13 (unblock events)', e)
}
// --- 14. disable-devtool library kill switch ---
try {
if (w.DisableDevtool) {
w.DisableDevtool.isSuspend = true
}
let _ddt = undefined
Object.defineProperty(w, 'DisableDevtool', {
get: () => _ddt,
set: (lib) => {
_ddt = lib
if (_ddt && typeof _ddt === 'object') {
_ddt.isSuspend = true
if (typeof _ddt.stop === 'function') try { _ddt.stop() } catch (e) {}
}
},
configurable: true,
})
} catch (e) {
reportError('Section 14 (DisableDevtool)', e)
}
})(unsafeWindow)