CWSS

Complete WebSocket Sniffer

目前為 2022-01-16 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/438408/1009004/CWSS.js

// ==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