博客

通过 Devtools 协议使用 Chrome 拦截和修改响应

F5 缩略图
F5
2018 年 9 月 17 日发布


在 Shape 中我们遇到了许多粗略的 JavaScript 片段。 我们发现恶意注入页面的脚本,它们可能是由客户发送以寻求建议,或者我们的安全团队可能会在网络上找到似乎专门参考我们服务某些方面的资源。 作为我们日常工作的一部分,我们首先深入研究脚本以了解它们的作用和工作原理。 它们通常被最小化,经常被混淆,并且总是需要多层次的修改,然后才真正可以进行深入分析。

直到最近,进行这种分析的最简单方法是使用允许手动编辑的本地缓存设置或使用代理动态重写内容。 本地解决方案是最方便的,但是网站并不总是能完美地转换到其他环境中,并且它常常会导致人们陷入故障排除的困境,只是为了提高工作效率。 代理非常灵活,但通常很麻烦并且不具可移植性——每个人都有自己针对其环境的自定义设置,并且有些人对一个代理比另一个代理更熟悉。 我已经开始使用 Chrome 及其 devtools 协议,以便在发生请求和响应时挂接它们并动态修改它们。 它可移植到任何具有 Chrome 的平台,绕过一大堆问题,并能与常见的 JavaScript 工具很好地集成。 在这篇文章中,我将介绍如何使用Chrome 的 devtools 协议动态拦截和修改 JavaScript。

我们将使用节点,但只要您可以轻松访问 devtools 钩子,许多内容都可以移植到您选择的语言中。

首先,如果您从未探索过 Chrome 脚本,Eric Bidelman 撰写了一篇出色的Headless Chrome 入门指南。 那里的技巧适用于 Headless 和 GUI Chrome(我将在下一节中解决一个怪癖)。

启动 Chrome

我们将使用 npm 的chrome-launcher库来简化这一操作。

通过 Devtools 协议拦截和修改 Chrome 的响应

chrome-launcher可以精确地完成您所想的操作,并且您可以不加更改地传递从终端使用的相同命令行开关(此处维护了一个很棒的列表)。 我们将传递以下选项:

–窗口大小=1200,800

  • 自动将窗口大小设置为合理大小。

–auto-open-devtools-for-tabs

  • 自动打开 devtools 因为我们经常使用它们。

–用户数据目录=/tmp/chrome-testing

  • 设置恒定的用户数据目录。 (理想情况下我们不需要这个,但 Mac OSX 上的非无头模式似乎不允许您在没有此标志的情况下拦截请求。 如果您知道更好的方法,请通过Twitter告诉我!)
通过 Devtools 协议拦截和修改 Chrome 的响应

尝试运行脚本以确保您能够打开 Chrome。 你应该看到类似这样的内容:

通过 Devtools 协议拦截和修改 Chrome 的响应

使用 Chrome Devtools 协议

这也被称为“Chrome 调试器协议”,并且这两个术语似乎在 Google 的文档中可以互换使用。 首先,通过 npm 安装chrome-remote-interface包,它为我们提供了与 devtools 协议交互的便捷方法。 如果您想深入了解,请确保手边有协议文档

通过 Devtools 协议拦截和修改 Chrome 的响应

要使用 CDP,您需要连接到调试器端口,并且由于我们使用chrome-launcher库,因此可以通过chrome.port方便地访问。

通过 Devtools 协议拦截和修改 Chrome 的响应

协议中的许多域需要首先启用,我们将从运行时域开始,以便我们可以挂接到控制台 API 并将浏览器中的任何控制台调用传递到命令行。

通过 Devtools 协议拦截和修改 Chrome 的响应

现在,当您运行脚本时,您将获得一个功能齐全的 Chrome 窗口,该窗口还会将其所有控制台消息输出到您的终端。 这本身就很棒,特别是对于测试目的而言!

拦截请求

