wasm parser

used to modify wasm

Versão de: 28/11/2022. Veja: a última versão.

Este script não deve ser instalado diretamente. Este script é uma biblioteca de outros scripts para incluir com o diretório meta // @require https://update.greasyfork.org/scripts/455550/1122221/wasm%20parser.js

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         wasm parser
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  used to modify wasm
// @author       bismuth
// @match        https://diep.io/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=diep.io
// @grant        none
// ==/UserScript==
const OP = {"unreachable":0,"nop":1,"block":2,"loop":3,"if":4,"else":5,"end":11,"br":12,"br_if":13,"br_table":13,"return":15,"call":16,"call_indirect":17,"drop":26,"select":27,"local":{"get":32,"set":33,"tee":34},"global":{"get":35,"set":36},"i32":{"load":40,"load8_s":44,"load8_u":45,"load16_s":46,"load16_u":47,"store":54,"store8":58,"store16":59,"const":65,"eqz":69,"eq":70,"ne":71,"lt_s":72,"lt_u":73,"gt_s":74,"gt_u":75,"le_s":76,"le_u":77,"ge_s":78,"ge_u":79,"clz":103,"ctz":104,"popcnt":105,"add":106,"sub":107,"mul":108,"div_s":109,"div_u":110,"rem_s":111,"rem_u":112,"and":113,"or":114,"xor":115,"shl":116,"shr_s":117,"shr_u":118,"rotl":119,"rotr":120,"wrap_i64":167,"wrap_f32_s":168,"wrap_f32_u":169,"wrap_f64_s":170,"wrap_f64_u":171,"reinterpret_f32":188},"i64":{"load":41,"load8_s":48,"load8_u":49,"load16_s":50,"load16_u":51,"load32_s":52,"load32_u":53,"store":55,"store8":60,"store16":61,"store32":62,"const":66,"eqz":80,"eq":81,"ne":82,"lt_s":83,"lt_u":84,"gt_s":85,"gt_u":86,"le_s":87,"le_u":88,"ge_s":89,"ge_u":90,"clz":121,"ctz":122,"popcnt":123,"add":124,"sub":125,"mul":126,"div_s":127,"div_u":128,"rem_s":129,"rem_u":130,"and":131,"or":132,"xor":133,"shl":134,"shr_s":135,"shr_u":136,"rotl":137,"rotr":138,"extend_i32_s":172,"extend_i32_u":173,"trunc_f32_s":174,"trunc_f32_u":175,"trunc_f64_s":176,"trunc_f64_u":177,"reinterpret_f64":189},"f32":{"load":42,"store":56,"const":67,"eq":75,"ne":76,"lt":77,"gt":78,"le":79,"ge":80,"abd":139,"neg":140,"ceil":141,"floor":142,"trunc":143,"nearest":144,"sqrt":145,"add":146,"sub":147,"mul":148,"div":149,"min":150,"max":151,"copysign":152,"convert_i32_s":178,"convert_i32_u":179,"convert_i64_s":180,"convert_i64_u":181,"demote_f64":182,"reinterpret_i32":190},"f64":{"load":43,"store":57,"const":68,"eq":97,"ne":98,"lt":99,"gt":100,"le":101,"ge":102,"abd":153,"neg":154,"ceil":155,"floor":156,"trunc":157,"nearest":158,"sqrt":159,"add":160,"sub":161,"mul":162,"div":163,"min":164,"max":165,"copysign":166,"convert_i32_s":183,"convert_i32_u":184,"convert_i64_s":185,"convert_i64_u":186,"promote_f32":187,"reinterpret_i64":191},"memory":{"size":63,"grow":64}};
class WASMSection {
    constructor(desc,length) {
        this.section = desc;
        this.body = new Array(length);
    }
}
class WASMParser {
    constructor(bin) {
        this.lexer = new Reader(new Uint8Array(bin));
        this.sections = new Array(13);
        this.adjustImports = 0;
        this.importFuncCount = 0;
        this.parseWASM();
    }
    read(bin) { this.lexer.packet = new Uint8Array(bin) }
    loadFunc(index) {
        this.lexer.set(this.sections[10].body[index - this.importFuncCount]);
        const localLength = this.lexer.vu();
        for (let n = 0; n < localLength; n++) {
            this.lexer.vu();
            this.lexer.u8();
        }
        return;
    }
    set(index, val) {
        this.sections[10].body[index - this.importFuncCount] = val;
    }
    getAdjusted(index) {
        if (index < this.importFuncCount) return index;
        return index + this.adjustImports;
    }
    addImportEntry(options) {
        const map = ['f64','f32','i64','i32'];
        switch(options.type) {
            case 'func':
                this.sections[2].body.push({
                    name: options.name,
                    type: "func",
                    index: this.sections[1].body.length
                });
                this.sections[1].body.push({
                    param: options.params,
                    return: options.returns
                });
                this.adjustImports++;
                return this.sections[2].body.length - 1;
                break;
            default:
                throw new Error('oops, not supported yet');
                break;
        }
    }
    reindex() {
        let section = this.sections[10].body;
        let length = section.length;
        for (let n = 0; n < length; n++) this.sections[10].body[n] = this.parseFunction(section[n]);
        section = this.sections[9].body;
        length = section.length;
        for (let n = 0; n < length; n++) {
            const l = section[n].funcs.length;
            for (let p = 0; p < l; p++) this.sections[9].body[n].funcs[p] = this.getAdjusted(section[n].funcs[p]);
        }
        section = this.sections[7].body;
        length = section.length;
        for (let n = 0; n < length; n++) this.sections[7].body[n].index = this.getAdjusted(section[n].index);
        this.adjustImports = 0;
    }
    compile() {
        const bin = [0, 97, 115, 109, 1, 0, 0, 0];
        for (let n = 0; n < 12; n++) {
            if (!this.sections[n]) continue;
            const section = this[`compileSection0x${n.toString(16)}`]();
            bin.push(n);
            bin.push(...Writer.vu(section.length));
            for (const byte of section) bin.push(byte);
        }
        return (this.lexer.packet = new Uint8Array(bin));
    }
    compileSection0x1() {
        const map = ['f64','f32','i64','i32'];
        const section = this.sections[1].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            bin.push(0x60);
            bin.push(...Writer.vu(section[n].param.length));
            for (const param of section[n].param) bin.push(map.indexOf(param) + 0x7C);
            bin.push(...Writer.vu(section[n].return.length));
            for (const param of section[n].return) bin.push(map.indexOf(param) + 0x7C);
        }
        return bin;
    }
    compileSection0x2() {
        const map = ['func','table','mem','global'];
        const section = this.sections[2].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            const nameSplit = section[n].name.split('.');
            for (const part of nameSplit) bin.push(...Writer.stringLEN(part));
            bin.push(map.indexOf(section[n].type));
            bin.push(...Writer.vu(section[n].index));
            //console.log(bin);
        }
        return bin;
    }
    compileSection0x3() {
        const section = this.sections[3].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) bin.push(...Writer.vu(section[n]));
        return bin;
    }
    compileSection0x4() {
        const section = this.sections[4].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) for (let p = 0; p < 4; p++) bin.push(...Writer.vu(section[n][p]));
        return bin;
    }
    compileSection0x5() {
        const section = this.sections[5].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            bin.push(...Writer.vu(section[n].type));
            bin.push(...Writer.vu(section[n].limit[0]));
            bin.push(...Writer.vu(section[n].limit[1]));
        }
        return bin;
    }
    compileSection0x6() {
        const map = ['f64','f32','i64','i32'];
        const section = this.sections[6].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            bin.push(map.indexOf(section[n].type) + 0x7C);
            bin.push(section[n].mutable);
            for (const expr of section[n].expr) bin.push(...Writer.vu(expr));
            bin.push(11);
        }
        return bin;
    }
    compileSection0x7() {
        const map = ['func','table','mem','global'];
        const section = this.sections[7].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            bin.push(...Writer.stringLEN(section[n].name));
            bin.push(map.indexOf(section[n].type));
            bin.push(...Writer.vu(section[n].index));
        }
        return bin;
    }
    compileSection0x8() {
        const section = this.sections[8].body;
        const length = 1;
        const bin = [1];
        bin.push(...Writer.vu(section));
        return bin;
    }
    compileSection0x9() {
        const section = this.sections[9].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            bin.push(section[n].type, section[n].expr[0]);
            bin.push(...Writer.vu(section[n].expr[1]),11);
            bin.push(...Writer.vu(section[n].funcs.length));
            for (const funcIdx of section[n].funcs) bin.push(...Writer.vu(funcIdx));
        }
        return bin;
    }
    compileSection0xa() {
        const section = this.sections[10].body;
        const length = section.length;
        const bin = Writer.vu(length);
        for (let n = 0; n < length; n++) {
            //section[n] = this.parseFunction(section[n]);
            bin.push(...Writer.vu(section[n].length));
            for (const byte of section[n]) bin.push(byte);
        }
        return bin;
    }
    compileSection0xb() {
        return this.sections[11].body;
    }
    parseWASM() {
        this.lexer.index = 8;
        while (this.lexer.has()) {
            const id = this.lexer.u8();
            if (id > 12) return;
            this[`parseSection0x${id.toString(16)}`]();
        }
        this.importFuncCount = this.sections[2].body.filter(({type}) => type === 'func').length;
    }
    parseSection0x1() {
        const map = ['f64','f32','i64','i32'];
        const rawLength = this.lexer.vu();
        const section = new WASMSection('functypes', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) {
            const type = { param: [], return: [] }
            if (this.lexer.u8() !== 0x60) break;
            let len = this.lexer.vu();
            for (let n = 0; n < len; n++) type.param.push(map[this.lexer.u8()-0x7C]);
            len = this.lexer.vu();
            for (let n = 0; n < len; n++) type.return.push(map[this.lexer.u8()-0x7C]);
            section.body[n] = type;
        }
        return (this.sections[1] = section);
    }
    parseSection0x2() {
        const map = ['func','table','mem','global'];
        this.lexer.vu();
        const section = new WASMSection('imports', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) section.body[n] = { name: this.lexer.stringLEN() + '.' + this.lexer.stringLEN(), type: map[this.lexer.u8()], index: this.lexer.vu() };
        return (this.sections[2] = section);
    }
    parseSection0x3() {
        this.lexer.vu();
        const section = new WASMSection('functions', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) section.body[n] = this.lexer.vu();
        return (this.sections[3] = section);
    }
    parseSection0x4() {
        this.lexer.vu();
        const section = new WASMSection('tables', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) section.body[n] = [this.lexer.vu(), this.lexer.vu(), this.lexer.vu(), this.lexer.vu()]; //incomplete
        return (this.sections[4] = section);
    }
    parseSection0x5() {
        this.lexer.vu();
        const section = new WASMSection('mem', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) section.body[n] = { type: this.lexer.vu(), limit: [this.lexer.vu(), this.lexer.vu()] }
        return (this.sections[5] = section);
    }
    parseSection0x6() {
        const map = ['f64','f32','i64','i32'];
        this.lexer.vu();
        const section = new WASMSection('globals', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) {
            section.body[n] = { type: map[this.lexer.u8()-0x7C], mutable: this.lexer.u8(), expr: [] }
            section.body[n].expr.push(this.lexer.ru8());
            switch(this.lexer.u8()) {
                case OP.i32.const:
                case OP.i64.const:
                    section.body[n].expr.push(this.lexer.vu());
                    break;
                case OP.f32.const:
                    section.body[n].expr.push(this.f32());
                    break;
                case OP.f64.const:
                    section.body[n].expr.push(this.f64());
                    break;
            }
            this.lexer.u8();
        }
        return (this.sections[6] = section);
    }
    parseSection0x7() {
        const map = ['func','table','mem','global'];
        this.lexer.vu();
        const section = new WASMSection('exports', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) {
            const name = this.lexer.stringLEN();
            const type = map[this.lexer.u8()];
            const index = this.lexer.vu();
            section.body[n] = { name, type, index };
        }
        return (this.sections[7] = section);
    }
    parseSection0x8() {
        this.lexer.vu();
        const section = new WASMSection('start', this.lexer.vu());
        section.body = this.vu();
        return (this.sections[8] = section);
    }
    parseSection0x9() {
        this.lexer.vu();
        const section = new WASMSection('elements', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) {
            section.body[n] = { type: this.lexer.u8() }; //NEED TO ACCOUNT FOR DIFFERENT TYPES
            section.body[n].expr = [this.lexer.ru8()];
            switch(this.lexer.u8()) {
                case OP.i32.const:
                case OP.i64.const:
                    section.body[n].expr.push(this.lexer.vu());
                    break;
                case OP.f32.const:
                    section.body[n].expr.push(this.lexer.f32());
                    break;
                case OP.f64.const:
                    section.body[n].expr.push(this.lexer.f64());
                    break;
            }
            this.lexer.u8();
            const repeat = this.lexer.vu();
            section.body[n].funcs = [];
            for (let p = 0; p < repeat; p++) section.body[n].funcs.push(this.lexer.vu());
        }
        return (this.sections[9] = section);
        /*
        const section = new WASMSection('elements', 1);
        const len = this.lexer.vu();
        section.body = this.lexer.packet.slice(this.lexer.index, this.lexer.index += len);
        return (this.sections[9] = section);
        */
    }
    parseSection0xa() {
        this.lexer.vu();
        const section = new WASMSection('code', this.lexer.vu());
        for (let n = 0; n < section.body.length; n++) {
            const len = this.lexer.vu();
            section.body[n] = this.lexer.packet.slice(this.lexer.index, this.lexer.index += len);
        }
        return (this.sections[10] = section);
    }
    parseSection0xb() {
        const section = new WASMSection('data', 1);
        const len = this.lexer.vu();
        section.body = this.lexer.packet.slice(this.lexer.index, this.lexer.index += len);
        return (this.sections[11] = section);
    }
    parseFunction(func) {
        this.lexer.set(func);
        const localLength = this.lexer.vu();
        let len, before;
        for (let n = 0; n < localLength; n++) {
            this.lexer.vu();
            this.lexer.u8();
        }
        while(this.lexer.has()) {
            let op;
            switch(op = this.lexer.u8()) {
                case OP.block: case OP.loop: case OP.if:
                case OP.memory.size: case OP.memory.grow:
                    this.lexer.u8();
                    break;
                case OP.br: case OP.br_if:
                case OP.local.get: case OP.local.set: case OP.local.tee:
                case OP.i32.const: case OP.i64.const:
                    this.lexer.vu();
                    break;
                case OP.f32.const:
                    this.lexer.f32();
                    break;
                case OP.f64.const:
                    this.lexer.f64();
                    break;
                case OP.global.get: case OP.global.set:
                    this.lexer.vu();
                    break; //adjust global index later
                case OP.i32.load: case OP.i32.load8_s: case OP.i32.load8_u: case OP.i32.load16_s: case OP.i32.load16_u:
                case OP.i64.load: case OP.i64.load8_s: case OP.i64.load8_u: case OP.i64.load16_s: case OP.i64.load16_u: case OP.i64.load32_s: case OP.i64.load32_u:
                case OP.f32.load:
                case OP.f64.load:
                case OP.i32.store: case OP.i32.store8: case OP.i32.store16:
                case OP.i64.store: case OP.i64.store8: case OP.i64.store16: case OP.i64.store32:
                case OP.f32.store:
                case OP.f64.store:
                    this.lexer.vu();
                    this.lexer.vu();
                    break;
                case OP.call_indirect:
                    this.lexer.vu();
                    this.lexer.u8();
                    break;
                case OP.br_table:
                    len = this.lexer.vu();
                    for (let n = 0; n < len+1; n++) this.lexer.vu();
                    break;
                case OP.call:
                    len = this.lexer.index;
                    before = this.lexer.vu();
                    this.lexer.index = len;
                    this.lexer.replaceVu(this.getAdjusted(before));
                    break;
                default:
                    if (op > 255) throw new Error('oops, you did something wrong');
                    break;
            }
        }
        return this.lexer.packet;
    }
}
class Writer {
    static vu(num) {
        const ret = [];
        while (num >= 128) {
            ret.push((num & 127) | 128);
            num >>= 7;
        }
        ret.push(num);
        return ret;
    }
    static stringLEN(str) {
        str = new TextEncoder().encode(str);
        if (str.length > 127) throw new Error('Unsupported string length: don\'t use a string that long (max 127 byte length)');
        return [str.length, ...str];
    }
}
class Reader {
    constructor(packet) {
        this.packet = packet;
        this.index = 0;
        const buffer = new ArrayBuffer(8);
        this._u8 = new Uint8Array(buffer);
        this._f32 = new Float32Array(buffer);
        this._f64 = new Float64Array(buffer);
    }
    inject(code) {
        const newBuf = new Uint8Array(code.length + this.packet.length);
        newBuf.set(this.packet.slice(0,this.index),0);
        newBuf.set(code,this.index);
        newBuf.set(this.packet.slice(this.index),(this.index+code.length));
        return (this.packet = newBuf);
    }
    replaceVu(replace) {
        const before = this.index, old = this.vu(), now = this.index;
        replace = Writer.vu(replace);
        if (replace.length === now - before) this.packet.set(replace, before);
        else {
            const newBuf = new Uint8Array(this.packet.length-now+before+replace.length);
            newBuf.set(this.packet.slice(0,before),0);
            newBuf.set(replace,before);
            newBuf.set(this.packet.slice(now),(this.index=before+replace.length));
            this.packet = newBuf;
        }
    }
    has() { return this.index < this.packet.length }
    set(packet) {
        this.packet = packet;
        this.index = 0;
    }
    ru8() { return this.packet[this.index] }
    u8() { return this.packet[this.index++] }
    f32() {
        this._u8.set(this.packet.slice(this.index, this.index += 4));
        return this._f32[0];
    }
    f64() {
        this._u8.set(this.packet.slice(this.index, this.index += 8));
        return this._f64[0];
    }
    vu() {
        let out = 0, at = 0;
        while (this.packet[this.index] & 0x80) {
            out |= (this.u8() & 0x7f) << at;
            at += 7;
        }
        out |= this.u8() << at;
        return out;
    }
    stringLEN() {
        const len = this.u8();
        const ret = new TextDecoder().decode(this.packet.slice(this.index, this.index += len));
        return ret;
    }
}