Google Chrome 浏览器中内置了一套强大的开发者工具,无论是做源码分析还是 JS 脚本的调试都是比较方便的。要进行抓包或者调试前端的加密脚本,和控制台的交互必不可少。不少做了安全防护的网站都会禁止用户打开 Chrome 开发者工具,下面总结所有 Chrome 控制台的打开方法。
Elements(元素) 面板,左侧显示页面源码的 DOM 树,可以在这里对页面源代码进行增、删、改、查等操作,右侧展示被选中页面节点的层叠样式表 (Cascading Style Sheets, CSS),它主要是用来对页面进行修饰美化。值得注意的是,左侧显示的页面源代码并非原始代码,而是和 HTML、CSS与 JS 结合的结果。 获取原始网页源代码,有以下两种方式:
如果想要隐藏网页中展示的节点,如一些容易误触的广告,只需要选中 Elements 面板中对应的代码节点后,按 H 键(也可以右键单击需要隐藏的节点,然后选择 Hide element)。日常开发中使用的组合键,如 Ctrl + C 键和 Ctrl + V 键等,也都可以在 Elements 面板中使用。也就是说,在 Elements 面板中可以对网页进行自由调整和编辑。
在日常的爬虫开发中,需要与源代码打交道的地方大多数是页面元素的定位。Chrome 开发者工具内置了一套定位工具,只需要在 Elements 面板中按 Ctrl + F 键,就会在源代码下方出现如下图所示的调试框,可以在其中编写 CSS 选择器语法或 Xpath 语法,实时地对页面节点进行定位。
如果要快速复制页面节点的路径,可以点击要定位的节点,再单击 Copy 菜单项,其中有多种可供选择的网页页面定位语法,如下图所示:
笔者其实不大喜欢这种方式,虽说从一定时间上能提升开发的效率,但更多时候其实也是需要自己修改的, 所以笔者更偏向自己写 Xpath 规则(更相信自己)。
此外,断点操作是进行代码调试和分析时的必要操作。 在 Elements 面板中可以进行 DOM 断点分析,右击页面任意节点,会发现 Break on 菜单项中有以下三种断点:
subtree modifications:在节点子树发生修改时断点。attribute modifications:在节点属性发生修改时断点。node removal:在节点被移除时发生断点。更具体的断点操作将在后续进行说明。
Console 面板是与网页进行交互的控制台窗口,它用于显示 DOM 对象信息和调试 JS 代码,熟练使用它将会大大提升开发速度。在 Console 面板中操作节点时,通常需要先定位到页面节点,才可以进行节点操作。输入 $0
可以对当前选中的页面节点进行引用,输入 $1
可以对上一次选择的节点进行引用,以此类推,输入可以一直回溯到 $4
。也可以使用 CSS 选择器语法对节点进行操作。复制需要定位的网页节点的 selector 路径后,使用 document. querySelector 或者 $
方法可以定位第一个符合语法的节点。如果要选择所有符合 CSS 选择器语法的节点,可以使用 document.querySelectorAll 或者 $
$
方法返回一个符合语法的节点数组。Console 面板中提供了多种方法来观察和检查事件监听器,常用的方法:monitorEvents():监听目标事件信息。unmonitorEvents():停止监听。getEventListeners():获取 DOM 节点的监听器。monitorEvents() 的第一个参数是要监听事件的对象,第二个参数是要监听的事件字符串或者字符串数组。以监听 CSDN 首页的“搜索”按钮为例,代码如下:
只要没有取消对目标节点的事件监听,在每次和页面交互时,控制台都输出监听信息。要停止监听事件,需要使用 unmonitorEvents() 方法,参数是要停止事件监听的页面节点,例如:
unmonitorEvents(searchBtn);
getEventListeners() 方法的参数是网页页面节点,它返回在节点上注册事件的监听器,其中会包含每个已经注册事件类型的数组。例如,以下代码会监听“搜索” 按钮已注册事件的监听器。
getEventListeners(searchBtn);
如果要查看 DOM 节点上注册的事件监听器,则需要到 Elements 面板中查看 EventListeners 选项卡,它会显示附加到页面上的所有事件,如下图所示:
Sources 面板是我们必须掌握的面板,对 JS 加密脚本的断点调试与代码分析主要从这里出发。如一个网页对登录参数做了加密,可以在加密 JS 脚本处设置断点,这样就可以跟进查看加密函数了。
1. 设置断点。 设置断点最基本的方法是在代码的行序列号上手动添加,也可以将其设置为在满足某些条件下才会触发断点。一旦在某一代码行上设置了断点,网页在加载到这一行代码时就会全局暂停,直到断点删除。要在特定的代码行上设置断点,需要打开 Source 面板,并在 File Navigator 窗口中选择要分析的脚本文件,在源代码的左侧可以看到行序列号,单击行序列号就会在这一行代码上添加断点,如下图所示:
如果一个表达式占据了多行,这时把一个断点设置在表达式中间,那么断点会被自动调整到下一个表达式上。如下图所示,在代码第684行设置断点,断点会自动调整到第692行。
条件断点只有在输人表达式为 true 时,才会被触发暂停。如图所示,右击行序列号,单击 Add conditional breakpoint 菜单项可以创建一个条件断点。
调试者在代码中添加的所有断点都会被记录在右侧的 Breakpoints 栏中。如果要删除一个断点,除了再次单击行序列号之外,还可以右击下图所示断点,选择 Remove breakpoint 菜单项。如果只是想暂时性地删除该断点,可以仅取消勾选复选框。
在 XHR 请求中设置断点的情况也很常见。当任何 XHR 与设置的 URL 中的子串相匹配时或者 XHR 到达生命周期的某个阶段时,这类断点会被触发。如果想在 XHR 与 URL 子串匹配时触发暂停,可以在 XHR/fetch Break points 窗格中进行 XHR 断点设置。如果想要在 XHR 生命周期的某个阶段触发暂停,可以在 Event Listener Breakpoints 窗格中查看 XHR 目录,如图所示:
2.调试代码。 设置好断点后就可以开始遍历代码了,可以通过一次执行一行代码或者一个函数来观察数据和页面的更改,也可以修改 JS 脚本及其中的数值。页面登录时的密码加密方式和判断参数正确的标志?都可以通过代码调试逐步找出来。代码调试通过 Sources 面板右上角的图标进行操纵,如图所示:
第一个图标的含义是,恢复代码执行直到遇到下一个断点,如果没有遇到断点,就会恢复正常;第二个图标的含义是,执行当前行的代码,并跳转到下一行;第三个图标的含义是,如果下一行代码包含一个函数调用,就跳转到该函数内部并在该函数的第一行暂停;第四个图标的含义是,执行当前函数的剩余部分,然后在函数调用后的下一个语句处暂停;第五个图标的含义是,暂时禁用所有断点,用于恢复完整的执行,而不是将断点全部删除;第六个图标的含义是,当异常发生时自动暂停代码。在实际的加密脚本调试中,需要将上述图标结合起来使用。除此之外,当脚本暂停在断点时,Scope 窗格会显示当前时刻所有定义在本地、闭包和全局的属性,如图所示:
仅在脚本暂停时,Scope 窗格才会有显示。当页面正常运行时,Scope 窗格是空白的。在进行断点时,Call Stack 窗格会显示代码的执行路径。如下图所示,它按照时间逆序,从上到下单击查看时,会自动跳转到对应代码块,这有助于调试者理解代码如何运行。
3.在任何页面上运行自定义代码块。 代码块是可以在 Sources 面板中创建和执行的小脚本,在任何页面都可以访问和运行。假设调试者有一个 JS 加密方法库,内置了多种常见的加密方法,在调试脚本时,如果要在多个页面中反复使用,就可以考虑将脚本另存为代码块。要创建一个代码块,需要打开 Sources 面板,单击左侧 Snippets 选项卡,右击空白处,选择 Create new snippet(或 New snippet) 选项,如图:
如果代码块编写后还未保存,文件名会出现下图所示的符号 “*”,需要按 Ctrl + S 键来进行保存。保存后的代码块要想在当前页面中使用,需要右击文件名,单击 Run 菜单项。
4.美化打印代码块。 一般来说,打开一个网页源代码或者脚本文件,会发现它是经过压缩的,观察起来比较困难,如下图所示:
单击源代码左下角的 {}
图标,可以进行代码的美化打印。
5.跟踪监视变量。 有时候需要持续监视脚本运行中某一个变量的值,如果一直在控制台进行调试输出会有些烦琐。Sources 面板右侧的 Watch 窗格提供了在程序中跟踪监视变量的功能,利用它可以不用反复地将监控对象输出到控制台中。要将变量添加到监控列表中,只需要单击 Watch 窗格中的 Add expression 图标(只有在 Watch 窗格展开时才会出现),如下图所示:
此时会打开一个内联输入框,输入要监控的变量名称,按 Enter 键,即可完成变量添加。如果要监控的变量没有被设置或未被找到,就会显示为下图中的 not available:
Network 面板会记录与网页有关的每个网络操作的详细信息,包括 HTTP 请求和 HTTP 响应。在该面板中,要掌握的是下图中标注的三个窗格,其中1号窗格用于控制 Network 面板的外观和功能,2号窗格用于过滤请求列表中的资源请求和响应,3号窗格列举了按照时间顺序存储的每个网络资源。
1号窗格中的 Preserve Log 复选框用于保存日志,Disable cache 复选框用于禁用缓存。单击3号窗格中的任意一个网络资源,可以查看该网络资源的更多详细信息,如下图所示,打开后默认显示 HTTP 请求头,包含统一资源定位符、HTTP 请求方法和状态码。
请求的详细内容后续笔者在另一篇博文中展开,这里主要讲解面板的使用。如果要对网络资源进行 Preview 预览,二进制图片资源会直接显示请求资源在页面中的展示,也可能不显示具体信息,具体情况取决于选择查看的资源类型。如果查看 HTTP 响应的具体内容,可以单击 Response 选项卡,如下图所示,HTML 资源会以源代码的方式呈现,具体返回内容取决于查看的资源类型。
Application 面板中可以查看和删除 Cookie,但是不能修改 Cookie 值。如下图所示,Cookie 会按照域列出,不过需要注意,来自不同域的 Cookie 可能会出现在同一栏中,相同的 Cookie 也可能会出现在多栏中。
使用 local storage 本地存储来存储键值对,可以在其中进行键值对的检查、修改和删除操作。常用的5种方法如下所示:
(1) setItem():存储一个名称为 key 的值 value,如果 key 存在,就更新value。
(2) getItem():获取名称为 key 的 value,如果 key 不存在,则返回 null。
(3) removeItem():删除名称为 key 的信息,这个 key 所对应的 value 也会全部被删除。
(4) clear():清空 localStorage 中所有信息。
(5) key():键的索引。
以设置键值对为例,创建一个 key 为 name、value 为 test 的键值对,可以在控制台输入如下代码:
localStorage.setItem("name", "test");
在掌握了 Chrome 开发者工具的基本使用后,需要将其运用到实际的调试中。在做日常的网页端数据抓包时,通常会遇到各类加密参数,如何快速定位加密脚本和关键函数极为重要。
在 Network 面板中找到了需要的资源包,当其中的 HTTP 内容中存在加密键值对时,可以使用搜索来快速定位加密脚本和关键函数。如下图所示,需要先单击右上角的展开图标,再单击 Search 菜单项,之后在下方的搜索框中输入要搜索的加密参数,最后单击搜索框右边的 Refresh 图标。如果 Sources 中存在这个加密参数,就会在下方返回符合条件的所有文本文件。
以全局搜索 safe 加密参数为例,因为通常情况下返回的匹配内容是较多的,可以在原有参数基础上再加一些标识符,例如:password=、 password:。这样可以大幅度减少匹配项,从而减轻寻找关键函数的负扭。
在 Network 面板中,通过资源的 Initiator 列可以看到它的请求调用栈,排序方式是逆序。如果将鼠标移动到某一个请求资源的 Initiator 上,会弹出该请求资源的请求调用堆栈。单击下图的请求调用堆栈中的任意显示项,即可跳转到对应的脚本文件的具体调用行中。
不少加密数据包传输的时候,会使用 XHR 请求断点。当目标加密参数存在于 XHR 数据包的时候,选择 XHR 请求断点会比全局搜索更加快捷,下图是添加 XHR 请求断点的联内输入框。
条件断点不仅可以写判断表达式,还可以在其中输入、输出表达式,这样就可以在脚本运行到对应行时,在控制台中打印输出对应参数,达到插桩的效果。如下图所示,添加条件断点时,可以输入 “console.log()” 对密码加密脚本中的密码值进行输出调试。
Console 插桩通常用于滑块验证码的调试,滑块轨迹的输出如果设置了一般断点,就会移动一次暂停一次,使用插桩形式就可以直接在控制台中流畅地输出滑动轨迹了。
在脚本中设置断点后,可以在当前断点暂停时,在 Console 面板中调试输出具体函数,单击函数内容可以跳转到具体的代码行,如下图所示:
在 Console 面板的调试中,通常需要将调试输出内容进行复制,方便将其写入本地文件进行调用。但一般情况下,直接复制往往得不到需要的内容,这个时候,可以在控制台中尝试以下四种方法,最后一种方法需要在 Sources 面板的 Snippets 代码块中添加 CryptoJS 加密库。(1) copy()、(2) JSON.stringify()、(3) Object.toString()、(4) CryptoJS.enc.Utf8.stringfy()。使用复制方法后,Console 面板的内容就会被复制到粘贴板上。
本地覆盖是一个实用的调试方法,能够使开发者用自己的文件来替换请求的资源。即不必再继续向服务器请求资源,而是直接在本地修改,当浏览器向目标地址请求资源时,会使用本地的文件来进行代替。这样,开发者可以随意对网页脚本文件进行修改,包括添加 Console 插桩、添加循环 debugger 以及实现脚本文件在被调用时,直接在控制台对加密参数或函数进行输出等操作。这里讲解 Chrome local override 和 Fiddler 自动响应两种方法。
Chrome 64 之后的开发者工具可以直接在 Sources 面板中进行操作,如下图所示,切换到 Overrides 选项卡中,单击 Select folder for overrides 选项,在弹出的文件夹选择框中选择要进行替换的资源的所属文件夹。
选择文件夹后,浏览器上方会弹出对话框询问是否允许 Chrome 开启访问目录权限,单击 “允许” 按钮。接下来,以替换百度首页图标为例进行说明。
(1) 打开 Network 面板进行抓包,找到百度首页图标的 HTTP 请求包。
(2) 右击对应请求资源,选择下图中的 Save for overrides 菜单项。
(3) 在 Overrides 中,将选择的文件夹下的图片直接拖动到中间区域。
(4) 再次刷新百度首页页面,会发现图标已经被替换。
除了在 Chrome 中完成本地覆盖,也可以在 Fiddler 抓包工具中实现相同效果,只需要进行如下三步操作: