// ==UserScript==
// @license MIT
// @name CWSS
// @version 1.1
// @description Complete WebSocket Sniffer
// @author 0vC4
// @match http://*/*
// @match https://*/*
// @grant none
// @run-at document-start
// @namespace https://greasyfork.org/users/670183
// ==/UserScript==
/* usage:
// priority => (event) -> Infinity .. -Infinity -> listener
// "this" instance of "WebSocket" for all functions in "hook"
hook {
priority: Number,
init?: Function(),
send?: Function(data),
open?: Function(event),
message?: Function(event),
close?: Function(event),
}
// @return CWSS
CWSS.setHook(hook);
CWSS.setHooks([hook, hook, ...] | hook, hook, ...);
CWSS.sockets; // [WebSocket, WebSocket, ...];
*/
const CWSS = (() => {
const _hooks_ = 'hooks'; // all hooks WebSocket.prototype[_hooks_]
const eventKey = key => key+'_listener'; // all event listeners WebSocket.prototype[eventKey('open'|'message'|'close')]
const listeners = ['open', 'message', 'close']; // (+init&send hooks available by default)
const proto = window.WebSocket.prototype;
const def = Object.defineProperty;
const hidden = (obj, key, value) => def(obj, key, {configurable: true, value});
const rebase = (obj, key, list) => def(obj, key, {
configurable: true,
enumerable: true,
set: func => list.push(func)
});
const native = (obj, value) => {
obj.toString = function(){return Function.toString.call(value, ...arguments);};
};
const sockets = [];
const hooks = (() => {
if (_hooks_ in proto) return proto[_hooks_];
hidden(proto, _hooks_, []);
const hooks = proto[_hooks_];
const {send, addEventListener} = proto;
const pipe = (type, ...next) => function() {
for (const hook of hooks.sort((a, b) => b.priority - a.priority)) {
if (hook[type]) arguments = hook[type].call(this, ...arguments);
if (!arguments) return;
}
next.flat().forEach(func => func.call(this, ...arguments));
};
proto.send = pipe('send', send);
native(proto.send, send);
proto.addEventListener = function() {
const type = arguments[0];
const func = arguments[1];
const list = this[eventKey(type)];
if (list) list.push(func);
else addEventListener.call(this, ...arguments);
}
native(proto.addEventListener, addEventListener);
const Ows = window.WebSocket;
window.WebSocket = function() {
const ws = new Ows(...arguments);
sockets.push(ws);
pipe('init').call(ws);
for(const key of listeners) {
const list_key = eventKey(key);
const list = hidden(ws, list_key, [])[list_key];
addEventListener.call(ws, key, pipe(key, list));
rebase(ws, 'on'+key, list);
}
return ws;
}
for(const k in Ows) if (k != 'prototype') window.WebSocket[k] = Ows[k];
for(const k in Ows.prototype) if (k != 'constructor') try {window.WebSocket.prototype[k] = Ows.prototype[k];} catch(e) {};
WebSocket.prototype[_hooks_] = Ows.prototype[_hooks_];
native(window.WebSocket, Ows);
return hooks;
})();
const root = {
sockets,
setHook(hook) {
hooks.push(hook);
return root;
},
setHooks(..._hooks) {
hooks.push(..._hooks.flat());
return root;
}
};
return root;
})();
// example
CWSS.setHook({
priority: 9,
init() {
console.log(`Open WebSocket channel by url: ${this.url}`, this);
return arguments;
},
message(e) {
console.log(`Got data:`, e.data);
return arguments;
},
send(data) {
console.log(`Sending data:`, data);
return arguments;
},
});
// 0vC4#7152