目录
- 一、打开Chrome DevTools的三种方式
- 二、Elements元素面板
- 三、Console控制台面板
- 四、Sources面板
- 五、Network面板
- 六、Application面板
- 七、逆向调试技巧
- 7.1 善用搜索
- 7.2 查看请求调用堆栈
- 7.3 XHR 请求断点
- 7.4 Console 插桩
- 7.5 堆内存函数调用
- 7.6 复制Console面板输出
工欲善其事,必先利其器。本文先来基于 Chrome 浏览器介绍浏览器开发者工具的使用,但由于开发者工具的功能十分复杂,本文主要介绍对 JavaScript 逆向有一些帮助的功能,学会了这些,我们在做 JavaScript 逆向调试的过程中会更加得心应手。参考文档:https://developer.chrome.com/docs/devtools?hl=zh-cn 有空的话可以通篇瞅瞅
一、打开Chrome DevTools的三种方式
Google Chrome 浏览器中内置了一套强大的开发者工具,无论是做源码分析还是 JS 脚本的调试都是比较方便的。要进行抓包或者调试前端的加密脚本,和控制台的交互必不可少。不少做了安全防护的网站都会禁止用户打开 Chrome 开发者工具 如:https://wap.jjwxc.net/book2/545120/4,下面总结打开 Chrome DevTools 的三种方式:
-
可以通过 Chrome 中的下拉菜单访问 Chrome DevTools,如下图所示:
-
如需打开上次使用的开发者工具面板,请点击地址栏右侧的
┇
按钮,然后依次选择更多工具 > 开发者工具。
-
使用以下快捷方式打开面板:元素、控制台或上一个面板
操作系统 元素 控制台 上次使用的面板 Windows 或 Linux Ctrl + Shift + C Ctrl + Shift + J F12/Ctrl + Shift + I Mac Cmd + Option + C Cmd + Option + J Fn + F12/Cmd + Option + I
遇到该类型的网站 https://wap.jjwxc.net/book2/545120/4 ,我们可以在浏览器中先打开新的标签页,然后使用上述任意方法打开 Chrome DevTools,接着在地址栏中输入要调试的网址,回车,这样就能正常使用 Chrome DevTools 进行调试啦。打开 Chrome DevTools 之后,我们可以看到很多面板标签,如 Elements、Console、Sources 等,这就是 Chrome DevTools 的一个个面板,功能丰富而又强大。下面先对面板进行简单介绍:
- Elements:元素面板,用于查看或修改当前网页 HTML 节点的属性、CSS 属性、监听事件等。HTML 和 CSS 都可以即时修改和即时显示。
- Console:控制台面板,用于查看调试日志或异常信息。另外,我们还可以在控制台输入 JavaScript 代码,方便调试。
- Sources:源代码面板,用于查看页面的 HTML 文件源代码、JavaScript 源代码、CSS 源代码。此外,还可以在此面板对 JavaScript 代码进行调试,比如添加和修改 JavaScript 断点,观察 JavaScript 变量变化等。
- Network:网络面板,用于查看页面加载过程中的各个网络请求,包括请求、响应等。
- Performance:性能面板,用于记录和分析页面在运行时的所有活动,比如 CPU 占用情况、呈现页面的性能分析结果。
- Memory:内存面板,用于记录和分析页面占用内存的情况,如查看内存占用变化,查看 JavaScript 对象和 HTML 节点的内存分配。
- Application:应用面板,用于记录网站加载的所有资源信息,如存储、缓存、字体、图片等,同时也可以对一些资源进行修改和删除。
- Lighthouse:审核面板,用于分析网络应用和网页,收集现代性能指标并提供对开发人员最佳实践的意见。 参考链接:https://github.com/GoogleChrome/lighthouse/blob/HEAD/docs/user-flows.md
Ctrl + Shift + C/Cmd + Option + C 可在
检查器模式下打开 Elements 面板。当将鼠标悬停在页面上的元素上时,此模式会显示有用的提示。还可以点击任何元素,在 Elements(元素)> Styles(样式)标签页中查看其 CSS。
Device Mode (设备模式):可以设置设备模式。一般而言对我们作用不大,现在的网站手机页面写的不是响应式,而是用一个新域名做指向 m.********.com,所以利用这个方案不太能看得见手机端的样式了,开发者还是很常用的,但是我们用不到。
简单了解了这些面板之后,我们来深入了解几个面板中对 JavaScript 调试很有帮助的功能。
二、Elements元素面板
Elements(元素) 面板,左侧显示页面源码的 DOM 树,可以在这里对页面源代码进行增、删、改、查等操作,右侧展示被选中页面节点的层叠样式表 (Cascading Style Sheets,CSS),它主要是用来对页面进行修饰美化。值得注意的是,左侧显示的页面源代码并非原始代码,而是HTML、CSS与JS结合的结果。
获取原始网页源代码,有以下两种方式:
(1) 右击任意网页页面空白处,单击查看网页源代码菜单项,或者按 Ctrl + U 组合键,如下图所示:
(2) 切换到 Sources 面板,选择左栏中包含网页地址的 HTML 文件。如下图所示:
如果想要隐藏网页中展示的节点,如一些容易误触的广告,只需要选中 Elements 面板中对应的代码节点后,按 H 键(也可以右键单击需要隐藏的节点,然后选择 Hide element)。日常开发中使用的组合键,如 Ctrl + C 键和 Ctrl + V 键等,也都可以在 Elements 面板中使用。也就是说,在 Elements 面板中可以对网页进行自由调整和编辑。
在日常的爬虫开发中,需要与源代码打交道的地方大多数是页面元素的定位。Chrome 开发者工具内置了一套定位工具,只需要在 Elements 面板中按 Ctrl + F 键,就会在源代码下方出现如下图所示的调试框,可以在其中编写 CSS 选择器语法或 Xpath 语法,实时地对页面节点进行定位。
如果要快速复制页面节点的路径,可以点击要定位的节点,再单击 Copy 菜单项,其中有多种可供选择的网页页面定位语法,如下图所示:
笔者其实不大喜欢这种方式,虽说从一定时间上能提升开发的效率,但更多时候其实也是需要自己修改的, 所以笔者更偏向自己写 Xpath 规则(更相信自己)。此外,断点操作是进行代码调试和分析时的必要操作。 在 Elements 面板中可以进行 DOM 更改断点分析,如果想要暂停更改 DOM 节点或其子级的代码,可以使用 DOM 更改断点。如需设置 DOM 更改断点,请执行以下操作:
① 点击元素标签页
② 前往要设置断点的元素。
③ 右键点击相应元素。
④ 将鼠标悬停在断点 (Break on) 上,然后选择子树修改 (Subtree configuration)、属性修改 (Attribute modified) 或节点移除。用于创建 DOM 更改断点的上下文菜单。
我们可以在以下位置找到 DOM 更改断点列表:元素 > DOM 断点窗格。Sources > DOM Breakpoints 侧窗格。"Elements"
和 "Sources"
面板中的 DOM 断点列表:
我们可以在其中执行以下操作:
- 使用 复选框,启用或停用它们。
- 右键点击 > 在 DOM 中移除或显示这些元素。
DOM 更改断点的类型:
- 子树修改:在移除或添加当前所选节点的子级,或更改子级内容时触发。在子节点属性更改或对当前所选节点进行任何更改时不会触发。
- 属性修改:在当前所选节点上添加或移除属性,或属性值发生变化时触发。
- 节点移除:在移除当前选定的节点时会触发。
三、Console控制台面板
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 选项卡,它会显示附加到页面上的所有事件,如下图所示:
其他,从左至右:
- 弹出消息统计,清空消息栏,选择控制台线程-也可以粗略的理解为 iframe(不同框架内变量有差异)
- 小眼睛:创造一个表达式,用来做观察对比用
- 过滤器:消息过滤,一般情况用不到,但是遇到控制台一直吐无用消息的时候很好用
- 默认等级:显示消息等级,一般不用
- Issues:一般不用管
- 控制台右键:清理
参考链接:https://developer.chrome.com/docs/devtools/console?hl=zh-cn
四、Sources面板
左侧:
- page 页面加载的所有内容,包含各种样式。只要是本页面加载的,都可以在这里面找到
- 本地覆盖是一个实用的调试方法,能够使开发者用自己的文件来替换请求的资源。即不必再继续向服务器请求资源,而是直接在本地修改,当浏览器向目标地址请求资源时,会使用本地的文件来进行代替。这样,开发者可以随意对网页脚本文件进行修改,包括添加 Console 插桩、添加循环 debugger 以及实现脚本文件在被调用时,直接在控制台对加密参数或函数进行输出等操作。这里讲解 Chrome local overrides 的方式。Chrome 64 之后的开发者工具可以直接在 Sources 面板中进行操作,如下图所示,切换到 Overrides 选项卡中,单击 Select folder for overrides 选项,在弹出的文件夹选择框中选择要进行替换的资源的所属文件夹。
选择文件夹后,浏览器上方会弹出对话框询问是否允许 Chrome 开启访问目录权限,单击"允许"
按钮。
接下来,以替换百度首页 title 标签中的文字为例进行说明。(1) 打开 Network 面板进行抓包,找到百度首页请求包。(2) 右击对应请求包,选择下图中的 Override content 菜单项。
(3) 选择 Override content 菜单项之后,会自动跳转到 Sources 面板中的 Overrides,如下图所示:
(4) 再次刷新百度首页页面,会发现页面中的内容已经被替换。
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 面板右上角的图标进行操纵,如下图所示:
第一个图标的含义是,恢复代码执行直到遇到下一个断点,如果没有遇到断点,就会恢复正常;第二个图标的含义是,执行当前行的代码,并跳转到下一行;第三个图标的含义是,如果下一行代码包含一个函数调用,就跳转到该函数内部并在该函数的第一行暂停;第四个图标的含义是,执行当前函数的剩余部分,然后在函数调用后的下一个语句处暂停;第五个图标的含义是单步调试,可以用来调试异步代码,比如 Promise;第六个图标的含义是,暂时禁用所有断点,用于恢复完整的执行,而不是将断点全部删除;第七个图标的含义是,当异常发生时自动暂停代码。在实际的加密脚本调试中,需要将上述图标结合起来使用。除此之外,当脚本暂停在断点时,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面板
Network 面板会记录与网页有关的每个网络操作的详细信息,包括 HTTP 请求和 HTTP 响应。在该面板中,要掌握的是下图中标注的三个窗格,其中1号窗格用于控制 Network 面板的外观和功能,2号窗格用于过滤请求列表中的资源请求和响应,3号窗格列举了按照时间顺序存储的每个网络资源。
从左至右:
- 红色圆点:开启/关闭流量监控
- 圈叉:清空当前的捕获数据
- 漏斗:过滤器面板是否开启
- 放大镜:搜索
- Preserve log:Preserve Log 复选框用于保存日志
- Disable cache:Disable cache 复选框用于禁用缓存
- throttling:开发用,用于模拟网络环境不好的时候,加载页面情况
- network conditions:配置一些请求信息,主要是 UA(这个不好用,有更好用的 chrome 插件)
- 上传下载:不用说了,保存这些缓存到本地或者从本地上传到 chrome
抓包面板:
- Name:名字
- Status:状态码
- Type:加载形式
- Initiator:可以理解这个数据的来源位置,如果是 js 代码发出的,可以看到发出的调用栈
- Size:大小
- Time:加载时间
- Waterfall:加载时间轴
面板右键:会有一些操作, 主要常用的是 copy curl 用于一键生成爬虫代码,如下图所示:
单击3号窗格中的任意一个网络资源,可以查看该网络资源的更多详细信息,如下图所示,打开后默认显示 HTTP 请求头,包含统一资源定位符、HTTP 请求方法和状态码。
请求的详细内容后续笔者在另一篇博文中展开,这里主要讲解面板的使用。如果要对网络资源进行 Preview 预览,二进制图片资源会直接显示请求资源在页面中的展示,也可能不显示具体信息,具体情况取决于选择查看的资源类型。如果查看 HTTP 响应的具体内容,可以单击 Response 选项卡,如下图所示,HTML 资源会以源代码的方式呈现,具体返回内容取决于查看的资源类型。
六、Application面板
Application 面板中可以查看和删除 Cookie,但是不能修改 Cookie 值。如下图所示,Cookie 会按照域列出,不过需要注意,来自不同域的 Cookie 可能会出现在同一栏中,相同的 Cookie 也可能会出现在多栏中。
Manifest: https://web.dev/add-manifest/?utm_source=devtools
Storage 下面的数据库面板汇总,一般用于一键清除所有数据:
- Local Storage: 相当于浏览器里针对这个网页的一个数据库
- Session Storage: 同上
- indexedDB: 同上,但是没见人用过 https://developer.chrome.com/docs/devtools/storage/indexeddb/?utm_source=devtools
- webSQL:用来查询 Chrome 数据库的,支持 sql 语言,https://developer.chrome.com/docs/devtools/storage/websql/?utm_source=devtools
Cookies:
- Domain:指定 Cookie 的有效域,决定在向该域发送请求时是否携带此 Cookie
- path:指定 Cookie 的有效路径
- Expires/Max-age:过期时间
- HttpOnly:用于避免 Cookie 被 Javascript 访问
- Secure Cookie 的安全属性,用于指定 Cookie 需要通过安全 Socket 层连接传递。若设置为 true,则浏览器只会在 HTTPS 和 SSL 等安全协议中传输此 Cookie
- SameSite 用来限制第三方 Cookie,从而减少安全风险。它有3个属性,分别是:① Strict 完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie;② Lax 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外 ③ None 网站可以选择显式关闭 SameSite 属性,将其设为 None。不过,前提是必须同时设置 Secure 属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
- Priority 优先级,Chrome 的提案,定义了三种优先级,Low/Medium/High,当 Cookie 数量超出时,低优先级的 Cookie 会被优先清除
使用 local storage 本地存储来存储键值对,可以在其中进行键值对的检查、修改和删除操作。常用的5种方法如下所示:
setItem(): 存储一个名称为 key 的值 value,如果 key 存在,就更新value。
getItem(): 获取名称为 key 的 value,如果 key 不存在,则返回 null。
removeItem(): 删除名称为 key 的信息,这个 key 所对应的 value 也会全部被删除。
clear(): 清空 localStorage 中所有信息。
key(): 键的索引。
以设置键值对为例,创建一个 key 为 name、value 为 test 的键值对,可以在控制台输入如下代码:
localStorage.setItem("name", "test");
七、逆向调试技巧
在掌握了 Chrome 开发者工具的基本使用后,需要将其运用到实际的调试中。在做日常的网页端数据抓包时,通常会遇到各类加密参数,如何快速定位加密脚本和关键函数极为重要。
7.1 善用搜索
在 Network 面板中找到了需要的资源包,当其中的 HTTP 内容中存在加密键值对时,可以使用搜索来快速定位加密脚本和关键函数。如下图所示,需要先单击右上角的展开图标,再单击 Search 菜单项,之后在下方的搜索框中输入要搜索的加密参数,最后单击搜索框右边的 Refresh 图标。如果 Sources 中存在这个加密参数,就会在下方返回符合条件的所有文本文件。
以全局搜索 safe 加密参数为例,因为通常情况下返回的匹配内容是较多的,可以在原有参数基础上再加一些标识符,例如:password=、 password:。这样可以大幅度减少匹配项,从而减轻寻找关键函数的负扭。
7.2 查看请求调用堆栈
在 Network 面板中,通过资源的 Initiator 列可以看到它的请求调用栈,排序方式是逆序。如果将鼠标移动到某一个请求资源的 Initiator 上,会弹出该请求资源的请求调用堆栈。单击下图的请求调用堆栈中的任意显示项,即可跳转到对应的脚本文件的具体调用行中。
7.3 XHR 请求断点
不少加密数据包传输的时候,会使用 XHR 请求断点。当目标加密参数存在于 XHR 数据包的时候,选择 XHR 请求断点会比全局搜索更加快捷,下图是添加 XHR 请求断点的联内输入框。具体细节可以查看文章:https://blog.csdn.net/xw1680/article/details/124605393
7.4 Console 插桩
条件断点不仅可以写判断表达式,还可以在其中输入、输出表达式,这样就可以在脚本运行到对应行时,在控制台中打印输出对应参数,达到插桩的效果。如下图所示,添加条件断点时,可以输入 console.log()
对密码加密脚本中的密码值进行输出调试。
Console 插桩通常用于滑块验证码的调试,滑块轨迹的输出如果设置了一般断点,就会移动一次暂停一次,使用插桩形式就可以直接在控制台中流畅地输出滑动轨迹了。
7.5 堆内存函数调用
在脚本中设置断点后,可以在当前断点暂停时,在 Console 面板中调试输出具体函数,单击函数内容可以跳转到具体的代码行,如下图所示:
7.6 复制Console面板输出
在 Console 面板的调试中,通常需要将调试输出内容进行复制,方便将其写入本地文件进行调用。但一般情况下,直接复制往往得不到需要的内容,这个时候,可以在控制台中尝试以下四种方法,最后一种方法需要在 Sources 面板的 Snippets 代码块中添加 CryptoJS 加密库。(1) copy()、(2) JSON.stringify()、(3) Object.toString()、(4) CryptoJS.enc.Utf8.stringfy()。使用复制方法后,Console 面板的内容就会被复制到粘贴板上。