从Chrome扩展程序中Hook Websockets

5
我正在尝试创建一个Chrome扩展程序的内容脚本,可以钩取特定页面的WebSocket。当前,在访问页面时,内容脚本会将另一个脚本注入到页面中,如下所示:
    var s = document.createElement('script');
    s.src = chrome.extension.getURL('Script.js');
    s.onload = function () {
        this.remove();
    };
    (document.head || document.documentElement).appendChild(s);

然后我的Script.js文件包含一个用于websockets的addEventListener事件监听器。

Window.WebSocket.addEventListener("message",function(event){
console.log("Websocket read");
})

但是当我运行扩展时,我收到一个错误信息,无法读取未定义的属性addEventListener。因此,我不确定这是否是正确的方法。我已经验证了脚本确实被注入到页面中,但是如何在原始onmessage WebSocket的基础上覆盖或创建自己的监听器呢?这可能吗?


请使用 onmessage,例如 window.WebSocket.onmessage = function(event) { console.debug("WebSocket message received:", event); }; - Arun Kumar
你能够实现这个吗? - deepg
我不知道,你有解决方案吗? - Irelia
1
你在哪里定义了 Window.WebSocket?我不是 Chrome 扩展程序的专家,但我认为这是错误的。在 JavaScript 中,你应该首先使用 new 关键字创建 WebSocket 的实例。 - pouyan
1
这个回答解决了你的问题吗?https://dev59.com/XWAh5IYBdhLWcg3wDfuy - skara9
显示剩余10条评论
3个回答

3

WebSocket 是一个类。而 addEventListener 函数只能在该类的实例中使用。因此,您必须找到网页创建的变量并将监听器添加到其中。例如:

var mySocket = new WebSocket("wss://127.0.0.1:8080");

您可以这样做:
mySocket.addEventListener("message", (e) => console.log(e));

然而,这种方法会忽略所有初始消息,并且它取决于您能否访问变量,这在某些情况下可能不可能。相反,您可以使用自己的类覆盖该类,以便当他们创建一个WebSocket实例时,实际上是您的类。例如:
class MyWebSocket extends WebSocket
{
    __constructor()
    {
        super(...arguments);
    }

    addEventListener(name, callback)
    {
        // call the original event listener
        super.addEventListener(name, function()
        {
            console.log("Im sneakily listening in here", arguments);

            // call their callback
            callback(...arguments);
        });
    }
}

// Override the real web socket with your version
window.WebSocket = MyWebSocket;

请注意,上面的代码是伪代码且未经测试。

如果您只想查看套接字数据,请打开Chrome开发工具,单击“ws”过滤器。然后单击列出的Web套接字并查看正在传输的原始数据。

以下是应该可以从您的上下文脚本中正常工作的示例。

while(document == null || document.head == null);

var s = document.createElement("script");

function hookWebSocket()
{
    window.RealWebSocket = window.WebSocket;

    window.WebSocket = function()
    {
        var socket = new window.RealWebSocket(...arguments);

        socket.addEventListener("message", (e) =>
        {
            console.log("The message is: ", e);
        });

        return socket;
    }
}

s.innerHTML = `
    ${hookWebSocket.toString()}

    hookWebSocket();
`;

document.head.prepend(s);

是的,我知道如果找到变量是可能的,但对于每个依赖它的可能网站找到websocket变量并不是一个通用的解决方案。这就是为什么我希望有一种内部挂钩的方法。但我认为覆盖WebSocket类以便我的类被实例化而不是原始类可能是唯一的解决方案。 - Irelia
@Akali 我为你添加了一个可用的示例。 - John
那么这个操作本质上是将脚本标签附加或插入到文档头部,然后从那里挂接WebSocket? - Irelia
@Akali,这就是中了! - John

1
您可以使用wshook,该工具覆盖了原始的WebSocket类,并添加了回调函数,可用于任务。您可以直接复制wsHookjs脚本并在代码之前粘贴,或像普通JavaScript一样导入,然后开始使用它。请导入该库。
<script src='wsHook.js'></script>

然后,您可以添加回调函数来拦截 WebSocket 发送请求的前、后或两者都要:

wsHook.before = function(data, url, wsObject) {
    console.log("Sending message to " + url + " : " + data);
}

// Make sure your program calls `wsClient.onmessage` event handler somewhere.
wsHook.after = function(messageEvent, url, wsObject) {
    console.log("Received message from " + url + " : " + messageEvent.data);
    return messageEvent;
}

注意:内容脚本可以在页面加载之前加载。有关如何在页面加载之前加载内容脚本的更多信息,您可以查看此帖子

@Akali 你可以在页面加载之前注入脚本,如果想了解如何在页面加载之前加载内容脚本,请查看这里 - Chandan
你是在暗示我要注入整个wsHook.js文件吗? - Irelia
@Akali 是的,那么页面将使用包装的 WebSocket,该包装器将调用前后回调。 - Chandan
我在我的content.js中添加了wsHookjs的内容,以及before和after,并在我的manifest.json中添加了run at:document_start,同时赋予它在特定网站上运行的权限,但仍然无法钩取websocket。 - Irelia
@John,这正是我所想的。那么,run_at:document_start然后尝试注入脚本会起作用吗?我可以尝试一下。 - Irelia
显示剩余8条评论

0

你能试试这个吗?

s.onload = function () {
  this.parentNode.removeChild(this);
};

脚本的注入部分运行良好。 - Irelia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接