关于浏览器Devtools的open/close监听
前言
常见open行为结果
- 无限debugger
- 反复刷新页面
- 跳转指定页面
页面跳转:
*// 类似 HTTP 重定向到菜鸟教程* window.location.replace("http://localhost"); *// 类似点击菜鸟教程的链接(a 标签)* window.location.href = "http://localhost";
关闭页面:
window.close() // 不过只能由 Window.open() 方法打开的窗口的 window 对象来调用。
通常各个监听行为都使用了
setInterval
或setTimeout
函数, 不方便debug的时候可以使用 定时器事件断点
以下是sgcc.com.cn
中对于打开devtool行为的处理
try {
window.opener = null, // 返回打开当前窗口的那个窗口的引用
window.open("", "_self"), // 这里先使用了, open方法打开当前窗口, 再使用close去关闭
window.close(), // 关闭当前窗口, 不过有时候还是会失败(应该与浏览器有关系)
window.history.back() // 会话历史记录中向后移动一页, 没有不执行
} catch (e) {
}
setTimeout((function () {
window.location.href = f.timeOutUrl ||
"https://theajack.github.io/disable-devtool/404.html?h=".concat(encodeURIComponent(location.host))
}
), 500)
一些使用了反调试的网站
TIP: 浏览器的不同可能导致方法在不同的浏览器出现不同的结果
1. 窗口判断
方案: 根据浏览器窗口判断
缺点: 无法监听独立窗口的devtool
demo: https://sindresorhus.com/devtools-detect/
代码: sindresorhus/devtools-detect: Detect if DevTools is open and its orientation (github.com)
const devtools = {
isOpen: false,
orientation: undefined,
};
const threshold = 170;
const main = ({emitEvents = true} = {}) => {
const widthThreshold = globalThis.outerWidth - globalThis.innerWidth > threshold;
const heightThreshold = globalThis.outerHeight - globalThis.innerHeight > threshold;
const orientation = widthThreshold ? 'vertical' : 'horizontal';
if (
!(heightThreshold && widthThreshold)
&& ((globalThis.Firebug && globalThis.Firebug.chrome && globalThis.Firebug.chrome.isInitialized) || widthThreshold || heightThreshold)
) {
devtools.isOpen = true;
devtools.orientation = orientation;
} else {
devtools.isOpen = false;
devtools.orientation = undefined;
}
};
2. debugger时间差
方案: 打开控制台会触发debugger, 导致debugger前后时间差过大; 根据时间差判断是否打开控制台
缺点: 容易被定位到关键代码位置, 覆盖/重写关键函数即可破解; 使用debugger
直接使用永不在此暂停
就跳过了这个debug
相关:
- demo: https://codepo8.github.io/console-killer/demo.html
- 代码: https://github.com/codepo8/console-killer
- 文章: https://dev.to/codepo8/cracking-a-developer-tools-killer-script-2lpl
具体代码还做了对F12
, 右键菜单做了屏蔽,无法在demo中打开控制台; 但是这种无法避免我先打开控制台, 再进行访问地址
var minimalUserResponseInMiliseconds = 200;
function check() {
console.clear();
before = new Date().getTime();
debugger;
after = new Date().getTime();
if (after - before > minimalUserResponseInMiliseconds) {
document.write(" Dont open Developer Tools. ");
self.location.replace(
window.location.protocol + window.location.href.substring(
window.location.protocol.length
)
);
} else {
before = null;
after = null;
delete before;
delete after;
}
setTimeout(check, 100);
}
3. 读取元素id
利用Chrome开发者工具会自动读取元素id的特性,重写getter实现需求
let element = document.createElement('checkDevTools');
Object.defineProperty(element, 'id', {
get: function () {
/* TODO */
isDevToolsOpened = true;
}
});
element.__defineGetter__('id', function () {
isDevToolsOpened = true;
});
console.debug(element); //使用console.debug()使打印出的辅助信息默认隐藏
4. performance对象比较性能
博客: 如何知道 Chrome 控制台何时打开 (guya.net)
demo: Chrome Developer Tools State (guya.net)
APi: Performance - Web API | MDN (mozilla.org)
- **通过
performance
对象:**可以使用performance
对象来检测页面加载的性能。一般情况下,开发者工具的打开会对性能指标产生一些影响,如资源加载时间的延长。因此,可以通过比较某些性能指标的值来判断开发者工具是否打开。if (performance.memory && performance.memory.usedJSHeapSize > 0) { console.log("Developer tools open"); // 内存使用情况大于0,可能表示开发者工具已打开 }
5. toString
对于一些浏览器,比如Chrome、FireFox,如果控制台输出的是对象,则保留对象的引用,每次打开开发者工具的时候都会重新调用一下对象的toString()方法将返回结果打印到控制台(console tab)上。
所以只需要创建一个对象,重写它的toString()方法,然后在页面初始化的时候就将其打印在控制台上(这里假设控制台还没有打开),当用户打开控制台时会再去调用一下这个对象的toString()方法,用户打开控制台的行为就会被捕获到。
缺点: 只能捕获到开发者工具处于关闭状态向打开状态转移的过程(先打开devtool再访问网页即可破解)
var devtools = function() {};
// var devtools = /./;
devtools.toString = function() {
if (!this.opened) {
alert("Opened");
}
this.opened = true;
}
console.log('%c', devtools);
不过使用这个在新版的 chrome 126.0.6478.127
中貌似已经失效了, 我测试theajack/disable-devtool
库只能使用窗口判断和性能判断两种方式可以触发这个版本的 chrome.
三方库:
1. theajack/disable-devtool
demo: Disable web developer tools with one line (theajack.github.io)
使用的检测方法如下:
RegToString = 0, // 根据正则检测
DefineId, // 根据dom id检测
Size, // 根据窗口尺寸检测
DateToString, // 根据Date.toString 检测
FuncToString, // 根据Function.toString 检测
Debugger, // 根据断点检测,仅在ios chrome 真机情况下有效
Performance, // 根据log大数据性能检测
sgcc.com.cn 网站使用了这个库
2. AEPKILL/devtools-detector
使用了多种检查方式判断
github: devtools-detector/src/index.ts at master · AEPKILL/devtools-detector (github.com)
demo: https://blog.aepkill.com/demos/devtools-detector/
other:
讨论
- javascript - Find out whether Chrome console is open - Stack Overflow
- (3 封私信) 前端开发中如何在JS文件中检测用户浏览器是否打开了调试面板(F12打开开发者工具)? - 知乎 (zhihu.com)
- https://dev.to/codepo8/cracking-a-developer-tools-killer-script-2lpl
- https://jsfiddle.net/eligrey/j4v270p4/