目录
1、开发者工具介绍
2、查看节点事件
3、断点调试
4、观察调用栈
5、恢复 JavaScript 执行
6、Ajax 断点
7、改写 JavaScript 文件
1、开发者工具介绍
由于需要学习 JavaScript 逆向,所以此文主要介绍与 JavaScript 逆向有帮助的功能。
以下链接为例:Scrape | Movie
右键 --> 检查,打开开发者工具,结果如下图所示:
这里可以看到多个面板标签,如 Elements 、 Console 、 Sources 等,这就是开发者工具的一个个面板,功能丰富而又强大。下面先对面板进行简单介绍。
- Elements :元素面板,用于查看或修改当前网页 HTML 节点的属性、 CSS 属性、监听事件等。 HTML 和 CSS 都可以即时修改和即时显示。
- Console :控制台面板,用于查看调试日志或异常信息。另外,我们还可以在控制台输入JavaScript 代码,方便调试。
- Sources :源代码面板,用于查看页面的 HTML 文件源代码、 JavaScript 源代码、 CSS 源代码。此外,还可以在此面板对 JavaScript 代码进行调试,比如添加和修改 JavaScript 断点,观察 JavaScript 变量变化等。
- Network :网络面板,用于查看页面加载过程中的各个网络请求,包括请求、响应等。
- Performance :性能面板,用于记录和分析页面在运行时的所有活动,比如 CPU 占用情况、呈现页面的性能分析结果。
- Memory :内存面板,用于记录和分析页面占用内存的情况,如查看内存占用变化,查看 JavaScript 对象和 HTML 节点的内存分配。
- Application :应用面板,用于记录网站加载的所有资源信息,如存储、缓存、字体、图片等,同时也可以对一些资源进行修改和删除。
- Lighthouse :审核面板,用于分析网络应用和网页,收集现代性能指标并提供对开发人员最佳实践的意见。(>>中)
了解了这些面板之后,我们来深入了解几个面板中对 JavaScript 调试很有帮助的功能。
2、查看节点事件
这里以进入下一页的节点为例,点击图中步骤 1 即可查看节点,步骤 2 查看鼠标指向节点,如下图:
步骤 2 过后,如下图:
右侧的 Styles 可以看到对应节点的 CSS 样式,可在这里自行增删样式,实时预览效果。
Computed 选项卡中,可以看到当前节点的盒子模型,可以看到当前节点最终计算出的 CSS 样式等,如下图:
接下来,切换到右侧的 Event Listeners (事件监听器)选项卡,这里可以显示各个节点当前已经绑定的事件 ,都是 JavaScripl 原生支持的,下面简单列举几个事件。
- click :用户点击 HTML 元素时会触发的事件。
- keydown :用户按下键盘按键时会触发的事件。
- mousedown:用户按下鼠标按钮时触发事件。可以监听这个事件来捕获鼠标按下的操作。
- mouseup:用户松开鼠标按钮时触发事件。可以监听这个事件来捕获鼠标松开的操作。
- change : HTML 元素改变时会触发的事件。
- mouseover :用户在一个 HTML 元素上移动鼠标时会触发的事件。
- mouseout :用户从一个 HTML 元素上移开鼠标时会触发的事件。
- load :浏览器完成页面加载时会触发的事件。
通常,按钮都会绑定一个点击事件,它的处理逻辑一般是由 JavaScript 定义的,这样在我们点击按钮的时候,对应的 JavaScript 代码便会执行。比如在下图中,我们切换到 第 2 页面的节点,右侧 Event Listeners 选项卡下会看到它的绑定事件。
这里有对应事件的代码位置,内容为一个 JavaScript 文件名称 chunk-vendors.77daf991.js,然后紧跟一个冒号,接着跟了一个数字 7 。所以对应的事件处理函数是定义在 chunk-vendors.77daf991.js 这个文件的第 7 行。点击这个代码位置,便会自动跳转到 Sources 面板,打开对应的 chunk-vendors.77daf991.js 文件并跳转到对应的位置,如下图所示:
如果显示的代码可读性很差(上述图片中是优化了的结果),点击上述图片中的 { }就能优化代码。
我们在 JavaScipt 逆向过程中利用好 Event Listeners ,可以轻松找到各个节点绑定事件的处理方法所在位置,帮助找到一些突破口。
3、断点调试
接下来,我们介绍一个非常重要的功能——断点调试。在调试代码的时候,我们可以在需要的位置上打断点,当对应事件触发时,浏览器就会自动停在断点的位置等待调试,此时我们可以选择单步调试,在面板中观察调用栈、变量值,以更好地追踪对应位置的执行逻辑。
那么断点怎么打呢?我们接着以上面的例子来说。首先单击上图红线的下一行代码,会出现如下图效果:
这时候行号处就出现了一个蓝色的箭头,这就证明断点已经添加好了,同时在右侧的 Breakpoints 选项卡下会出现我们添加的断点的列表。
由于我们知道这个断点是用来处理翻页按钮的点击事件的,所以可以在网页里面点击按钮试一下,比如点击第 2 页的按钮,这时候就会发现断点被触发了,如下图所示:
这时候我们可以看到页面中显示了一个叫作 Paused in debugger 的提示,这说明浏览器执行到刚才我们设置断点的位置处就不再继续执行了,等待我们发号施令执行调试。
此时代码停在了该行,回调参数 e 就是对应的点击事件 PointerEvent 。在右侧的 Scope 面板处,可以观察到各个变量的值,比如在 Local 域下有当前方法的局部变量,我们可以在这里看到 PointerEvent 的各个属性,如下图所示:
另外,我们关注到有一个方法 o ,它在 Jr 方法下面,所以切换到 Closure(Jr) 域,可以查看它的定义及其接收的函数,如下图所示:
我们可以看到, Functionlocation 又指向了方法 o ,点击之后便又可以跳到指定位置,用同样的
方式进行断点调试即可。
在 Scope 面板还有多个域,这里就不再展开介绍了。总之,通过 Scope 面板,我们可以看到当前执行环境下变量的值和方法的定义,知道当前代码究竟执行了怎样的逻辑。
接下来,切换到 Watch 面板,在这里可以自行添加想要查看的变量和方法。点击右上角的+按钮。我们可以任意添加想要监听的对象,如下图所示:
假如这里我们比较关注 o._wrapper 方法,于是点击添加 o._wrapper ,这里就会把对应的方法定义呈现出来,展开之后再点击 FunctionLocation 定位其源码位置。
我们还可以切换到 Console 面板,输入任意的 JavaScript 代码,此时便会执行、输出对应的结果,比如输入 arguments[0] ,此时便会输出对应的结果,如下图所示:
我们还可以选择单步调试,这里有三个重要的按钮,如下图所示:
这3个按钮都可以做单步调试,但功能不同。
- 第一个 Step Over Next Function Call:逐语句执行,跳过下一个函数调用。当遇到函数调用时,它会直接执行该函数并跳过函数内部的逐步执行,直接移到函数调用后的下一行代码。这对于不想深入函数内部执行细节,而是只关注函数调用的结果的情况非常有用。
- 第二个 Step Into Next Function Call:逐步进入下一个函数调用。这意味着如果当前行包含函数调用,使用Step Into会跳转到该函数内部并逐步执行函数内部的代码。
- 第三个 Step Out of Current Function:跳出当前方法。
用得较多的是第一个,相当于逐行调试,即跳转至下一行。(若点击多次Step Over Next Function Call后调用栈中没有方法了,且Step Over Next Function Call点击不了可能是因为执行的函数中没有其他函数调用或者调用的函数已经执行完毕,或者当前代码处于异步执行状态,比如正在等待某个异步操作的结果,这时候该按钮可能会被禁用,因为执行下一行代码可能会导致未知的行为。或者当前代码处于某个循环或递归中,如果想继续执行代码,可以尝试使用Step Into Next Function Call)
4、观察调用栈
在调试的过程中,有时候也会直接跳转至非下一行的新位置。那究竟是如何跳转过来的了?比如我们点击 Step Into Next Function Call,跳转到了一个叫作 nt 的方法中,如下图:
在 Call Stack中我们可以看到全部的调用过程,直接点击其中的方法,就可以跳转到对应的代码位置。
有时候调用栈是非常有用的,可以回溯某个逻辑的执行流程,从而快速找到突破口。
5、恢复 JavaScript 执行
在调试过程中,如果想快速跳到下一个断点或者让 JavaScript 代码运行下去,可以点击 Resume script execution 按钮,即上图三个按钮旁边的蓝色三角形按钮。
6、Ajax 断点
上面我们介绍了一些DOM 节点的监听器(Listener),通过监听器我们可以手动设置断点并进行调试。但其实针对这个例子,通过翻页的点击事件监听器是不太容易找到突破口的。
接下来我们再介绍一个方法——Ajax断点,它可以在发生 Ajax 请求的时候触发断点。对于这个例子,我们的目标其实就是找到Ajax 请求的那一部分逻辑,找出加密参数是怎么构造的。可以想到,通过Ajax断点,使页面在获取数据的时候停下来,我们就可以顺着找到构造Ajax请求的逻辑了。怎么设置呢?
我们把之前的断点全部取消,切换到Sources 面板下,然后展开 XHR/fetch Breakpoints,这里就可以设置Ajax断点,如下图所示:
要设置断点,就要先观察 Ajax 请求。和之前一样,我们点击翻页按钮 2,在Network面板里面观察Ajax 请求是怎样的,请求的 URL 如图11-23所示。
可以看到,URL里面包含 /api/movie 这样的内容,所以我们可以在刚才的 XHR/fetch Breakpoints 面板中添加拦截规则。点击+按钮,可以看到一行 Break when URL contains: 的提示,意思是当Ajax 请求的 URL 包含填写的内容时,会进入断点停止,这里可以填写 /api/movie,如下图所示:
这时候再点击翻页按钮 3 ,触发第 3 页的 Ajax 请求,会发现点击后页面走到断点停下来了,如下图:
看一下右侧的代码,发现它停到了 Ajax 最后发送的那个时候,即底层的 XMLHttpRequest 的 send 方法,可是似乎还是找不到 Ajax 请求是怎么构造的。前面我们讲过 Call Stack ,通过它可以顺着找到前序调用逻辑,所以顺着它一层层找,也可以找到构造 Ajax 请求的逻辑,最后会找到一个叫作 onFetchData 的方法,如下图:
接下来,切换到 onFetchData 方法并将代码格式化(右下角{ },下图是以优化的代码),可以看到如下图所示的调用方法:
可以发现,这里可能使用 axios 库发起了一个 Ajax 请求,还有 limit 、 offset 、 token 这3个参数,基本就能确定了,顺利找到突破口!我们就不在此展开分析了。
因此在某些情况下,我们可以比较容易地通过 Ajax 断点找到分析的突破口,这是一个常见的寻找 JavaScript 逆向突破口的方法。
要取消断点也很简单,只需要在 XHR / fetch Breakpoints 面板取消勾选即可。
7、改写 JavaScript 文件
我们知道,一个网页里面的 JavaScript 是从对应服务器上下载下来并在浏览器执行的。有时候,我们可能想要在调试的过程中对 JavaScript 做一些更改,比如说有以下需求。
- 发现 JavaScript 文件中包含很多阻挠调试的代码或者无效代码、干扰代码,想要将其删除。
- 调试到某处,想要加一行 console.log 输出一些内容,以便观察某个变量或方法在页面加载过程中的调用情况。在某些情况下,这种方法比打断点调试更方便。
- 调试过程遇到某个局部变量或方法,想要把它赋值给 window对象以便全局可以访问或调用。
- 在调试的时候,得到的某个变量中可能包含一些关键的结果,想要加一些逻辑将这些结果转发到对应的目标服务器。
这时候我们可以使用浏览器的 Overrides 功能,它在 Sources 面板左侧,如下图所示:
下面来使用一下 Overrides 功能。
首先,根据前面设置 Ajax 断点的方法,找到对应的构造 Ajax 请求的位置,根据一些网页开发知识,我们可以大体判断出 then 后面的回调方法接收的参数 a 中就包含了 Ajax 请求的结果,如下图所示:
点击 Overrides ,点击+按钮,这时候浏览器会提示我们选择一个本地文件夹,用于存储要替换的 JavaScript 文件。这里我选定了一个新建的文件夹 ChromeOverrides ,注意这时候可能会遇到如下图所示的提示,如果没有问题,直接点击"允许"即可。
这时,在 Overrides 面板下就多了一个 ChromeOverrides 文件夹,用于存储所有我们想要的 JavaScript 文件,如下图:
我们可以看到,现在所在的 JavaScript 选项卡是 chunk-10192a00.243cb8b7.js,可以直接在浏览器中修改的,添加一行代码 console.log()(输出内容,该行代码是为了在 Ajax 请求成功获得响应的时候,在控制台输出响应的结果。) ,修改如下:
then((function(a) {
console.log('response', a) // 添加一行代码
var e = a.data
, s = e.results
, n = e.count;
t.loading = !1,
t.movies = s,
t.total = n
}
写入时,会弹出提示,直接按照提示输入即可,输入完毕后保存,之后切换回 Overrides 面板,就可以发现成功生成了新的 JavaScript 文件,它用于替换原有的 JavaScript 文件,如下图:
此时我们取消所有断点,然后刷新页面,就可以在控制台看到输出的响应结果了,如下图所示:
正如我们所料,我们成功将变量 a 输出,其中的 data 字段就是 Ajax 的响应结果,证明改写 JavaScript 成功!而且刷新页面也不会丢失了。
我们还可以增加一些 JavaScript 逻辑,比如直接将变量 a 的结果通过 API 发送到远程服务器,并通过服务器将数据保存下来,也就完成了直接拦截 Ajax 请求并保存数据的过程了。
修改 JavaScript 文件有很多用途,此方案可以为我们进行 JavaScript 逆向带来极大的便利。
文章到此结束,本人新手,若有错误,欢迎指正;若有疑问,欢迎讨论。若文章对你有用,点个小赞鼓励一下,谢谢大家,一起加油吧!