跳到主要内容

Web代码怎么追溯变量和函数方法

纯理论的知识再多也记不住,这块后面需要建立靶场作业测试。

先说说为什么需要动态调试,因为很多时候,你在谷歌开发者工具的后台,看不到一些JS文件的,因为经常的会有这么一些情况发生,比如执行到某些地方,JS才会下载下来,然后互相使用对方的变量或者函数等。

关于调试工具方面的选择,网上有很多工具,其实只需要用两个工具即可,一个是谷歌开发者工具,一个是火狐开发者工具,谷歌开发者工具是比较常用的,但是火狐开发者工具也是很常用的,因为火狐开发者工具可以模拟一些环境更方便一些。还有就是有一些IE类的环境,这个时候就只能下载IE的浏览器了。因此,工具的选择选谷歌开发者工具就行了。

方法

JS断点调试,即是在浏览器开发者工具中为JS代码添加断点,让JS执行到某一特定位置停住,方便开发者对该处代码段的分析与逻辑处理。为了能够观察到断点调试的效果,我们预先随意准备一段JS代码:

XSS攻击方案

代码很简单,就是定义一个函数,传入两个数,分别加上一个乱七八糟的随机整数后,再返回两个数的总和。以Chrome开发者工具为例,我们来看一下JS断点调试的基本方法。

Sources断点,测试代码中我们通过上图console的输出结果可以看出代码应该是正常运行了,但是为什么是应该呢?因为函数中加了一个随机数,而最终结果是否真的是正确的呢?这是毫无意义的猜想,但是假设我现在就是要验证一下:函数传入的两个数、被加的随机数,以及最终的总和。

XSS攻击方案

验证过程存在很明显的弊端就是,添加了很多冗余代码,接下来我们看一下使用断点进行验证,是否更加方便,先看一个如何加断点,以及断点后是什么效果

XSS攻击方案

给一段代码添加断点的流程是"F12(Ctrl + Shift + I)打开开发工具"——"点击Sources菜单"——"左侧树中找到相应文件"——"点击行号列"即完成在当前行添加/删除断点操作。当断点添加完毕后,刷新页面JS执行到断点位置停住,在Sources界面会看到当前作用域中所有变量和值,只需对每个值进行验证即可完成我们题设验证要求。

执行到断点的时候,显示的变量a和b的值是已经进行过加法运算后的,我们看不到调用sum函数时初始传入的10和20。那么该怎么办呢?这就要回过头来先学习一下断点调试的一些基础知识了。我们打开Sources面板后其实会在界面中看到如下内容,我们跟着鼠标轨迹逐一看看都是什么意思:

XSS攻击方案

从左到右,各个图标表示的功能分别为:

  • Pause/Resume script execution:暂停/恢复脚本执行(程序执行到下一断点停止)。
  • Step over next function call:执行到下一步的函数调用(跳到下一行)。
  • Step into next function call:进入当前函数。
  • Step out of current function:跳出当前执行函数。
  • Deactive/Active all breakpoints:关闭/开启所有断点(不会取消)。
  • Pause on exceptions:异常情况自动断点设置。

各个变量的变化情况如下图所示:

XSS攻击方案

其余几个功能键,改动一下代码

XSS攻击方案

Debugger断点

具体的说就是通过在代码中添加"debugger;"语句,当代码执行到该语句的时候就会自动断点。接下去的操作就跟在Sources面板添加断点调试几乎一模一样,唯一的区别在于调试完后需要删除该语句。

Debugger断点

DOM断点调试

DOM断点,顾名思义就是在DOM元素上添加断点,进而达到调试的目的。而在实际使用中断点的效果最终还是落地到JS逻辑之内。我们依次来看一下每一种DOM断点的具体效果。

当节点内部子节点变化时断点

在前端开发越来越复杂的今天,前端JS代码越来越多,逻辑越来越复杂,一个看似简单的Web页面,通常伴随着大段大段的JS代码,涉及诸多DOM节点增、删、改的操作。难免遇到直接通过JS代码很难定位代码段的情况,而我们却可以通过开发者工具的Elements面板,快速定位到相关DOM节点,这时候通过DOM断点定位脚本就显得尤其重要了。

当节点内部子节点变化时断点

当节点属性发生变化时断点

由于前端处理的业务逻辑越来越复杂,对一些数据的存储依赖越来越强烈,而将临时数据存储于DOM节点的(自定义)属性中,是很多情况下开发者优先选择的方式。特别是在HTML5标准增强自定义属性支持(例:dataset、data-*之类)之后,属性设置应用越来越多,因此Chrome开发者工具也提供了属性变化断点支持,其效果大致如下

当节点属性发生变化时断点

对子节点的属性进行任何操作也不会触发节点本身的断点。

当节点被移除时断点

同上,没什么区别

XHR也有断点

也就是常说的Ajax。

XHR也有断点

我们可以通过"XHR Breakpoints"右侧的"+"号为异步断点添加断点条件,当异步请求触发时的URL满足此条件,JS逻辑则会自动产生断点。演示动画中并没有演示到断点位置,这是因为,演示使用的是jQuery封装好的ajax方法,代码已经过压缩,看不到什么效果,而事实上XHR断点的产生位置是"xhr.send()"语句。

XHR断点的强大之处是可以自定义断点规则,这就意味着我们可以针对某一批、某一个,乃至所有异步请求进行断点设置,非常强大。但是,似乎这个功能在日常开发中用得并不多,至少我用得不多。想想原因大概有两点:其一,这类型的断点调试需求在日常业务中本身涉及不多;其二,现阶段的前端开发大多基于JS框架进行,最基本的axios也已经对Ajax进行了良好封装,极少有人自己封装Ajax方法,而项目为了减少代码体积,通常选择压缩后的代码库,使得XHR断点跟踪相对不那么容易了。

Event Listener断点(比较重要)

事件监听器断点,即根据事件名称进行断点设置。当事件被触发时,断点到事件绑定的位置。事件监听器断点,列出了所有页面及脚本事件,包括:鼠标、键盘、动画、定时器、XHR等等。极大的降低了事件方面业务逻辑的调试难度。

Event Listener断点

JS代码的追溯

到此,上面的把工具的各个地方都详细的介绍了,那么JS追溯就很容易了自然而然会了。

这个,还有一个方案是这样的,可以保证所有地方都能用,不用管他有多少的加密混淆,都能一直找到JS变量,就是在开发者工具里面写这样的代码。

let obj = {
name: 1
};

Object.defineProperty(obj , "name", {
set: function(value) {
debugger;
}
});

还可以把get也写进去,具体可以参考defineProperty这个的用法。

这样每次name被设置的时候,都会进入断点。

defineProperty参考: https://www.cnblogs.com/qinlinkun/p/18028406