为啥?
我经常喜欢打开开发者工具,看看网站的源码,dom之类的。看到喜欢的好看的页面,就会想办法看看是如何实现,甚至还会跑跑对方的接口。也会在一些收费或者VIP视频网站内看看是否可以钻一下漏洞,然后写一个油猴插件,增加使用体验。这就可能会对一些网站的拥有者造成了一些权益的损害。所以为了防止我们的代码被开发者调教,就得想办法在不影响正常用户的前提下增加调试者的门槛。
禁用右键+F12+(ctrl+shift+i)+(Shift+F10)
首先,第一道防线肯定是防止这位老哥打开devtool。
于是我们可以通过禁用右键与禁用F12的方法来防止唤起开发者工具。
// 禁止右键事件
document.oncontextmenu = function noMenuOne() {
//alert('禁止右键菜单!');
return false;
};
document.onkeydown = function () {
if (window.event && window.event.keyCode == 123) {
return false;
//禁用ctrl+shift+i,
} else if (window.event.ctrlKey && window.event.shiftKey && window.event.keyCode == 73) {
return false;
//屏蔽Shift+F10
} else if (window.event.shiftKey && window.event.keyCode == 121) {
return false;
}
};//禁止F12
这确实可以防止很多小白打开开发者工具。
还可以先打开开发者工具再打开我们页面,所以,防不胜防啊家人们。
无限debugger
看到这个,很多前端老哥自己悟了。心想这还不简单。
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) { }
})();
反手一个自执行函数+无限调用等于无敌
但,真的这么简单吗?我们完全可以通过打开这个"停用断点"功能来绕过无限debugger。
或者通过忽略执行代码的方式,也可以避免无限debugger
不过忽略代码可以这样来绕过
debugger
// 替换为
Function("debugger")()
Function 构造器生成的 debugger 会在每一次执行时开启一个临时 js 文件。
于是这种对老开发来讲掩耳盗铃的操作,效果就不尽人意了。
所以我们还能做什么?
监听浏览器宽高变化
实时检测浏览器宽高与打开F12后的宽高进行比对,有差值,说明打开了开发者工具,则清空DOM。
(() => {
function block() {
if (window.outerHeight - window.innerHeight > 200 || window.outerWidth - window.innerWidth > 200) {
document.body.innerHTML = "检测到非法调试";
}
setInterval(() => {
(function () {
return false;
}
['constructor']('debugger')
['call']());
}, 50);
}
try {
block();
} catch (err) { }
})();
这招确实逆天。但是仍然可以在"源代码/来源的位置查看封装后的源码等等"
但还有更强的方案
利用debugger特性禁止调试
我们运行下一段代码
(() => {
function block() {
setInterval(function () {
var startTime = performance.now();
// 设置断点
debugger;
var endTime = performance.now();
// 设置一个阈值,例如100毫秒
if (endTime - startTime > 100) {
window.location.href = 'about:blank';
}
}, 100);
}
try {
block();
} catch (err) { }
})();
由于debugger是同步停顿,无论再快,经过测试,阈值也在100毫秒以上,我们可以通过这个方法来检查是否开启开发者工具。如果开了,就跳转空白页,这回确实啥都看不到了,我愿称为自己的版本答案。那,别人的版本答案是什么?
disable-devtool
这是一个开源的可以禁用所有一切可以进入开发者工具的库。
支持可配置是否禁用右键菜单
禁用 f12 和 ctrl+shift+i 等快捷键
支持识别从浏览器菜单栏打开开发者工具并关闭当前页面
开发者可以绕过禁用 (url参数使用tk配合md5加密)
多种监测模式,支持几乎所有浏览器(IE,360,qq浏览器,FireFox,Chrome,Edge...)
高度可配置、使用极简、体积小巧
支持npm引用和script标签引用(属性配置)
识别真移动端与浏览器开发者工具设置插件伪造的移动端,为移动端节省性能
支持识别开发者工具关闭事件
支持可配置是否禁用选择、复制、剪切、粘贴功能
支持识别 eruda 和 vconsole 调试工具
支持挂起和恢复探测器工作
支持配置ignore属性,用以自定义控制是否启用探测器
支持配置iframe中所有父页面的开发者工具禁用
功能之多,恐怖如斯。github 1.6K start。中文文档。
使用
原生
<script disable-devtool-auto src='https://cdn.jsdelivr.net/npm/disable-devtool@latest'></script>
npm
npm i disable-devtool
import DisableDevtool from 'disable-devtool';
DisableDevtool(options);
options中的参数与说明如下
interface IConfig {
md5?: string; // 绕过禁用的md5值,详情见3.2,默认不启用绕过禁用
url?: string; // 关闭页面失败时的跳转页面,默认值为localhost
tkName?: string; // 绕过禁用时的url参数名称,默认为 ddtk
ondevtoolopen?(type: DetectorType, next: Function): void; // 开发者面板打开的回调,启用时url参数无效,type 为监测模式,详见3.5, next函数是关闭当前窗口
ondevtoolclose?(): void; // 开发者面板关闭的回调
interval?: number; // 定时器的时间间隔 默认200ms
disableMenu?: boolean; // 是否禁用右键菜单 默认为true
stopIntervalTime?: number; // 在移动端时取消监视的等待时长
clearIntervalWhenDevOpenTrigger?: boolean; // 是否在触发之后停止监控 默认为false, 在使用ondevtoolclose时该参数无效
detectors?: Array<DetectorType>; // 启用的检测器 检测器详情见 3.5 默认为全部,建议使用全部
clearLog?: boolean; // 是否每次都清除log
disableSelect?: boolean; // 是否禁用选择文本 默认为false
disableCopy?: boolean; // 是否禁用复制 默认为false
disableCut?: boolean; // 是否禁用剪切 默认为false
disablePaste: boolean; // 是否禁用粘贴 默认为false
ignore?: (string|RegExp)[] | null | (()=>boolean); // 某些情况忽略禁用
disableIframeParents?: boolean; // iframe中是否禁用所有父窗口
timeOutUrl?: string; // 关闭页面超时跳转的url;
rewriteHTML: string; // 检测到打开之后重写页面
}
enum DetectorType {
Unknown = -1,
RegToString = 0, // 根据正则检测
DefineId, // 根据dom id检测
Size, // 根据窗口尺寸检测
DateToString, // 根据Date.toString 检测
FuncToString, // 根据Function.toString 检测
Debugger, // 根据断点检测,仅在ios chrome 真机情况下有效
Performance, // 根据log大数据性能检测
DebugLib, // 检测第三方调试工具 eruda 和 vconsole
};
完结
以上的操作对一些大佬仍然是小巫见大巫,毕竟客户已经通过请求拿到了相关源码。
破解禁止调试方面
全局禁用debugger
局部禁用debugger
条件禁用debugger
JS保存到本地,然后替换debugger字符
油猴插件,YYDS
油猴代码如下
(function () {
// 破解无限Debugger
var constructorHook = constructor;
Function.prototype.constructor = function (s) {
if (s == "debugger") {
return function () { }
}
return constructorHook(s);
}
const setInterval = window.setInterval;
window.setInterval = function (fun, time) {
// console.log(time, 'ddddd', fun.toString());
if (fun && fun.toString) {
var funString = fun.toString();
if (funString.indexOf('debugger') > -1) return;
if (funString.indexOf('window.close') > -1) return;
}
return setInterval(fun, time);
}
})()
(() => {
function ban() {
setInterval(() => {
debugger;
}, 50);
}
try {
ban();
} catch (err) { }
})();
原理就是替换,功能还可以根据我上面的方法做针对性修改。