浏览器调试与常用 Hook
Hook 相关内容参考视频 https://www.bilibili.com/video/BV1gQ4mzMEA4/
断点
根据不同情况,可选用不同断点
dom断点
通过 ajax 请求拿到数据后,渲染到页面中时可通过 dom 断点定位页面元素被修改的代码
Elements tap ->指定标签右键 -> Break on(发生中断的条件)
script断点
读取到脚本时暂停 Event Listener Breakpoints 事件监听器断点 -> Scripts 脚本
xhr断点
发送指定请求时的断点,可通过请求地址暂停到指定请求之前
源码断点
源码断点常用的是以下几种
普通代码断点:蓝色
条件断点:橙色。右键添加条件断点。运行到条件断点处,会执行设置的内容。当设置内容返回为true时中断
编写条件,返回true时断下
- 参数数量为 N 时断下
arguments.callee.length === N - window 上绑定变量用于控制是否启动断点后,启动断点
window.enableBreakpoints === true
- 参数数量为 N 时断下
可以编写普通代码(无返回值代码,如Statement),则会执行而不中断(代码不返回true时,不会触发断点机制)
Eg1: 修改断点行的参数值
var id = 1; var a = id; // 此行加断点,编写 id = 999,则此时id被修改为999,a会被赋值id的当前值,即999Eg2: 计时
for (let i = 1; i < 9999999; i++) { // 断点内容 console.time('calc time') window.getComputedStyle(document.body) } // 断点内容 console.timeEnd('calc time')
日志断点:粉色。自动在控制台输出断点所设置的内容(监控当前代码位置,某个变量的值,即使当前代码为使用该变量)。通常设置为字符串拼接变量,例如
"当前i的值为" + i
调用堆栈
在调试时,常看的调用堆栈信息分为 Network 数据包的 Initiator 启动器 和代码断点的 Call Stack 调用栈
两个地方堆栈显示都遵循了上方是栈顶的策略。即最下方的是先触发的,最上方的是最后调用的
禁控制台
开源项目 disable-devtool 会检测控制台情况并触发以下行为
- 禁用 F12 与右键,减少控制台打开的方式
- 检测到控制台打开后,调用
window.close()关闭当前标签页 - 检测到控制台打开后,跳转到其他安全页面
- 检测到控制台打开后,反复使用
console.clear()清空控制台输出 - 检测到控制台打开后,反复使用
console.table()控制台输出大量内容 - 检测到控制台打开后,反复使用
window.history.go()与window.history.back()进行跳转 - etc(其他行为可详见项目说明)
处理手段
- hook
window.close()为空函数,防止自动关闭 - hook
window.onbeforeunload = () => {debugger;return false}, 在堆栈中定位跳转代码,移除跳转代码并 overrides - hook
console.clear()为空函数,防止清楚控制台输出 - hook
console.table() - hook
window.history的go和back是空方法
过 debugger
debugger 干扰调试通过与定时器配合实现反复触发 debugger 但不影响主进程执行业务逻辑
常规的 debugger 可通过断点不暂停过掉。
出现一些debugger无法通过断点不暂停过掉的情况,如: 使用 Function 构造 debugger
// 通过自定义函数构造器,调用 Function 构造 debugger 并调用
func.constructor('debugger').call()
// 通过匿名函数
(function(){}.constructor('debugger').call())
可在发生 debugger 时查看堆栈,定位触发 debugger 的代码(函数),在执行代码块前下断点。刷新后重写该代码,再放行
或通过 hook 方式过 debugger (修改内置可能会被检测)。以下是 hook 代码
- 重写定时器
_setInterval = setInterval setInterval = function(a, b){ // 判断无 debugger 关键字,再正常设置定时器 if (a.toSting().indexOf('debugger') === -1) { return _setInterval(a, b) } } - 重写构造器
_constructor = Function.prototype.constructor Function.prototype.constructor = function() { if(arguments[0] === 'debugger') { // 什么都不做 } else { return _constructor.apply(this, arguments) } }