首先,我们需要通过向setRequestInterception提交一个RequestPatterns列表来注册我们想要拦截的内容。 您可以在“请求”阶段或“HeadersReceived”阶段进行拦截,并且要实际修改响应,我们需要等待“HeadersReceived”。 资源类型映射到您在 devtools 的网络窗格上常见的类型

不要忘记像上面的Runtime一样通过将Network.enable()添加到同一个数组来启用网络域。

通过 Devtools 协议拦截和修改 Chrome 的响应

注册事件处理程序相对简单,每个拦截的请求都带有一个interceptionId ,可用于查询有关请求的信息或最终发出延续。 在这里我们只是介入并记录我们拦截到终端的每个请求。

通过 Devtools 协议拦截和修改 Chrome 的响应

修改请求

要修改请求,我们需要安装一些对 base64 字符串进行编码和解码的辅助库。 有大量可用的库;请随意选择您自己的。 我们将使用atobbtoa

通过 Devtools 协议拦截和修改 Chrome 的响应

处理响应的 API 有点尴尬。 要处理响应,您需要在请求拦截中包含所有响应逻辑(而不是简单地拦截响应),然后必须通过拦截 ID 查询正文。这是因为在调用处理程序时正文可能不可用,这允许您明确等待您正在寻找的内容。 主体也可以采用 base64 编码,因此您需要在盲目传递它之前对其进行检查和解码。

通过 Devtools 协议拦截和修改 Chrome 的响应

此时您可以自由地使用 JavaScript。 您的代码将您置于响应的中间,使您既可以访问所请求的完整 JavaScript,也可以发回修改后的响应。 惊人的! 我们只需在 JS 末尾添加一个console.log来对其进行调整,这样当我们修改后的代码在浏览器中执行时,我们的终端就会收到一条消息。

通过 Devtools 协议拦截和修改 Chrome 的响应

我们不能简单地传递修改后的主体,因为其内容可能与原始资源一起发送的标头相冲突。 由于您正在积极地测试和调整,您可能希望先从基础开始,然后再过多担心需要传达的任何其他标题信息。 如果需要,您可以通过传递给事件处理程序的responseHeaders访问响应标头,但现在我们只需在数组中制作我们自己的最小集合,以便以后轻松操作和编辑。

通过 Devtools 协议拦截和修改 Chrome 的响应

发送新的响应需要制作完整的 base64 编码的 HTTP 响应(包括 HTTP 状态行),并通过传递给continueInterceptedRequest 的对象中的rawResponse属性发送它。

通过 Devtools 协议拦截和修改 Chrome 的响应

现在,当你执行脚本并在互联网上导航时,你会在终端中看到类似以下内容,因为你的脚本拦截了 JavaScript,并且当你修改后的 JavaScript 在浏览器中执行时, ​console.log()会通过我们在教程开始时创建的钩子冒泡。

通过 Devtools 协议拦截和修改 Chrome 的响应

基本示例的完整工作代码如下:

通过 Devtools 协议拦截和修改 Chrome 的响应

下一步该怎么做

您可以从漂亮地打印源代码开始,这始终是开始进行逆向工程的有效方法。 是的,当然,您可以在大多数现代浏览器中执行此操作,但您需要自己控制修改的每个步骤,以保持跨浏览器和浏览器版本的一致性,并能够在分析源代码时连接点。 当我深入研究外部的、模糊的代码时,我喜欢重命名变量和函数,因为我开始了解它们的用途。 安全地修改 JavaScript 并非易事,这本身就是一篇博客文章,但现在您可以使用类似unminify 的工具来撤消常见的缩小和混淆技术。

您可以通过 npm 安装unminify ,并通过调用unminify包装新的 JavaScript 主体以查看其运行情况:

通过 Devtools 协议拦截和修改 Chrome 的响应

我们将在下一篇文章中更深入地探讨这些转变。 如果您有任何问题、意见或其他巧妙的技巧,请通过Twitter 与我联系!