🏠 返回首頁 

Greasy Fork is available in English.

浏览器控制台防检测

根据 https://github.com/AEPKILL/devtools-detector 的检测方法进行了一个逆向反检测...需要在哪些网站上运行,自己添加到脚本编辑器-设置-用户包括里面去


Installer ce script?
// ==UserScript==
// @name               浏览器控制台防检测
// @namespace          https://greasyfork.org/users/667968-pyudng
// @version            0.10.1
// @description        根据 https://github.com/AEPKILL/devtools-detector 的检测方法进行了一个逆向反检测...需要在哪些网站上运行,自己添加到脚本编辑器-设置-用户包括里面去
// @author             PY-DNG
// @license            MIT
// @match              http*://AddYour.OwnMatch/
// @match              http*://blog.aepkill.com/demos/devtools-detector/
// @require            https://update.greasyfork.org/scripts/456034/1546794/Basic%20Functions%20%28For%20userscripts%29.js
// @grant              GM_registerMenuCommand
// @grant              GM_getValue
// @grant              GM_setValue
// @grant              GM_addElement
// @run-at             document-start
// ==/UserScript==
/* eslint-disable no-multi-spaces */
/* eslint-disable no-return-assign */
/* global LogLevel DoLog Err $ $All $CrE $AEL $$CrE addStyle detectDom destroyEvent copyProp copyProps parseArgs escJsStr replaceText getUrlArgv dl_browser dl_GM AsyncManager queueTask FunctionLoader loadFuncs require isLoaded */
(function __MAIN__() {
'use strict';
const CONST = {
TextAllLang: {
DEFAULT: 'zh-CN',
'zh-CN': {}
}
};
// Init language
const i18n = Object.keys(CONST.TextAllLang).includes(navigator.language) ? navigator.language : CONST.TextAllLang.DEFAULT;
CONST.Text = CONST.TextAllLang[i18n];
// As a common dependency for almost all funcs, load it synchrously to execute funcs as soon as poosible
// If we write utils as a normal func which executes in loadFuncs() and serves as a func module through require('utils'),
// func that depends on utils must wait until utils load asynchrously, which will takes a lot of time.
// Execute as soon as possible is essential to ensure we have time advantage agains devtools detectors.
const utils = (function() {
const win = typeof unsafeWindow === 'undefined' ? window : unsafeWindow;
const SavedValues = {
_targets: ['Object', 'Function', 'Symbol', 'Reflect', 'performance', 'setTimeout', 'Proxy'],
_thiskey: Symbol('SavedValues.this-key'),
original: {}, // original property descriptors
bound: {}     // property descriptors with "this" bound for whose value is function
};
for (const target_name of SavedValues._targets) {
const source = win[target_name];
if (['object', 'function'].includes(typeof source) && source !== null) {
// Save original
const obj_original = SavedValues.original[target_name] = { [SavedValues._thiskey]: source };
const obj_bound = SavedValues.bound[target_name] = {}
for (const prop of Reflect.ownKeys(source)) {
const desc_original = Object.getOwnPropertyDescriptor(source, prop);
Object.defineProperty(obj_original, prop, desc_original);
const desc_bound = { ...desc_original };
if (typeof desc_bound.value === 'function') {
desc_bound.value = desc_bound.value.bind(source);
}
Object.defineProperty(obj_bound, prop, desc_bound);
}
} else {
SavedValues.original[target_name] = source;
SavedValues.bound[target_name] = source;
}
}
function hook(obj, prop, func) {
const ori_func = obj[prop];
const hook_func = obj[prop] = wrap(ori_func, func);
return {
hook_func, ori_func, unhook
};
function unhook() {
obj[prop] = ori_func;
}
function wrap(ori_func, func) {
const toString = ori_func.toString;
return new SavedValues.original.Proxy[SavedValues._thiskey](ori_func, {
construct(target, argumentsList, newTarget) {
return new func(target, ...argumentsList);
},
apply(target, thisArg, argumentsList) {
return func.call(thisArg, target, ...argumentsList);
},
get(target, prop, receiver) {
if (prop === 'toString') {
return wrap(target[prop], function() { return toString.call(this); });
}
return target[prop];
}
});
}
}
return { window: win, SavedValues, hook }
}) ();
loadFuncs([{
id: 'log-toString-trap',
desc: '处理各种通过自定义toString/字符串getter,再log到console的检测方法',
func() {
const console = utils.window.console;
const toStringModified = obj => Function.prototype.toString.call(obj.toString) !== 'function toString() { [native code] }';
utils.hook(console, 'log', function(log, obj) {
if (obj instanceof Date && toStringModified(obj)) {
DoLog(LogLevel.Success, ['拦截了一次 date-toString-trap']);
return log.call(this);
}
if (obj instanceof Date && toStringModified(obj)) {
DoLog(LogLevel.Success, ['拦截了一次 date-toString-trap']);
return log.call(this);
}
if (obj instanceof HTMLElement && utils.SavedValues.original.Object.hasOwnProperty.call(obj, 'id')) {
DoLog(LogLevel.Success, ['拦截了一次 element-id-trap']);
return log.call(this);
}
if (obj instanceof Function && toStringModified(obj)) {
DoLog(LogLevel.Success, ['拦截了一次 function-toString-trap']);
return log.call(this);
}
if (obj instanceof RegExp && toStringModified(obj)) {
DoLog(LogLevel.Success, ['拦截了一次 regexp-toString-trap']);
return log.call(this);
}
const args = [...arguments];
args.shift();
return log.apply(this, args);
});
utils.hook(console, 'table', function(table, obj) {
for (const prop of utils.SavedValues.bound.Reflect.ownKeys(obj)) {
const val = obj[prop];
if (val instanceof RegExp && toStringModified(val)) {
DoLog(LogLevel.Success, ['拦截了一次 dep-reg-trap']);
return table.call(this);
}
}
const args = [...arguments];
args.shift();
return table.apply(this, args);
});
}
}, {
id: 'time-sync',
desc: '处理各种利用时间检测的方法;原理是让获取时间的函数在一次事件循环里返回同一个值',
func() {
return {
getTime: {
performance: hookTimeFunc(utils.window.performance, 'now'),
date: hookTimeFunc(utils.window.Date, 'now'),
dateInstance: hookTimeFunc(utils.window.Date, 'getTime', true)
}
};
function hookTimeFunc(obj, funcname, construct=false) {
// 存储当前事件循环里,performance.time返回的值
// 确切地说,是从一次work调用到下一次work调用之间,返回相同的值
let time;
// hook performance.now,始终返回time的值
const getTime = construct ?
(function() {
const func = obj.prototype[funcname];
return function() {
return func.call(new obj(), ...arguments);
};
}) () : obj[funcname].bind(obj);
Object.defineProperty(construct ? obj.prototype : obj, funcname, {
get() { return function() { return time; } },
set() { return true; },
});
// work函数,每次事件循环执行一次,清空
function work() {
const setTimeout = utils.SavedValues.original.setTimeout[utils.SavedValues._thiskey].bind(utils.window);
setTimeout(work);
time = getTime();
}
work();
return getTime;
}
}
}, {
id: 'no-debugger',
desc: '拦截debugger语句',
func() {
utils.hook(Function.prototype, 'constructor', function(target, ...args) {
const code = [...args].pop();
const is_debugger = code.replaceAll(/[\s;]/g, '') === 'debugger';
return is_debugger ? target() : target.apply(this, args);
});
utils.hook(utils.window, 'Function', function(target, ...args) {
const code = [...args].pop();
const is_debugger = code.replaceAll(/[\s;]/g, '') === 'debugger';
return is_debugger ? target() : target.apply(this, args);
});
}
}, {
id: 'console-no-clear',
desc: '防止调用console.clear()循环清除控制台',
func() {
const console = utils.window.console;
const clear = console._clear = console.clear;
// 提供选项彻底禁用console.clear
let no_clear = isEnabled();
GM_registerMenuCommand('彻底禁用console.clear', function() {
no_clear = !no_clear;
if (no_clear) {
enable();
console._log(`%c已彻底禁用%cconsole.clear\n%c如需清除控制台,请使用控制台清除按钮,或者调用%cconsole._clear()`, 'color: #66cc00;', 'color: orange;', 'color: #66ccff;', 'color: orange;');
} else {
disable();
console._log('%c已重新启用%cconsole.clear', 'color: #66cc00;', 'color: orange;');
}
});
// 自动拦截高频清除
const min_interval = 750, startup_protection = 5000;
let last_clear = getTime();
const hook = utils.hook(console, 'clear', function() {
const startup_time = getTime();
const time_past = startup_time - last_clear;
const can_clear = !no_clear && time_past > min_interval && startup_time > startup_protection;
can_clear && clear();
last_clear = startup_time;
});
console.log([
`%c[${GM_info.script.name}] %c已拦截%cconsole.clear%c以防止控制台被高频循环清除`,
`当%cconsole.clear%c距离上次调用的时间间隔小于%c${ min_interval / 1000 }秒%c时,将不执行清除`,
`同时,页面加载的%c前${ startup_protection / 1000 }秒%c也不会执行清除`,
`需要注意的是,当将浏览器/标签页切换到后台时,循环清除任务可能会被浏览器放慢,从而绕过高频清除限制`,
'',
`您也可通过手动开启 %c"彻底禁用console.clear"%c 功能彻底禁止%cconsole.clear%c清除控制台`,
`当前 %c"彻底禁用console.clear"%c 功能%c${ no_clear ? '已开启' : '未开启'}%c`
].join('\n'), ...[
'color: #9999ff;', '',
'color: orange;', '',
'color: orange;', '',
'color: orange;', '',
'color: orange;', '',
'color: #cc9966;', '',
'color: orange;', '',
'color: #cc9966;', '',
'color: #66cc00;', ''
]);
return {
clear,
get clear_diabled() { return no_clear; },
set clear_diabled(val) { (no_clear = val) ? enable() : disable(); }
}
// 获取精确的(未被hook修改的)页面加载到现在的时间戳
function getTime() {
return isLoaded('time-sync') ? require('time-sync').getTime.performance() : performance.now();
}
function getEnabledHostList() {
const enabled_list = GM_getValue('noclear-hosts', []);
return enabled_list;
}
function saveEnabledHostList(enabled_list) {
GM_setValue('noclear-hosts', enabled_list);
}
function isEnabled() {
const host = location.host;
const enabled_list = getEnabledHostList();
return enabled_list.includes(host);
}
function enable() {
const host = location.host;
const enabled_list = getEnabledHostList();
!enabled_list.includes(host) && enabled_list.push(host);
saveEnabledHostList(enabled_list);
}
function disable() {
const host = location.host;
const enabled_list = getEnabledHostList();
enabled_list.includes(host) && enabled_list.splice(enabled_list.indexOf(host), 1);
saveEnabledHostList(enabled_list);
}
}
}, {
id: 'console-filter',
desc: '提供选项供用户拦截控制台日志',
func() {
let filter_enabled = isEnabled();
GM_registerMenuCommand('禁止网页输出日志', function() {
filter_enabled = !filter_enabled;
if (filter_enabled) {
enable();
console._log(`%c已禁止网页输出日志到控制台\n%c如需调用%cconsole.xxx%c输出,请在xxx前加一个下划线%c"_"%c再调用,如 %cconsole._log('Hello, world');`, 'color: #66cc00;', 'color: #66ccff;', 'color: orange;', 'color: #66ccff;', 'color: orange', 'color: #66ccff;', 'color: orange');
} else {
disable();
console._log('%c已允许网页输出日志到控制台', 'color: #66cc00;');
}
});
const console = utils.window.console;
Object.keys(console).forEach(prop => {
if (['clear', '_clear'].includes(prop)) { return; }
console['_' + prop] = console[prop];
utils.hook(console, prop, function(func, ...args) {
if (!filter_enabled) {
return func.apply(this, args);
}
});
});
return {
get enabled() { return filter_enabled; },
set enabled(val) { val ? enable() : disable(); }
};
function getEnabledHostList() {
const enabled_list = GM_getValue('filtered-hosts', []);
return enabled_list;
}
function saveEnabledHostList(enabled_list) {
GM_setValue('filtered-hosts', enabled_list);
}
function isEnabled() {
const host = location.host;
const enabled_list = getEnabledHostList();
return enabled_list.includes(host);
}
function enable() {
const host = location.host;
const enabled_list = getEnabledHostList();
!enabled_list.includes(host) && enabled_list.push(host);
saveEnabledHostList(enabled_list);
}
function disable() {
const host = location.host;
const enabled_list = getEnabledHostList();
enabled_list.includes(host) && enabled_list.splice(enabled_list.indexOf(host), 1);
saveEnabledHostList(enabled_list);
}
}
}, {
id: 'user-code',
desc: '在文档开始加载时运行用户提供的自定义代码',
func() {
// 运行用户代码
const user_code = getUserCode();
GM_addElement(document.head, 'script', {
textContent: user_code
});
// 接收用户代码
GM_registerMenuCommand('自定义用户代码', function() {
const user_input = prompt('您可以在这里输入一些您自己的javascript代码,脚本会在页面开始加载时执行它', getUserCode());
if (user_input === null) { return; }
saveUserCode(user_input);
});
function getUserCode() {
const user_code = GM_getValue('user-code', '');
return user_code;
}
function saveUserCode(user_code) {
GM_setValue('user-code', user_code);
}
}
}]);
})();