The binary was an ELF that implemented a simple VM with copy, add, subtract, xor and compare operations.The bytecode code was decoding part of itself with by xoring it again and again - the operation was done 4096^4 times. Hence it took a lot of time.This is the disassembled bytecode that shows the 4 nested loops of 4096```0x801 : mov data[1], 1 : 0...0x813 : mov data[26], 4095 : 00x814 : if !data[26] jmp ip+84 : 40950x815 : sub data[26], data[1] : 4095 : 10x816 : mov data[25], 4095 : 00x817 : if !data[25] jmp ip+80 : 40950x818 : sub data[25], data[1] : 4095 : 10x819 : mov data[24], 4095 : 00x81a : if !data[24] jmp ip+76 : 40950x81b : sub data[24], data[1] : 4095 : 10x81c : mov data[23], 4095 : 00x81d : if !data[23] jmp ip+72 : 40950x81e : sub data[23], data[1] : 4095 : 1```
We patch these 4 vm instructions and set 4096 to 1 and run the binary. The disassembler can be written easily as there was no obfuscation. This is a simple tracer that disassembles and prints the bytes read.```jssetImmediate(function () {    var base;    var modules = Process.enumerateModules();    modules.forEach((i) => {        // console.log(i.name);        if (i.name === "chall") {            base = i.base;        }    });    console.log(base);    var tohook = base.add(0x10A7);    var comphook = base.add(0x1214);    var data = base.add(0x4038);
    Interceptor.attach(tohook,        function (args) {            var i = this.context.rdx;            var op = (i >> 24) & 0xff;            var arg1 = (i >> 12) & 0xfff;            var arg2 = i & 0xfff;            console.log(this.context.rax);            if (op == 0xd6) {                var d1 = data.add(Number(arg1 * 4)).readU32();                var d2 = data.add(Number(arg2 * 4)).readU32();                console.log(`mov data[${arg1}], data[${arg2}] : ${d1} : ${d2}`);            }            else if (op == 0xd5) {                var d1 = data.add(Number(arg1 * 4)).readU32();                console.log(`data[${arg1}] = getchar() : ${d1}`);            }            else if (op == 0xd8) {                var d1 = data.add(Number(arg1 * 4)).readU32();                console.log(`mov data[${arg1}], ${arg2} : ${d1}`);            }            else if (op == 0xf6) {                var d1 = data.add(Number(arg1 * 4)).readU32();                console.log(`putchar(data[${arg1}])`);            }            else if (op == 0x18) {                var d1 = data.add(Number(arg1 * 4)).readU32();                var d2 = data.add(Number(arg2 * 4)).readU32();                console.log(`xor data[${arg1}], data[${arg2}] : ${d1} : ${d2}`);            }            else if (op == 0x69) {                var d1 = data.add(Number(arg1 * 4)).readU32();                console.log(`if !data[${arg1}] jmp ip+${arg2} : ${d1}`);            }            else if (op == 0xa6) {                console.log(`jmp ${arg1}`);            }            else if (op == 0x16) {                var d1 = data.add(Number(arg1 * 4)).readU32();                var d2 = data.add(Number(arg2 * 4)).readU32();                console.log(`add data[${arg1}], data[${arg2}] : ${d1} : ${d2}`);            }            else if (op == 0x17) {                var d1 = data.add(Number(arg1 * 4)).readU32();                var d2 = data.add(Number(arg2 * 4)).readU32();                console.log(`sub data[${arg1}], data[${arg2}] : ${d1} : ${d2}`);            }            else {                console.log(`${op} ${arg1} ${arg2}`);            }        }    );    Interceptor.attach(comphook,        function (args) {            var readat = ptr(Number(Number(this.context.rax * 4) + Number(this.context.rbp)));            console.log(readat, readat.readU32());        }    );});```