wasm parser

used to modify wasm

Versione datata 28/11/2022. Vedi la nuova versione l'ultima versione.

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greasyfork.org/scripts/455550/1122226/wasm%20parser.js

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         wasm parser
// @version      1.0
// @description  used to modify wasm
// @author       bismuth
// ==/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 - 1]);
        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 - 1] = 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;
    }
}