Tofu
Tofu

Reputation: 3613

Hooking Websockets from a Chrome Extension

I'm trying to create a Chrome extension content script which can hook the WebSocket of a specific page. Currently when the page is accessed, the content script injects another script into the page like so:

    var s = document.createElement('script');
    s.src = chrome.extension.getURL('Script.js');
    s.onload = function () {
        this.remove();
    };
    (document.head || document.documentElement).appendChild(s);

Then my Script.js contains an addEventListener for the websockets.

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

But when I run the extension I receive an error can not read property addEventListener of undefined. So I'm not entirely certain if that's the correct way to do it. I have verified that the script does get injected the page but how would I override or create my own listener on top of original onmessage WebSocket from the page. Is it possible?

Upvotes: 5

Views: 3699

Answers (3)

John
John

Reputation: 6268

WebSocket is a class. The addEventListener function is only available in an instance of the class. So you must find the variable the web page creates and add the listener to that. For example:

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

You would do something like:

mySocket.addEventListener("message", (e) => console.log(e));

However, this will miss all the initial messages and it depends on you being able to get access to the variable which may not be possible in all situations.

Instead, what you can do is override the class with your own class so that when they create a WebSocket instance, its actually your class.

An example of that would be something like:

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;

Keep in mind the above code is pseudo and untested.

If you just want to see the socket data. You can open chrome dev tools, click the "ws" filter. Then click on the web socket listed and view the raw data being transferred.

Here is a working sample that should work from your context script.

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);

Upvotes: 3

Chandan
Chandan

Reputation: 11797

You can use wshook which overrides the original WebSocket class and add callbacks which can be used for the task

You can directly copy the script wsHookjs and paste before your code or import like normal javascript and then start using it.

Import the library

<script src='wsHook.js'></script>

then you can just add callbacks for either before or after or both to intercept websocket send request:

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;
}

Note: Content Script can be loaded before page load. For more information about how to load content script before page load you can check this post

Upvotes: 1

Samir Abis
Samir Abis

Reputation: 36

Could you try with this?

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

Upvotes: 0

Related Questions