在某些情况下我们需要检测当前用户是否打开了浏览器开发者工具,比如前端爬虫检测,如果检测到用户打开了控制台就认为是潜在的爬虫用户,再通过其它策略对其进行处理。本篇文章主要讲述几种前端JS检测开发者工具是否打开的方法。
一、重写toString()
对于一些浏览器,比如Chrome、FireFox,如果控制台输出的是对象,则保留对象的引用,每次打开开发者工具的时候都会重新调用一下对象的toString()方法将返回结果打印到控制台(console tab)上。
所以只需要创建一个对象,重写它的toString()方法,然后在页面初始化的时候就将其打印在控制台上(这里假设控制台还没有打开),当用户打开控制台时会再去调用一下这个对象的toString()方法,用户打开控制台的行为就会被捕获到。
下面是一个小小的例子,当Chrome用户的开发者工具状态从关闭向打开转移时,这个动作会被捕获到并交由回调函数处理:
<html> <head> <title>console detect test</title> </head> <body> <script> /** * 控制台打开的时候回调方法 */ function consoleOpenCallback(){ alert("CONSOLE OPEN"); return ""; } /** * 立即运行函数,用来检测控制台是否打开 */ !function () { // 创建一个对象 let foo = /./; // 将其打印到控制台上,实际上是一个指针 console.log(foo); // 要在第一次打印完之后再重写toString方法 foo.toString = consoleOpenCallback; }() </script> </body> </html>
效果:
当第一次在此页面打开控制台时会触发到检测,但是如果是在一个已经打开了控制台的窗口中粘贴网址访问则不会触发,同理在此页面上已经打开控制台时刷新也不会触发。
这种方式虽然比较取巧,但是并不具有通用性,并且只能捕获到开发者工具处于关闭状态向打开状态转移的过程,具有一定的局限性。
二、debugger
类似于代码里的断点,浏览器在打开开发者工具时(对应于代码调试时的debug模式)检测到debugger标签(相当于是程序中的断点)的时候会暂停程序的执行:
此时需要点一下那个蓝色的“Resume script execution”程序才会继续执行,这中间会有一定的时间差,通过判断这个时间差大于一定的值就认为是打开了开发者工具。这个方法并不会误伤,当没有打开开发者工具时遇到debugger标签不会暂停,所以这种方法还是蛮好的,而且通用性比较广。
下面是一个使用debugger标签检测开发者工具是否打开的例子:
<html> <head></head> <body> <script> function consoleOpenCallback() { alert("CONSOLE OPEN"); } !function () { const handler = setInterval(() => { const before = new Date(); debugger; const after = new Date(); const cost = after.getTime() - before.getTime(); if (cost > 100) { consoleOpenCallback(); clearInterval(handler) } }, 1000) }(); </script> </body> </html>
效果:
但是上面的代码有一个很严重的bug,就是在执行到debugger那一行的时候如果用户发现了猫腻没有点按resume script execution按钮,而是直接退出页面的话,那么将不能检测到本次的打开开发者工具行为,实际结果与预期不符,我认为这是严重bug,就像电影里演的不小心踩到地雷及时察觉不抬脚就还有活命机会,到了debugger标签我察觉到这是检测控制台是否打开的代码我退出然后使用其它手段绕过它,那我可能做了一个假功能。
有一个需要注意的地方就是使用此方法的时候当卡在debugger标签的时候,用户是能够看到debugger标签附近的代码的,如果是有经验的用户一眼就能看出里面的道道,所以要想办法隐藏一下真实目的,比如将debugger标签隐藏,并且对代码进行混淆尽量增加阅读难度,关于如何隐藏debugger标签前后的逻辑,可以参考这几个网站:(当前2018-7-4 23:12:17有效)
http://app2.sfda.gov.cn/datasearchp/gzcxSearch.do"_blank" href="https://www.qimai.cn/" rel="external nofollow" >https://www.qimai.cn/
使用此种方案的话可能有个需要注意的点就是debugger是有可能不会暂停的,比如Chrome浏览器的source面板可以选择在debugger语句时不暂停:
如果这个按钮被点亮,再测试上面的网页就会发现很悲剧检测代码失效了,因为debugger标签根本就没有暂停。
其实debugger标签还有另一种妙用,比如用来反调试,可以设定一个每秒就触发一个debugger,让调试者疲于应付debugger或者耗费他额外的成本去覆盖掉JS,上面给出的几个网站就是这么做的。
下面是一个使用debugger标签反js调试的简单例子:
<html> <head> <title>Anti debug</title> </head> <body> <script> !function () { setInterval(() => { debugger; }, 1000); }(); </script> </body> </html>
效果:
一个实际的例子,这个网站:http://jxw.uou0.com/的js检测脚本,而针对不同的情况它又会有不同的反调试策略。
注意要想复现需要粘贴视频地址解析之后才会加载检测脚本,比如可以尝试解析这个视频:http://film.qq.com/film/p/topic/thwjlxby/index.html。
当未打开开发者工具进行解析,然后打开开发者工具,则会使用这种检测方式:
!function() { var timelimit = 50; var open = false; setInterval(function() { var starttime = new Date(); debugger ;if (new Date() - starttime > timelimit) { open = true; window.stop(); $("#loading").hide(); $("#a1").remove(); $("#error").show(); $("#error").html("\u7cfb\u7edf\u68c0\u6d4b\u975e\u6cd5\u8c03\u8bd5\u002c\u8bf7\u5237\u65b0\u91cd\u8bd5\u0021") } else { open = false } }, 500) }();
因为这个网站是做vip视频免费解析的,一旦检测到有人打开开发者工具在调试,就将解析好的视频移除掉,通过弹出一个提示框:
而当已经打开开发者工具再粘贴地址进行视频解析的话,将会触发无限debugger。
当然,应付上面脚本最简单的方法是把Chrome浏览器设定为Deactive breakpoint,上面的脚本就歇菜了,不过这样的话自己也没办法调试了,用来反调试确实能够恶心一下对面的家伙,比较好的方法是使用Fiddler修改网页返回内容过滤掉debugger标签可以完美破解此套路。
三、检测窗口大小
检测窗口大小比较简单,首先要明确两个概念,窗口的outer大小和inner大小:
window.innerWidth / window.innerHeight :可视区域的宽高,window.innerWidth包含了纵向滚动条的宽度,window.innerHeight包含了水平(横向)滚动条的宽度。
window.outerWidth / window.outerHeight:会在innerWidth和innerHeight的基础上加上工具条的宽度。
关于检测窗口大小,不再自己写例子,有人专门针对此写了个库:https://github.com/sindresorhus/devtools-detect,毕竟几百个star,比我这个渣渣写的好多了,代码比较简单,使用部分其github都有说明,这里只对其核心代码做个分析,此处贴出鄙人对此库核心代码的分析:
/* eslint-disable spaced-comment */ /*! devtools-detect Detect if DevTools is open https://github.com/sindresorhus/devtools-detect by Sindre Sorhus MIT License comment by CC11001100 */ (function () { 'use strict'; var devtools = { open: false, orientation: null }; // inner大小和outer大小超过threshold被认为是打开了开发者工具 var threshold = 160; // 当检测到开发者工具后发出一个事件,外部监听此事件即可,设计得真好,很好的实现了解耦 var emitEvent = function (state, orientation) { window.dispatchEvent(new CustomEvent('devtoolschange', { detail: { open: state, orientation: orientation } })); }; // 每500毫秒检测一次开发者工具的状态,当状态改变时触发事件 setInterval(function () { var widthThreshold = window.outerWidth - window.innerWidth > threshold; var heightThreshold = window.outerHeight - window.innerHeight > threshold; var orientation = widthThreshold "image" border="0" alt="image" width="215" height="48" src="/UploadFiles/2021-04-02/202010021259478.png">靠左、靠右、靠下都会占用当前窗口的一些空间,这种情况会被检测到,但是独立窗口并不会占用打开网页窗口的空间,所以这种情况是检测不到的,可去此页面进行验证:https://sindresorhus.com/devtools-detect/。
四、总结
本文介绍了几种检测方式,其各有利弊,下面是对其缺点的一个简单的总结:
重写toString():只能捕获到开发者工具从关闭状态向打开状态转移的过程
debugger标签:当勾选了Chrome浏览器的Deactive breakpoint ,debugger标签不会暂停,将捕获不到
检测窗口大小:当开发者工具是以独立窗口打开的时候不能检测到
JS检测浏览器,开发者工具
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]