网页调试之 debugger 原理与绕过
debugger 语句用于停止执行 JavaScript (以下简称 JS),并调用 (如果可用) 调试函数。
使用 debugger 语句类似于在代码中设置断点。
注意: 如果调试工具不可用,则调试语句将无法工作。
实现 debugger 功能
直接使用书写 debugger
1 | <!DOCTYPE |
当我们使用浏览器打开 Devtools 即执行 debugger;如下图所示
eval 配合 debugger
eval () 函数计算 JavaScript 字符串,并把它作为脚本代码来执行。
如果参数是一个表达式,eval () 函数将执行表达式。如果参数是 Javascript 语句,eval () 将执行 Javascript 语句。
1 | <!DOCTYPE html> |
当使用 eval 执行时,将会在虚拟机中执行,也就是说非同一作用域。
同时也由于
将字符串当作表达式来执行
,那么里面常常伴随着代码混淆
函数内执行 debugger
1 | <!DOCTYPE |
因为以上三种体现形式,在 debugger 上所设计的方案十分多。例如常见的无限制 debugger、配合 settimeout 延迟 debugger、代码混淆 + debugger 等等。
设置 debugger 的原理去对抗反爬,其核心原理就是如果调试工具可用,则调试语句将执行
. 也就是经常一打开就跳出 debugger。
无限 debugger,其实是一种泛指的概念,无限泛指多,而非真的无限
其基于 debugger 之上,在此加入多次执行 debugger 的语句从而实现 “无限 debugger”。“反正只要 chrome
Devtools 不开 debugger 便不会执行”.(经过调试是这样的,如果不准确请自行完善哦)
debugger 绕过原理
debugger 的绕过也很简单,我个人总结共有两种大的方向。它们分别是替换、掠过。其原理都是不让 debugger 执行。个人并不推荐新手使用替换法中的方法
- 替换法
- JS 注入
- 重写 (Hook)
- 掠过法
- Never pause here
- 条件断点
JS 注入
实现 js 注入的方式有很多,例如 chrome Devtools 的 overrides、fiddler autoresponse、 mitmproxy、Charles 的 map
local 等等。若有兴趣自行搜索其使用方式
Never pause here
找到 debugger 前面的行号,鼠标右键点击该行号,点击 Never pause here。便会跳过此断点
条件断点
找到 debugger 前面的行号,鼠标右键点击该行号,点击 Add conditional breakpoint,直接写 false。回车即可
Deactivate breakpoints
打开这个图标如下图所示(高亮为打开)
当遇见 breakpoints 时会执行一次断点,鼠标单击如下图标
即可直接跳过 breakpoints。
小技巧:Deactivate breakpoints 可以配合 xhr、dom、Script 等断点使用,便于调试
Hook 绕过
1 | function a() { |
1 | (function() { |
此方法有局限性,若在此函数 (在这里指函数 a) 若没有借用相关函数(eval),那么就无法使用此方法绕过
函数滞空法
当遇见断点时,回退一次堆栈。将对应函数滞空即可,例如遇见如下的 debugger
1 | function a() { |
直接在控制台输入如下内容即可。
此方法有局限性,若在此函数中还参杂了关键代码,将可能无法访问或调试等
总结
Debugger 绕过其实并不难,但在调试中仅仅是一道 “开胃菜”,本节总结了 debugger 的实现方式,以及触发机制。当然也总结了几种我已知的所有绕过方案。
展望
如何 hook “变量” debugger?如果可以实现那么就可以实现反调试的 debugger “通杀”,当然目前我也有在探究此方案。在加到 hook 函数中,那么调试便可以近似于一步到位。
v1.5.2