├── output └── .gitkeep ├── .gitignore ├── README.md ├── src ├── bytecode.js ├── index.js ├── disassembler.js └── opcodes.js ├── LICENSE └── package.json /output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | test/ 4 | output/**.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ips-disassembler 2 | 3 | A repo to disassemble Kasada.io's virtual machine into an assembly like language. 4 | 5 | ## Get Started 6 | 7 | ```sh 8 | git clone git@github.com:umasii/ips-disassembler.git 9 | npm i 10 | npm run start 11 | ``` 12 | 13 | The bytecode you'd like to disassemble should be placed in `bytecode/bytecode.txt`. 14 | 15 | The resulting disassemblies are placed in `output`. Functions are found in `functions.json`, a linear sweep in `scan.json`, and the main function in `main.json`. Branches can be found in `branches.json`. 16 | 17 | I've also written a (messy) graph style explorer for functions. You can view it at the following URL. 18 | 19 | https://github.com/umasii/disassembler-graph-view 20 | 21 | I've also written a few blog posts on how this was written. You can read them at https://nullpt.rs. 22 | -------------------------------------------------------------------------------- /src/bytecode.js: -------------------------------------------------------------------------------- 1 | const decryptionConstants = { 2 | V: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 3 | W: 50, 4 | }; 5 | 6 | // decodes bytecode into instruction array 7 | function decryptBytecode(n) { 8 | for ( 9 | var t = decryptionConstants, 10 | r = t.V, 11 | i = t.W, 12 | o = r.length - i, 13 | u = [], 14 | e = 0; 15 | e < n.length; 16 | 17 | ) 18 | for (var f = 0, c = 1; ;) { 19 | var a = r.indexOf(n[e++]); 20 | if (((f += c * (a % i)), a < i)) { 21 | u.push(0 | f); 22 | break; 23 | } 24 | (f += i * c), (c *= o); 25 | } 26 | return u; 27 | } 28 | 29 | // decodes string section of the bytecode 30 | function decodeString(n, t) { 31 | n[t[0]++]; 32 | 33 | for (var c = "", a = n[t[0]++], v = 0; v < a; v++) { 34 | var l = n[t[0]++]; 35 | c += String.fromCharCode((4294967232 & l) | ((39 * l) & 63)); 36 | } 37 | return c; 38 | } 39 | 40 | module.exports = { 41 | decryptBytecode, 42 | decodeString, 43 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 umasi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ips-disassembler", 3 | "version": "1.0.0", 4 | "description": "A repo to disassemble Kasada.io's virtual machine into an assembly like language.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "acorn": "^8.6.0", 8 | "acorn-walk": "^8.2.0", 9 | "chalk": "^2.4.2", 10 | "color-convert": "^1.9.3", 11 | "ansi-styles": "^3.2.1", 12 | "arg": "^4.1.3", 13 | "color-name": "^1.1.3", 14 | "create-require": "^1.1.1", 15 | "globals": "^11.12.0", 16 | "escape-string-regexp": "^1.0.5", 17 | "diff": "^4.0.2", 18 | "make-error": "^1.3.6", 19 | "has-flag": "^3.0.0", 20 | "debug": "^4.3.2", 21 | "js-tokens": "^4.0.0", 22 | "jsesc": "^2.5.2", 23 | "ms": "^2.1.2", 24 | "source-map": "^0.5.7", 25 | "to-fast-properties": "^2.0.0", 26 | "typescript": "^4.4.4", 27 | "ts-node": "^10.4.0", 28 | "supports-color": "^5.5.0", 29 | "yn": "^3.1.1" 30 | }, 31 | "devDependencies": {}, 32 | "scripts": { 33 | "start": "node src/index.js" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/umasii/ips-disassembler.git" 38 | }, 39 | "author": "umasi", 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/umasii/ips-disassembler/issues" 43 | }, 44 | "homepage": "https://github.com/umasii/ips-disassembler#readme" 45 | } 46 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const decoder = require('./bytecode.js'); 3 | const disassembler = require('./disassembler.js'); 4 | 5 | const bytecode = fs.readFileSync("./bytecode/bytecode.txt", "utf8"); 6 | 7 | let decodedBytecode = decoder.decryptBytecode(bytecode); 8 | 9 | // splices the string section of the bytecode 10 | let stringIndex = decodedBytecode[decodedBytecode.length - 1] ^ decodedBytecode.length; 11 | let bytecodeStrings = decodedBytecode.splice(stringIndex, decodedBytecode[stringIndex + 1] + 2); 12 | 13 | let decodedStrings = decoder.decodeString(bytecodeStrings, [0]); 14 | 15 | let MainDisassembler = new disassembler(decodedBytecode, decodedStrings); 16 | 17 | console.log("Performing linear scan..."); 18 | let scan = MainDisassembler.step("scan", 1); 19 | fs.writeFileSync("./output/" + "scan" + ".json", JSON.stringify(scan)); 20 | console.log("Linear scanning has recovered " + (MainDisassembler.scanned["scan"].size) + " out of " + decodedBytecode.length + " instructions."); 21 | 22 | console.log("Starting recursive traversal"); 23 | console.log("Disassembling main function..."); 24 | let trace = MainDisassembler.step("step", 1); 25 | fs.writeFileSync("./output/main.json", JSON.stringify(trace)); 26 | 27 | console.log("Disassembling functions and branches..."); 28 | let { funcTraces, branchTraces } = MainDisassembler.scanPointers(); 29 | fs.writeFileSync("./output/functions" + ".json", JSON.stringify(funcTraces)); 30 | fs.writeFileSync("./output/branches" + ".json", JSON.stringify(branchTraces)); 31 | 32 | console.log("Recursive traversal has recovered " + (MainDisassembler.scanned["step"].size) + " out of " + decodedBytecode.length + " instructions."); -------------------------------------------------------------------------------- /src/disassembler.js: -------------------------------------------------------------------------------- 1 | const opcodes = require('./opcodes.js') 2 | 3 | const decodeConstants = { 4 | "B": { 5 | "q": 4, 6 | "D": 6, 7 | "G": 8, 8 | "H": 10, 9 | "J": 12, 10 | "K": 14 11 | }, 12 | "P": { 13 | "V": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 14 | "W": 50 15 | } 16 | } 17 | 18 | module.exports = class Disassembler { 19 | constructor(bytecodeArr, decodedString) { 20 | this.bytecodeArr = bytecodeArr; 21 | this.decodedString = decodedString; 22 | 23 | this.branches = new Set(); 24 | this.branchTraces = {}; 25 | this.scannedBranches = new Set() 26 | 27 | this.functionPointers = new Set(); 28 | this.scannedFunctions = new Set(); 29 | this.functionTraces = {}; 30 | 31 | this.scanned = { 32 | "step": new Set(), 33 | "scan": new Set() 34 | }; 35 | } 36 | 37 | getRegister() { 38 | this.scanned[this.mode].add(this.state.ptr) 39 | return this.bytecodeArr[this.state.ptr++] >> 5 40 | } 41 | 42 | getValue() { 43 | var i, o, u, e 44 | this.scanned[this.mode].add(this.state.ptr) 45 | let f = this.bytecodeArr[this.state.ptr++] 46 | if (1 & f) 47 | // case if f is an odd number 48 | return f >> 1; 49 | if (f === decodeConstants.B.q) 50 | // case to retrieve float value 51 | return this.scanned[this.mode].add(this.state.ptr), 52 | this.scanned[this.mode].add(this.state.ptr + 1), 53 | e = this.bytecodeArr[this.state.ptr++], 54 | i = this.bytecodeArr[this.state.ptr++], 55 | o = 2147483648 & e ? -1 : 1, 56 | u = (2146435072 & e) >> 20, 57 | e = (1048575 & e) * Math.pow(2, 32) + (i < 0 ? i + Math.pow(2, 32) : i), 58 | 2047 === u ? e ? NaN : 1 / 0 * o : (0 !== u ? e += Math.pow(2, 52) : u++, 59 | o * e * Math.pow(2, u - 1075)); 60 | if (f !== decodeConstants.B.D) 61 | // case to retrieve register value 62 | return f === decodeConstants.B.G || f !== decodeConstants.B.H && (f === decodeConstants.B.J ? null : f !== decodeConstants.B.K ? "reg" + (f >> 5) : void 0); 63 | // case to retrieve constant value 64 | this.scanned[this.mode].add(this.state.ptr) 65 | this.scanned[this.mode].add(this.state.ptr + 1) 66 | let n = this.bytecodeArr[this.state.ptr++] 67 | return this.decodedString.slice(n, n + this.bytecodeArr[this.state.ptr++]); 68 | } 69 | 70 | // creates a conditional branch given instruction pointer value to jump to, and adds it to the branches array for later disassembly. 71 | branch(ptr) { 72 | if (this.scannedBranches.has(ptr)) { 73 | return; 74 | } 75 | this.branches.add(ptr); 76 | } 77 | 78 | // creates a function given instruction pointer value to jump to, and adds it to the functionPointers array for later disassembly. 79 | function(ptr) { 80 | if (this.scannedFunctions.has(ptr)) { 81 | return; 82 | } 83 | this.functionPointers.add(ptr); 84 | } 85 | 86 | // Traverses the bytecode by following the instruction pointer, either linearly or recursively depending on this.mode 87 | // The current value of the instruction pointer is stored in this.state, along with the trace currently being built 88 | step(mode, ptr) { 89 | this.mode = mode; 90 | this.shouldStop = false; 91 | this.state = { 92 | ptr: ptr, 93 | trace: [] 94 | } 95 | 96 | while (!this.shouldStop) { 97 | let ptr = this.state.ptr++; 98 | // records that we have scanned this ptr value 99 | this.scanned[mode].add(ptr); 100 | 101 | let opcode = opcodes[this.bytecodeArr[ptr]]; 102 | 103 | if (!opcode || null === opcode.do) { 104 | this.state.trace.push("TERM"); 105 | this.shouldStop = true; 106 | } else { 107 | opcode.do(this); 108 | } 109 | 110 | // reformat opcode trace to have the pointer value in front 111 | let op = this.state.trace[this.state.trace.length - 1]; 112 | let newOp = ptr + " " + op; 113 | this.state.trace[this.state.trace.length - 1] = newOp; 114 | } 115 | 116 | return this.state.trace 117 | } 118 | 119 | // Calls step() on all functions and branches defined in the main trace, until no new functions or branches have been discovered 120 | // May make improvements to the branching logic later to reduce repeated non starting pointer instructions in branch traces 121 | scanPointers() { 122 | if (this.functionPointers.size > 0) { 123 | let fptr = this.functionPointers.values().next().value; 124 | this.functionPointers.delete(fptr); 125 | this.scannedFunctions.add(fptr); 126 | 127 | let ftrace = this.step("step", fptr); 128 | 129 | this.functionTraces[fptr] = ftrace; 130 | this.scanPointers(); 131 | } else if (this.branches.size > 0) { 132 | 133 | let bptr = this.branches.values().next().value; 134 | this.branches.delete(bptr); 135 | this.scannedBranches.add(bptr); 136 | 137 | let btrace = this.step("step", bptr); 138 | 139 | this.branchTraces[bptr] = btrace; 140 | 141 | this.scanPointers(); 142 | } 143 | if (this.functionPointers.size === 0 && this.branches.size === 0) { 144 | return { funcTraces: this.functionTraces, branchTraces: this.branchTraces } 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /src/opcodes.js: -------------------------------------------------------------------------------- 1 | var offset = ` `; 2 | 3 | module.exports = { 4 | 0x00: { 5 | // m(n, M(n) + M(n)) 6 | name: 'ADD', 7 | do: function (disassembler) { 8 | let val1 = disassembler.getValue(); 9 | let val2 = disassembler.getValue(); 10 | let reg1 = disassembler.getRegister(); 11 | disassembler.state.trace.push('ADD' + offset + val1 + ' + ' + val2 + '->' + ' reg' + reg1); 12 | }, 13 | }, 14 | 0x01: { 15 | // m(n, M(n) - M(n)) 16 | name: 'SUBTRACT', 17 | do: function (disassembler) { 18 | let val1 = disassembler.getValue(); 19 | let val2 = disassembler.getValue(); 20 | let reg1 = disassembler.getRegister(); 21 | disassembler.state.trace.push('SUBTRACT' + offset + val1 + ' - ' + val2 + ' -> reg' + reg1); 22 | } 23 | }, 24 | 0x02: { 25 | // m(n, M(n) * M(n)) 26 | name: 'MULTIPLY', 27 | do: function (disassembler) { 28 | let val1 = disassembler.getValue(); 29 | let val2 = disassembler.getValue(); 30 | let reg1 = disassembler.getRegister(); 31 | disassembler.state.trace.push('MULTIPLY' + offset + val1 + ' * ' + val2 + ' -> reg' + reg1); 32 | } 33 | }, 34 | 0x03: { 35 | // m(n, M(n) / M(n)) 36 | name: 'DIVIDE', 37 | do: function (disassembler) { 38 | let val1 = disassembler.getValue(); 39 | let val2 = disassembler.getValue(); 40 | let reg1 = disassembler.getRegister(); 41 | disassembler.state.trace.push('DIVIDE' + offset + val1 + ' / ' + val2 + ' -> reg' + reg1); 42 | } 43 | }, 44 | 0x04: { 45 | // m(n, M(n) % M(n)) 46 | name: 'MOD', 47 | do: function (disassembler) { 48 | let val1 = disassembler.getValue(); 49 | let val2 = disassembler.getValue(); 50 | let reg1 = disassembler.getRegister(); 51 | disassembler.state.trace.push('MOD' + offset + val1 + ' % ' + val2 + ' -> reg' + reg1); 52 | } 53 | }, 54 | 0x05: { 55 | // m(n, +M(n)) 56 | name: 'NUMBER', 57 | do: function (disassembler) { 58 | let val1 = disassembler.getValue(); 59 | let reg1 = disassembler.getRegister(); 60 | disassembler.state.trace.push('NUMBER' + offset + val1 + ' -> reg' + reg1); 61 | } 62 | }, 63 | 0x06: { 64 | // m(n, !M(n)) 65 | name: 'NOT', 66 | do: function (disassembler) { 67 | let val1 = disassembler.getValue(); 68 | let reg1 = disassembler.getRegister(); 69 | disassembler.state.trace.push('NOT' + offset + val1 + ' -> reg' + reg1); 70 | } 71 | }, 72 | 0x07: { 73 | // m(n, M(n) >> M(n)) 74 | name: 'RSHIFT', 75 | do: function (disassembler) { 76 | let val1 = disassembler.getValue(); 77 | let val2 = disassembler.getValue(); 78 | let reg1 = disassembler.getRegister(); 79 | disassembler.state.trace.push('RSHIFT' + offset + val1 + ' >> ' + val2 + ' -> reg' + reg1); 80 | } 81 | }, 82 | 0x08: { 83 | // m(n, M(n) << M(n)) 84 | name: 'LSHIFT', 85 | do: function (disassembler) { 86 | let val1 = disassembler.getValue(); 87 | let val2 = disassembler.getValue(); 88 | let reg1 = disassembler.getRegister(); 89 | disassembler.state.trace.push('LSHIFT' + offset + val1 + ' << ' + val2 + ' -> reg' + reg1); 90 | } 91 | }, 92 | 0x09: { 93 | // m(n, M(n) >>> M(n)) 94 | name: 'USHIFT', 95 | do: function (disassembler) { 96 | let val1 = disassembler.getValue(); 97 | let val2 = disassembler.getValue(); 98 | let reg1 = disassembler.getRegister(); 99 | disassembler.state.trace.push('USHIFT' + offset + val1 + ' >>> ' + val2 + ' -> reg' + reg1); 100 | } 101 | }, 102 | 0x0A: { 103 | // m(n, M(n) | M(n)) 104 | name: 'OR', 105 | do: function (disassembler) { 106 | let val1 = disassembler.getValue(); 107 | let val2 = disassembler.getValue(); 108 | let reg1 = disassembler.getRegister(); 109 | disassembler.state.trace.push('OR' + offset + val1 + ' | ' + val2 + ' -> reg' + reg1); 110 | } 111 | }, 112 | 0x0B: { 113 | // m(n, M(n) & M(n)) 114 | name: 'AND', 115 | do: function (disassembler) { 116 | let val1 = disassembler.getValue(); 117 | let val2 = disassembler.getValue(); 118 | let reg1 = disassembler.getRegister(); 119 | disassembler.state.trace.push('AND' + offset + val1 + ' & ' + val2 + ' -> reg' + reg1); 120 | } 121 | }, 122 | 0x0C: { 123 | // m(n, M(n) ^ M(n)) 124 | name: 'XOR', 125 | do: function (disassembler) { 126 | let val1 = disassembler.getValue(); 127 | let val2 = disassembler.getValue(); 128 | let reg1 = disassembler.getRegister(); 129 | disassembler.state.trace.push('XOR' + offset + val1 + ' ^ ' + val2 + ' -> reg' + reg1); 130 | } 131 | }, 132 | 0x0D: { 133 | // m(n, ~M(n)) 134 | name: 'INVERT', 135 | do: function (disassembler) { 136 | let val1 = disassembler.getValue(); 137 | let reg1 = disassembler.getRegister(); 138 | disassembler.state.trace.push('INVERT' + offset + val1 + ' -> reg' + reg1); 139 | } 140 | }, 141 | 0x0E: { 142 | // m(n, M(n)) 143 | name: 'SET', 144 | do: function (disassembler) { 145 | let val1 = disassembler.getValue(); 146 | let reg1 = disassembler.getRegister(); 147 | disassembler.state.trace.push('SET' + offset + val1 + ' -> reg' + reg1); 148 | } 149 | }, 150 | 0x0F: { 151 | // m(n, window) 152 | name: 'PUSH WINDOW', 153 | do: function (disassembler) { 154 | let reg1 = disassembler.getRegister(); 155 | // state.register[reg1] = state.window; 156 | disassembler.state.trace.push('PUSH WINDOW' + offset + ' -> reg' + reg1); 157 | } 158 | }, 159 | 0x10: { 160 | // m(n, M(n)[M(n)]) 161 | name: 'GET', 162 | do: function (disassembler) { 163 | let val1 = disassembler.getValue(); 164 | let val2 = disassembler.getValue(); 165 | let reg1 = disassembler.getRegister(); 166 | disassembler.state.trace.push('GET' + offset + val1 + '[' + val2 + '] -> reg' + reg1); 167 | } 168 | }, 169 | 0x11: { 170 | // M(n)[M(n)] = M(n) 171 | name: 'PUT', 172 | do: function (disassembler) { 173 | let val1 = disassembler.getValue(); 174 | let val2 = disassembler.getValue(); 175 | val3 = disassembler.getValue(); 176 | disassembler.state.trace.push('PUT' + offset + val1 + '[' + val2 + '] = ' + val3); 177 | } 178 | }, 179 | 0x12: { 180 | // m(n, window[M(n)]) 181 | name: 'GET WINDOW PROP', 182 | do: function (disassembler) { 183 | let val1 = disassembler.getValue(); 184 | let reg1 = disassembler.getRegister(); 185 | disassembler.state.trace.push('GET WINDOW PROP' + offset + val1 + ' -> reg' + reg1); 186 | } 187 | }, 188 | 0x13: { 189 | // m(n, M(n) in M(n)) 190 | name: 'IN', 191 | do: function (disassembler) { 192 | let val1 = disassembler.getValue(); 193 | let val2 = disassembler.getValue(); 194 | let reg1 = disassembler.getRegister(); 195 | disassembler.state.trace.push('IN' + offset + val1 + ' in' + val2 + ' -> reg' + reg1); 196 | } 197 | }, 198 | 0x14: { 199 | // m(n, delete M(n)[M(n)]) 200 | name: 'DELETE', 201 | do: function (disassembler) { 202 | let val1 = disassembler.getValue(); 203 | let val2 = disassembler.getValue(); 204 | let reg1 = disassembler.getRegister(); 205 | disassembler.state.trace.push('DELETE' + offset + val1 + '[' + val2 + '] -> reg' + reg1); 206 | } 207 | }, 208 | 0x15: { 209 | // m(n, M(n) instanceof M(n)) 210 | name: 'INSTANCEOF', 211 | do: function (disassembler) { 212 | let val1 = disassembler.getValue(); 213 | let val2 = disassembler.getValue(); 214 | let reg1 = disassembler.getRegister(); 215 | disassembler.state.trace.push('INSTANCEOF' + offset + val1 + ' instanceof ' + val2 + ' -> reg' + reg1); 216 | } 217 | }, 218 | 0x16: { 219 | // m(n, typeof M(n)) 220 | name: 'TYPEOF', 221 | do: function (disassembler) { 222 | let val1 = disassembler.getValue(); 223 | let reg1 = disassembler.getRegister(); 224 | disassembler.state.trace.push('TYPEOF' + offset + val1 + ' -> reg' + reg1); 225 | } 226 | }, 227 | 0x17: { 228 | /* 229 | var t = M(n) 230 | , r = M(n) 231 | , i = M(n); 232 | if (void 0 === t && j() && (t = a), 233 | r[v] && r[v].L === r) { 234 | n.g = [r[v].T, { 235 | h: t, 236 | M: r, 237 | g: d(n.g, h), 238 | j: d(h(), h), 239 | $: [], 240 | O: r[v].O 241 | }, void 0, function () { 242 | return arguments 243 | } 244 | .apply(void 0, F(i))]; 245 | for (var o = 0; o < i.length; o++) 246 | n.g.push(i[o]) 247 | } else if (r.toString) 248 | n.g[2] = r.apply(t, i); 249 | else { 250 | var u = !1 251 | , e = j(); 252 | if (e && e.console) 253 | for (var f = Object.keys(e.console), c = 0; c < f.length; c++) 254 | if (r === e.console[f[c]]) { 255 | u = !0, 256 | n.g[2] = r(i); 257 | break 258 | } 259 | u || (n.g[2] = r.apply(t, i)) 260 | } 261 | */ 262 | name: 'CALL FUNCTION: ', 263 | reads: 3, 264 | writes: 0, 265 | do: function (disassembler) { 266 | let val1 = disassembler.getValue(); 267 | let val2 = disassembler.getValue(); 268 | let val3 = disassembler.getValue(); 269 | if (val1 == undefined) { 270 | disassembler.state.trace.push('CALL FUNCTION' + offset + "PTR: " + val2 + ' MEM: ' + val3); 271 | } else { 272 | disassembler.state.trace.push('CALL FUNCTION' + offset + 'OBJ: ' + val1 + ' FUNC: ' + val2 + ' ARGS: ' + val3); 273 | } 274 | } 275 | }, 276 | 0x18: { 277 | /* var t = M(n) 278 | , r = M(n).slice(); 279 | r.unshift(void 0), 280 | m(n, new (Function.bind.apply(t, r))) */ 281 | name: 'BIND APPLY', 282 | do: function (disassembler) { 283 | let val1 = disassembler.getValue(); 284 | let val2 = disassembler.getValue(); 285 | let reg1 = disassembler.getRegister(); 286 | disassembler.state.trace.push("BIND APPLY" + offset + val1 + " " + val2 + " -> reg" + reg1); 287 | } 288 | }, 289 | 0x19: { 290 | // m(n, {}) 291 | name: 'EMPTY OBJECT', 292 | do: function (disassembler) { 293 | let reg1 = disassembler.getRegister(); 294 | disassembler.state.trace.push('EMPTY OBJECT -> reg' + reg1); 295 | } 296 | }, 297 | 0x1A: { 298 | // m(n, []) 299 | name: 'EMPTY ARRAY', 300 | do: function (disassembler) { 301 | let reg1 = disassembler.getRegister(); 302 | disassembler.state.trace.push('EMPTY ARRAY -> reg' + reg1); 303 | } 304 | }, 305 | 0x1B: { 306 | // m(n, new Array(M(n))) 307 | name: 'NEW ARRAY', 308 | do: function (disassembler) { 309 | let val1 = disassembler.getValue(); 310 | let reg1 = disassembler.getRegister(); 311 | disassembler.state.trace.push('NEW ARRAY' + offset + val1 + ' -> reg' + reg1); 312 | } 313 | }, 314 | 0x1C: { 315 | // m(n, new RegExp(M(n), M(n))) 316 | name: 'NEW REGEXP', 317 | do: function (disassembler) { 318 | let val1 = disassembler.getValue(); 319 | let val2 = disassembler.getValue(); 320 | let reg1 = disassembler.getRegister(); 321 | disassembler.state.trace.push('NEW REGEXP' + offset + val1 + ', ' + val2 + ' -> reg' + reg1); 322 | } 323 | }, 324 | 0x1D: { 325 | /* var t, r = []; 326 | for (t in M(n)) 327 | r.push(t); 328 | m(n, r) */ 329 | name: 'COPY ENUMERABLE', 330 | do: function (disassembler) { 331 | let val1 = disassembler.getValue(); 332 | let reg1 = disassembler.getRegister(); 333 | disassembler.state.trace.push('COPY ENUMERABLE' + offset + val1 + ' -> reg' + reg1); 334 | } 335 | }, 336 | 0x1E: { 337 | // m(n, M(n) == M(n)) 338 | name: 'EQUAL', 339 | do: function (disassembler) { 340 | let val1 = disassembler.getValue(); 341 | let val2 = disassembler.getValue(); 342 | let reg1 = disassembler.getRegister(); 343 | disassembler.state.trace.push('EQUAL' + offset + val1 + ' == ' + val2 + ' -> reg' + reg1); 344 | } 345 | }, 346 | 0x1F: { 347 | // m(n, M(n) === M(n)) 348 | name: 'STRICTLY EQUAL', 349 | do: function (disassembler) { 350 | let val1 = disassembler.getValue(); 351 | let val2 = disassembler.getValue(); 352 | let reg1 = disassembler.getRegister(); 353 | disassembler.state.trace.push('STRICT EQUAL' + offset + val1 + ' === ' + val2 + ' -> reg' + reg1); 354 | } 355 | }, 356 | 0x20: { 357 | // m(n, M(n) != M(n)) 358 | name: 'NOT EQUAL', 359 | do: function (disassembler) { 360 | let val1 = disassembler.getValue(); 361 | let val2 = disassembler.getValue(); 362 | let reg1 = disassembler.getRegister(); 363 | disassembler.state.trace.push('NOT EQUAL' + offset + val1 + ' != ' + val2 + ' -> reg' + reg1); 364 | } 365 | }, 366 | 0x21: { 367 | // m(n, M(n) !== M(n)) 368 | name: 'STRICTLY NOT EQUAL', 369 | do: function (disassembler) { 370 | let val1 = disassembler.getValue(); 371 | let val2 = disassembler.getValue(); 372 | let reg1 = disassembler.getRegister(); 373 | disassembler.state.trace.push('STRICT NOT EQUAL' + offset + val1 + ' !== ' + val2 + ' -> reg' + reg1); 374 | }, 375 | }, 376 | 0x22: { 377 | // m(n, M(n) < M(n)) 378 | name: 'LESS THAN', 379 | do: function (disassembler) { 380 | let val1 = disassembler.getValue(); 381 | let val2 = disassembler.getValue(); 382 | let reg1 = disassembler.getRegister(); 383 | disassembler.state.trace.push('LESS THAN' + offset + val1 + ' < ' + val2 + ' -> reg' + reg1); 384 | } 385 | }, 386 | 0x23: { 387 | /* var t = M(n); 388 | m(n, M(n) < t) */ 389 | name: 'GREATER THAN', 390 | do: function (disassembler) { 391 | let val1 = disassembler.getValue(); 392 | let val2 = disassembler.getValue(); 393 | let reg1 = disassembler.getRegister(); 394 | disassembler.state.trace.push('GREATER THAN' + offset + val1 + ' > ' + val2 + ' -> reg' + reg1); 395 | } 396 | }, 397 | 0x24: { 398 | // m(n, M(n) <= M(n)) 399 | name: 'LESS THAN OR EQUAL', 400 | do: function (disassembler) { 401 | let val1 = disassembler.getValue(); 402 | let val2 = disassembler.getValue(); 403 | let reg1 = disassembler.getRegister(); 404 | disassembler.state.trace.push('LESS THAN OR EQUAL' + offset + val1 + ' <= ' + val2 + ' -> reg' + reg1); 405 | } 406 | }, 407 | 0x25: { 408 | /* var t = M(n); 409 | m(n, M(n) <= t) */ 410 | name: 'GREATER THAN OR EQUAL', 411 | do: function (disassembler) { 412 | let val1 = disassembler.getValue(); 413 | let val2 = disassembler.getValue(); 414 | let reg1 = disassembler.getRegister(); 415 | disassembler.state.trace.push('GREATER THAN OR EQUAL' + offset + val1 + ' >= ' + val2 + ' -> reg' + reg1); 416 | } 417 | }, 418 | 0x26: { 419 | // n.g[0] = M(n) 420 | name: 'JUMP', 421 | do: function (disassembler) { 422 | let val1 = disassembler.getValue(); 423 | if (disassembler.mode == "step") { 424 | disassembler.state.ptr = val1; 425 | } 426 | disassembler.state.trace.push('JUMP' + offset + val1); 427 | } 428 | }, 429 | 0x27: { 430 | // M(n) ? M(n) : n.g[0] = M(n) 431 | name: 'JUMP IF FALSE', 432 | do: function (disassembler) { 433 | let val1 = disassembler.getValue(); 434 | let val2 = disassembler.getValue(); 435 | disassembler.state.trace.push('JUMP IF FALSE' + offset + val1 + ' TO: ' + val2 + " | " + disassembler.state.ptr); 436 | if (disassembler.mode == "step") { 437 | disassembler.branch(val2) 438 | disassembler.branch(disassembler.state.ptr) 439 | disassembler.shouldStop = true; 440 | } 441 | } 442 | }, 443 | 0x28: { 444 | // M(n) ? n.g[0] = M(n) : M(n) 445 | name: 'JUMP IF TRUE', 446 | do: function (disassembler) { 447 | let val1 = disassembler.getValue(); 448 | let val2 = disassembler.getValue(); 449 | disassembler.state.trace.push('JUMP IF TRUE' + offset + val1 + ' TO: ' + val2 + " | " + disassembler.state.ptr); 450 | if (disassembler.mode == "step") { 451 | disassembler.branch(val2) 452 | disassembler.branch(disassembler.state.ptr) 453 | disassembler.shouldStop = true 454 | } 455 | } 456 | }, 457 | 0x29: { 458 | /* var t = M(n) 459 | , r = M(n); 460 | p(n).$[t] = r */ 461 | name: 'SET MEMORY ELEMENT', 462 | do: function (disassembler) { 463 | let val1 = disassembler.getValue(); 464 | let val2 = disassembler.getValue(); 465 | disassembler.state.trace.push('SET MEMORY ELEMENT' + offset + val1 + ' = ' + val2); 466 | } 467 | }, 468 | 0x2A: { 469 | /* var t = M(n); 470 | p(n).$[t] = void 0 */ 471 | name: 'INIT MEMORY ELEMENT', 472 | do: function (disassembler) { 473 | let val1 = disassembler.getValue(); 474 | disassembler.state.trace.push('INIT MEMORY ELEMENT' + offset + val1); 475 | } 476 | }, 477 | 0x2B: { 478 | /* var t = M(n) 479 | , n = p(n) 480 | , r = n.M; 481 | n.$[t] = r */ 482 | name: 'INHERIT CALLER', 483 | do: function (disassembler) { 484 | let val1 = disassembler.getValue(); 485 | disassembler.state.trace.push('INHERIT CALLER' + offset + val1); 486 | } 487 | }, 488 | 0x2C: { 489 | /* 490 | for (var t = M(n), r = M(n), i = p(n); i; i = i.O()) 491 | if (t in i.$) 492 | return void (i.$[t] = r); 493 | for (var o = p(n); o; o = o.O()) 494 | if (t in o.$) 495 | return void (o.$[t] = r); 496 | throw '¯\\_(ツ)_/¯' 497 | */ 498 | name: "SET MEMORY ELEMENT IF INIT", 499 | reads: 2, 500 | writes: 0, 501 | do: function (disassembler) { 502 | let val1 = disassembler.getValue(); 503 | let val2 = disassembler.getValue(); 504 | disassembler.state.trace.push('SET MEMORY ELEMENT IF INIT' + offset + val1 + ' = ' + val2); 505 | } 506 | }, 507 | 0x2D: { 508 | /* for (var t = M(n), r = p(n); r; r = r.O()) 509 | if (t in r.$) 510 | return void m(n, r.$[t]); 511 | throw '¯\\_(ツ)_/¯' */ 512 | name: "PUSH MEMORY ELEMENT IF INIT", 513 | do: function (disassembler) { 514 | let val1 = disassembler.getValue(); 515 | let reg1 = disassembler.getRegister(); 516 | disassembler.state.trace.push('PUSH MEMORY ELEMENT IF INIT' + offset + val1 + ' -> reg' + reg1); 517 | } 518 | }, 519 | 0x2E: { 520 | // m(n, n.g[1].h) 521 | name: "PUSH H", 522 | do: function (disassembler) { 523 | // let val1 = state.register[1].h 524 | let reg1 = disassembler.getRegister(); 525 | disassembler.state.trace.push('PUSH H' + offset + ' -> reg' + reg1); 526 | } 527 | }, 528 | 0x2F: { 529 | // O(n, M(n)) 530 | name: "HANDLE TRACE", 531 | reads: 1, 532 | writes: 0, 533 | do: function (disassembler) { 534 | if (disassembler.state == "step") { 535 | disassembler.shouldStop = true; 536 | } 537 | let val1 = disassembler.getValue(); 538 | disassembler.state.trace.push("HANDLE ERROR" + offset + val1) 539 | } 540 | 541 | }, 542 | 0x30: { 543 | // w(n, M(n)) 544 | name: "JUMP OR RETURN VAL", 545 | do: function (disassembler) { 546 | let val1 = disassembler.getValue(); 547 | disassembler.state.trace.push('JUMP OR RETURN VAL' + offset + val1); 548 | if (disassembler.mode == "step") { 549 | disassembler.shouldStop = true; 550 | } 551 | } 552 | 553 | }, 554 | 0x31: { 555 | // w(n, void 0) 556 | name: "JUMP OR RETURN", 557 | do: function (disassembler) { 558 | disassembler.state.trace.push('JUMP OR RETURN'); 559 | if (disassembler.mode == "step") { 560 | disassembler.shouldStop = true; 561 | } 562 | } 563 | 564 | }, 565 | 0x32: { 566 | /* var r = M(n) 567 | , t = M(n) 568 | , i = M(n) 569 | , o = p(n) 570 | , u = function () { 571 | var n = $(); 572 | n.g[3] = arguments; 573 | for (var t = 0; t < arguments.length; t++) 574 | n.g[t + 4] = arguments[t]; 575 | return n.g[1] = { 576 | h: this, 577 | g: function () { 578 | return [0] 579 | }, 580 | j: function () { 581 | return [0] 582 | }, 583 | $: [], 584 | O: d(o, l), 585 | M: u 586 | }, 587 | n.g[0] = r, 588 | S(n), 589 | n.g[2] 590 | }; 591 | try { 592 | Object.defineProperty(u, "length", { 593 | value: i 594 | }), 595 | Object.defineProperty(u, "name", { 596 | value: t 597 | }) 598 | } catch (n) { 599 | for (var e = !1, f = "", c = 0; c < i; c++) 600 | e ? f += ",a".concat(c) : (f += "a".concat(c), 601 | e = !0); 602 | u = eval("(function(fn){" + "return function ".concat(t, "(").concat(f, "){return fn.apply(this, arguments)}") + "})")(u) 603 | } 604 | u[v] = { 605 | T: r, 606 | O: d(o, l), 607 | L: u 608 | }, 609 | m(n, u) */ 610 | name: "INIT FUNCTION STATE", 611 | do: function (disassembler) { 612 | let val1 = disassembler.getValue(); 613 | let val2 = disassembler.getValue(); 614 | val3 = disassembler.getValue(); 615 | let reg1 = disassembler.getRegister(); 616 | if (disassembler.mode == "step") { 617 | disassembler.function(val1) 618 | } 619 | disassembler.state.trace.push('INIT FUNCTION STATE' + offset + 'PTR: ' + val1 + ' NAME: ' + val2 + ' LEN: ' + val3 + ' -> reg' + reg1); 620 | } 621 | }, 622 | 0x33: { 623 | /* var t = M(n); 624 | n.g[1].I = t */ 625 | name: "SET I", 626 | do: function (disassembler) { 627 | let val1 = disassembler.getValue(); 628 | if (val1 !== "null" && disassembler.mode == "step") { 629 | disassembler.branch(val1) 630 | } disassembler.state.trace.push('SET I' + offset + val1); 631 | } 632 | }, 633 | 0x34: { 634 | /* var t = M(n); 635 | n.g[1].N = t */ 636 | name: "SET N", 637 | do: function (disassembler) { 638 | let val1 = disassembler.getValue(); 639 | if (val1 !== "null" && disassembler.mode == "step") { 640 | disassembler.branch(val1) 641 | } 642 | disassembler.state.trace.push('SET N' + offset + val1); 643 | } 644 | }, 645 | 0x35: { 646 | // m(n, n.F && n.F.k) 647 | name: "PUSH F.K", 648 | do: function (disassembler) { 649 | let reg1 = disassembler.getRegister(); 650 | disassembler.state.trace.push('PUSH F.K -> reg' + reg1); 651 | } 652 | 653 | }, 654 | 0x36: { 655 | // n.F = void 0 656 | name: "VOID F", 657 | do: function (disassembler) { 658 | // state.F = undefined; 659 | disassembler.state.trace.push('VOID F'); 660 | } 661 | }, 662 | 0x37: { 663 | /* 664 | n.F ? O(n, n.F.k) : (t = p(n)).C && w(n, t.C.k) 665 | if (n.F){ 666 | O(n, n.F.k) 667 | }else{ 668 | (t = p(n)).C && w(n, t.C.k) 669 | } 670 | */ 671 | name: "HANDLE TRACE OR LOOP OR RETURN C", 672 | do: function (disassembler) { 673 | if (disassembler.state == "step") { 674 | disassembler.shouldStop = true; 675 | } 676 | disassembler.state.trace.push("HANDLE TRACE OR JUMP OR RETURN C") 677 | } 678 | 679 | }, 680 | 0x38: { 681 | // null 682 | name: "NULL", 683 | do: null 684 | }, 685 | 0x39: { 686 | // m(n, t.inj0) 687 | name: "PROMISE", 688 | do: function (disassembler) { 689 | let reg1 = disassembler.getRegister(); 690 | disassembler.state.trace.push('PROMISE -> reg' + reg1); 691 | } 692 | } 693 | } --------------------------------------------------------------------------------