繁体   English   中英

以无法检测的方式检查 WebSocket 帧

[英]Inspecting WebSocket frames in an undetectable way

如何以页面无法检测到的方式读取 Chrome 扩展程序或 Firefox 附加组件中网页的 WebSocket 框架?

从 Chrome Dev Tools 扩展中检查 WebSockets 框架提出了一个类似的问题,但开发 NPAPI 插件不再有意义,因为它很快就会被删除。

拦截 WebSocket 数据很容易。 只需在页面构建 WebSocket 之前执行以下脚本即可。 此代码段对WebSocket构造函数进行了猴子修补:创建新的 WebSocket 构造函数时,该代码段订阅message事件,您可以从这里对数据执行任何您想做的操作。

此代码段旨在与本机代码无法区分,因此页面无法轻易检测到修改(但是,请参阅本文末尾的备注)。

(function() {
    var OrigWebSocket = window.WebSocket;
    var callWebSocket = OrigWebSocket.apply.bind(OrigWebSocket);
    var wsAddListener = OrigWebSocket.prototype.addEventListener;
    wsAddListener = wsAddListener.call.bind(wsAddListener);
    window.WebSocket = function WebSocket(url, protocols) {
        var ws;
        if (!(this instanceof WebSocket)) {
            // Called without 'new' (browsers will throw an error).
            ws = callWebSocket(this, arguments);
        } else if (arguments.length === 1) {
            ws = new OrigWebSocket(url);
        } else if (arguments.length >= 2) {
            ws = new OrigWebSocket(url, protocols);
        } else { // No arguments (browsers will throw an error)
            ws = new OrigWebSocket();
        }

        wsAddListener(ws, 'message', function(event) {
            // TODO: Do something with event.data (received data) if you wish.
        });
        return ws;
    }.bind();
    window.WebSocket.prototype = OrigWebSocket.prototype;
    window.WebSocket.prototype.constructor = window.WebSocket;

    var wsSend = OrigWebSocket.prototype.send;
    wsSend = wsSend.apply.bind(wsSend);
    OrigWebSocket.prototype.send = function(data) {
        // TODO: Do something with the sent data if you wish.
        return wsSend(this, arguments);
    };
})();

在 Chrome 扩展程序中,可以通过带有run_at:'document_start'内容脚本运行该代码段,请参阅使用内容脚本将代码插入页面上下文

Firefox 也支持content scripts ,同样的逻辑也适用(使用contentScriptWhen:'start' )。

注意:在页面的其余部分之前执行时,前面的代码段被设计为与本机代码无法区分。 检测这些修改的唯一(不寻常和脆弱的)方法是:

  • 将无效参数传递给 WebSocket 构造函数,捕获错误并检查依赖于实现的(特定于浏览器的)堆栈跟踪。 如果堆栈帧比平常多一个,则构造函数可能被篡改(从页面的角度来看)。

  • 序列化构造函数。 未修改的构造函数变成function WebSocket() { [native code] } ,而修补的构造函数看起来像function () { [native code] } (这个问题只存在于 Chrome 中;在 Firefox 中,序列化是相同的)。

  • 序列化WebSocket.prototype.send方法。 由于该函数未绑定,因此对其进行序列化( WebSocket.prototype.send.toString() )会揭示非本机实现。 这可以通过覆盖.send.toString方法来.send ,反过来可以通过与Function.prototype.toString的严格比较被页面检测到。 如果您不需要发送的数据,请不要覆盖OrigWebSocket.prototype.send

有一种替代 Rob W 的方法可以完全屏蔽与页面的任何交互(对于 Chrome

也就是说,您可以取出一些重炮并使用chrome.debugger

请注意,使用它会阻止您打开相关页面的开发工具(或者,更准确地说,打开开发工具将使其停止工作,因为只有一个调试器客户端可以连接)。 这已得到改进,因为:可以附加多个调试器。

这是一个非常低级的 API; 您需要自己使用调试器协议构建查询。 此外,相应的事件不在 1.1 文档中,您需要查看开发版本

您应该能够接收这些 WebSocket 事件并检查它们的payloadData

{"method":"Network.webSocketFrameSent","params":{"requestId":"3080.31","timestamp":18090.353684,"response":{"opcode":1,"mask":true,"payloadData":"Rock it with HTML5 WebSocket"}}}
{"method":"Network.webSocketFrameReceived","params":{"requestId":"3080.31","timestamp":18090.454617,"response":{"opcode":1,"mask":false,"payloadData":"Rock it with HTML5 WebSocket"}}}

此扩展示例应提供一个起点。

事实上,这是一个起点,假设tabId是您感兴趣的选项卡:

chrome.debugger.attach({tabId:tab.id}, "1.1", function() {
  chrome.debugger.sendCommand({tabId:tabId}, "Network.enable");
  chrome.debugger.onEvent.addListener(onEvent);
});

function onEvent(debuggeeId, message, params) {
  if (tabId != debuggeeId.tabId)
    return;

  if (message == "Network.webSocketFrameSent") {
    // do something with params.response.payloadData,
    //   it contains the data SENT
  } else if (message == "Network.webSocketFrameReceived") {
    // do something with params.response.payloadData,
    //   it contains the data RECEIVED
  }
}

我已经测试了这种方法(链接示例如上修改)并且它有效。

只是为@Xan 答案添加一个例外(我没有足够的代表对他的答案发表评论,因此我将其添加到此处,因为我相信它可以为其他人节省一些时间)。

如果 WebSocket 连接是在通过about:data:blob:方案加载的上下文中建立的,则该示例将不起作用。

有关相关错误,请参见此处: Attach debugger to worker from chrome devtools extension

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM