├── README.md ├── compiler ├── .gitignore ├── dist │ ├── bytecode.js │ ├── compiler.js │ └── index.js ├── input │ ├── basic-browserchecks │ │ ├── test.args │ │ ├── test.js │ │ └── test.jsvm │ ├── basic │ │ ├── test.args │ │ ├── test.js │ │ └── test.jsvm │ └── if │ │ ├── test.args │ │ ├── test.js │ │ └── test.jsvm ├── package-lock.json ├── package.json ├── src │ ├── bytecode.ts │ ├── compiler.ts │ ├── index.ts │ └── instrset.d.ts └── tsconfig.json └── vm ├── basic-test └── vm.js ├── test-browserchecks └── vm.js └── vm.js /README.md: -------------------------------------------------------------------------------- 1 | ## JSVM 2 | 3 | - A simple javascript virtualization obfuscation implementation 4 | - Custom stack based RISC virtual machine in javascript 5 | - Compiler to compile javascript into jsvm's bytecode 6 | - 24 Supported opcodes 7 | 8 | The compiler only covers a subset of javascript syntax, I will be adding more over time 9 | 10 | ## Compilation Process 11 | 12 | 1) JSVM's IR compiler will first construct a intermediate representation of the provided source code 13 | 2) JSVM's Bytecode compiler will then compile the intermediate representation into binary that JSVM will understand 14 | 15 | 16 | ## Example 17 | 18 | ```js 19 | 20 | import fs from 'fs' 21 | import { BytecodeCompiler } from './bytecode' 22 | import { Compiler } from './compiler' 23 | 24 | 25 | 26 | const src = fs.readFileSync('./input/basic-browserchecks/test.js').toString() 27 | 28 | 29 | const compiler = new Compiler(src) 30 | const ir = compiler.compile() 31 | 32 | const bytecodeCompiler = new BytecodeCompiler(ir) 33 | const vmArguments = bytecodeCompiler.compile() 34 | 35 | 36 | // console.log(vmArguments) 37 | fs.writeFileSync('./input/basic-browserchecks/test.args', JSON.stringify(vmArguments, null, 4)) 38 | fs.writeFileSync('./input/basic-browserchecks/test.jsvm', JSON.stringify(ir, null, 4)) 39 | ``` 40 | 41 | **Input JS Script** 42 | 43 | ```js 44 | 45 | 46 | console.log("Checking Basic Browser Properties") 47 | 48 | var appVersion = navigator.appVersion 49 | var evalLength = eval.toString().length 50 | // var accelerometerPermission = typeof DeviceMotionEvent !== 'undefined' && typeof DeviceMotionEvent.requestPermission === 'function' 51 | var deviceMemory = navigator.deviceMemory 52 | var languages = navigator.languages 53 | var concurrency = navigator.hardwareConcurrency 54 | 55 | 56 | 57 | console.log(appVersion, evalLength, deviceMemory, languages, concurrency) 58 | ``` 59 | 60 | **Bytecode** 61 | 62 | ```js 63 | 64 | { 65 | bytecode: 'BgEAAAAAAAAAAAYVAAAAAAAAAAAAQzRpSlpkUEUYFgEBAAAAAAAAABUGFQABAAAAAAAAAHhxOUJpTnhKBxUDABUDASEVBAAVAAIAAAAAAAAAZ0lqcEo0QlUVAwAiFQQCFQADAAAAAAAAAFVraWJ4UU9lBxYBAwAAAAAAAAAGAQIAAAAAAAAAAwMGAQUAAAAAAAAABhUEAxUABAAAAAAAAABzd0Z0OTRaehUDBSIVAAUAAAAAAAAAV0ZLS082aFIHFgEGAAAAAAAAAAYBBAAAAAAAAAADBhUEAhUABgAAAAAAAABvejA3MG9JTQcWAQgAAAAAAAAABgEHAAAAAAAAAAMIFQQCFQAHAAAAAAAAAEk3RnQ0RG1pBxYBCgAAAAAAAAAGAQkAAAAAAAAAAwoVBAIVAAgAAAAAAAAAN2xuVTkyNmgHFgEMAAAAAAAAAAYBCwAAAAAAAAADDAYBDQAAAAAAAAAGFQMCGBYBDgAAAAAAAAAVBhUACQAAAAAAAABMZzE5OXRPdwcVAw0VAw4hFQMEGBYBDwAAAAAAAAAVBhUACgAAAAAAAAAwNWdpTkFqWQcVAw0VAw8hFQMHGBYBEAAAAAAAAAAVBhUACwAAAAAAAAB3cWI3MWN1SQcVAw0VAxAhFQMJGBYBEQAAAAAAAAAVBhUADAAAAAAAAABiZ2w1YWdETwcVAw0VAxEhFQMLGBYBEgAAAAAAAAAVBhUADQAAAAAAAAA4dE9OTXVQMQcVAw0VAxIhFQQAFQAOAAAAAAAAAHBhdFRrWXpqFQMNIhk=', 66 | encryptedStrings: [ 67 | '\x00\\\f)1\r>"cv\b93\x07p\x071[\x1E9?\x16p\x151[\x19/(\x109 0', 68 | '\b\x04J*', 69 | '\x0B&\r', 70 | '4\x1B\x194\x1D#<\f:\x05', 71 | '\x07\x18\x15\x00K]4\x1D', 72 | ';#%,;^', 73 | '\x0B\x1FF^S\n\x04(\x02\x15BN', 74 | '%V(\x13A%\n\f:', 75 | '_\r\x1C1NSD\rt\x03\x006L@D\rY\x0F\x17', 76 | '<\x12BQ', 77 | '@@\x14\x01', 78 | '\x07\x04\x11_', 79 | '\x12\x12\x1F]', 80 | 'H\x01<&', 81 | '\x1C\x0E\x13' 82 | ], 83 | lookUpTable: { '\x01\x00\x0B\x0B': 0 } 84 | } 85 | ``` 86 | 87 | 88 | 89 | 90 | 91 | 92 | ## To-do 93 | 94 | - Add function support 95 | - Add dead code injection option 96 | - Add integrity checks and domain protection 97 | - Add dynamic map from program counter to opcodes 98 | 99 | ## Status 100 | 101 | **WIP** 102 | 103 | ## Unsupported Javascript types 104 | 105 | - Classes 106 | - Async 107 | 108 | 109 | ## Sources 110 | https://github.com/jwillbold/rusty-jsyc 111 | 112 | https://www.usenix.org/legacy/event/woot09/tech/full_papers/rolles.pdf 113 | 114 | https://synthesis.to/2021/10/21/vm_based_obfuscation.html 115 | -------------------------------------------------------------------------------- /compiler/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /compiler/dist/bytecode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.BytecodeCompiler = void 0; 4 | const Opcodes = new Map([ 5 | ['ADD', 0], 6 | ['SUB', 1], 7 | ['MUL', 2], 8 | ['DIV', 3], 9 | ['MOD', 4], 10 | ['NEG', 5], 11 | // Store a value into local variable 12 | ['STORE', 6], 13 | ['GET_PROPERTY', 7], 14 | ['SET_PROPERTY', 8], 15 | ['EXISTS', 9], 16 | ['DELETE_PROPERTY', 10], 17 | ['INSTANCE_OF', 11], 18 | ['TYPEOF', 12], 19 | ['CALL', 13], 20 | ['EQUAL', 14], 21 | ['NOT_EQUAL', 15], 22 | ['LESS_THAN', 16], 23 | ['LESS_THAN_EQUAL', 17], 24 | ['STRICT_NOT_EQUAL', 18], 25 | ['JMP_IF', 19], 26 | ['NOT', 20], 27 | ['PUSH', 21], 28 | ['POP', 22], 29 | ['INIT_CONSTRUCTOR', 23], 30 | ['INIT_ARRAY', 24], 31 | ['EXIT', 25], 32 | ['APPLY', 33], 33 | ['CALL_MEMBER_EXPRESSION', 34] 34 | ]); 35 | const Headers = new Map([ 36 | ['string', 0], 37 | ['number', 1], 38 | ['stack', 2], 39 | ['variable', 3], 40 | ['dependency', 4], 41 | ['undefined', 5], 42 | ['array', 6], 43 | ['object', 7] 44 | ]); 45 | class BytecodeCompiler { 46 | constructor(ir) { 47 | this.ir = ir; 48 | this.encryptedStrings = []; 49 | this.bytecode = []; 50 | this.lookUpTable = {}; 51 | } 52 | encryptXor(text, key) { 53 | var result = ''; 54 | for (var i = 0; i < text.length; i++) { 55 | result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); 56 | } 57 | return result; 58 | } 59 | longToByteArray(long) { 60 | // we want to represent the input as a 8-bytes array 61 | var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; 62 | for (var index = 0; index < byteArray.length; index++) { 63 | var byte = long & 0xff; 64 | byteArray[index] = byte; 65 | long = (long - byte) / 256; 66 | } 67 | return byteArray; 68 | } 69 | ; 70 | makeid() { 71 | var text = ""; 72 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 73 | for (var i = 0; i < 8; i++) 74 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 75 | return text; 76 | } 77 | stringToByteArray(key) { 78 | var bytes = []; 79 | for (var i = 0; i < key.length; i++) { 80 | bytes.push(key.charCodeAt(i)); 81 | } 82 | return bytes; 83 | } 84 | compileInstructionArgument(arg) { 85 | const header = Headers.get(arg.type); 86 | if (header == undefined) { 87 | console.log(arg.type); 88 | throw 'UNKNOWN_HEADER'; 89 | } 90 | switch (arg.type) { 91 | case "undefined": 92 | return [header]; 93 | case "object": 94 | return [header]; 95 | case "array": 96 | return [header]; 97 | case "string": 98 | var key = this.makeid(); 99 | var keyArray = this.stringToByteArray(key); 100 | const encoded = this.encryptXor(arg.value, key); 101 | this.encryptedStrings.push(encoded); 102 | var stringPointer = this.longToByteArray(this.encryptedStrings.length - 1); 103 | return [header, ...stringPointer, ...keyArray]; 104 | case "number": 105 | // console.log(this.longToByteArray(arg.value), arg.value)' 106 | // console.log(arg.value) 107 | // console.log(this.byteArrayToLong(this.longToByteArray(arg.value))) 108 | return [header, ...this.longToByteArray(arg.value)]; 109 | case "stack": 110 | return []; 111 | case "variable": 112 | return [header, arg.value]; 113 | case "dependency": 114 | return [header, arg.value]; 115 | } 116 | } 117 | compileBlock(block, bytes) { 118 | for (var i = 0; i < block.instructions.length; i++) { 119 | var instruction = block.instructions[i]; 120 | var opcode = Opcodes.get(instruction.opcode); 121 | if (opcode == undefined) { 122 | throw "UNHANDLED_OPCODE"; 123 | } 124 | if (instruction.opcode == "JMP_IF") { 125 | // need to implement a jmp look up table 126 | // console.log("JMP_IF", instruction.args[0]) 127 | // console.log(bytes.length) 128 | // we need to put a place holder of 9 bytes beforehand so we can replace it later onwards when we add in the jmp locations 129 | var pushOpcode = Opcodes.get("PUSH"); 130 | if (pushOpcode) { 131 | bytes.push(pushOpcode); 132 | bytes.push(...this.compileInstructionArgument({ 133 | type: "string", 134 | value: this.encryptXor(instruction.args[0].value, "label") 135 | })); 136 | } 137 | bytes.push(opcode); 138 | } 139 | else { 140 | bytes.push(opcode); 141 | for (var j = 0; j < instruction.args.length; j++) { 142 | bytes.push(...this.compileInstructionArgument(instruction.args[j])); 143 | } 144 | } 145 | } 146 | } 147 | compile() { 148 | const bytes = []; 149 | for (const [label, block] of Object.entries(this.ir)) { 150 | console.log(`SET LOCATION ${label}: ${bytes.length}`); 151 | this.lookUpTable[this.encryptXor(label, 'label')] = bytes.length; 152 | this.compileBlock(block, bytes); 153 | var exitOpcode = Opcodes.get("EXIT"); 154 | if (exitOpcode) { 155 | bytes.push(exitOpcode); 156 | } 157 | } 158 | this.bytecode = bytes; 159 | const encodedBytecode = Buffer.from(bytes).toString('base64'); 160 | return { 161 | bytecode: encodedBytecode, 162 | encryptedStrings: this.encryptedStrings, 163 | lookUpTable: this.lookUpTable 164 | }; 165 | } 166 | } 167 | exports.BytecodeCompiler = BytecodeCompiler; 168 | -------------------------------------------------------------------------------- /compiler/dist/compiler.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.Compiler = void 0; 7 | const generator_1 = __importDefault(require("@babel/generator")); 8 | const parser_1 = require("@babel/parser"); 9 | // Compiler is in charge of compiling the specified javascript code into raw bytecode 10 | // Compiler will first construct a basic IR 11 | class Compiler { 12 | constructor(src) { 13 | this.ast = (0, parser_1.parse)(src); 14 | this.dependencies = ['console', 'Array', 'navigator', 'eval']; 15 | this.contexts = [ 16 | { 17 | variables: new Map(), 18 | counter: 0, 19 | }, 20 | ]; 21 | var block = { 22 | instructions: [], 23 | inheritsContext: true, 24 | }; 25 | this.blocks = [block]; 26 | this.ir = { 27 | 'main': block 28 | }; 29 | } 30 | isVariableInitalized(name) { 31 | return this.contexts[0].variables.has(name); 32 | } 33 | initalizeVariable(name, dst) { 34 | this.contexts[0].variables.set(name, dst); 35 | } 36 | isADependency(name) { 37 | return this.dependencies.includes(name); 38 | } 39 | getDependencyPointer(name) { 40 | return this.dependencies.indexOf(name); 41 | } 42 | pushInstruction(instruction) { 43 | this.blocks[0].instructions.push(instruction); 44 | } 45 | createNumberArgument(dst) { 46 | return { 47 | type: 'number', 48 | value: dst 49 | }; 50 | } 51 | createArrayArgument() { 52 | return { 53 | type: 'array', 54 | value: null 55 | }; 56 | } 57 | createObjectArgument() { 58 | return { 59 | type: 'object', 60 | value: null 61 | }; 62 | } 63 | createUndefinedArgument() { 64 | return { 65 | type: 'undefined', 66 | value: null 67 | }; 68 | } 69 | createDependencyArgument(pointer) { 70 | return { 71 | type: 'dependency', 72 | value: pointer 73 | }; 74 | } 75 | createStringArgument(value) { 76 | return { 77 | type: 'string', 78 | value: value 79 | }; 80 | } 81 | createVariableArgument(dst) { 82 | return { 83 | type: 'variable', 84 | value: dst 85 | }; 86 | } 87 | translateUnaryExpression(node) { 88 | this.appendPushInstruction(this.translateExpression(node.argument)); 89 | switch (node.operator) { 90 | case "typeof": 91 | this.appendTypeofInstruction(); 92 | break; 93 | case "!": 94 | this.appendNotInstruction(); 95 | break; 96 | default: 97 | console.log(node.operator); 98 | throw "UNSUPPORTED_UNARY_TYPE"; 99 | } 100 | } 101 | translateExpression(node) { 102 | if (node == undefined || node == null) { 103 | return { 104 | type: 'undefined', 105 | value: null 106 | }; 107 | } 108 | switch (node.type) { 109 | case "UnaryExpression": 110 | this.translateUnaryExpression(node); 111 | var dst = this.contexts[0].counter++; 112 | this.appendPopInstruction(this.createNumberArgument(dst)); 113 | return this.createVariableArgument(dst); 114 | case "CallExpression": 115 | this.pushCallExpressionOntoStack(node); 116 | var dst = this.contexts[0].counter++; 117 | this.appendPopInstruction(this.createNumberArgument(dst)); 118 | return this.createVariableArgument(dst); 119 | case "MemberExpression": 120 | this.pushMemberExpressionOntoStack(node); 121 | this.appendGetPropertyInstruction(); 122 | var dst = this.contexts[0].counter++; 123 | this.appendPopInstruction(this.createNumberArgument(dst)); 124 | return this.createVariableArgument(dst); 125 | case "BinaryExpression": 126 | this.translateBinaryExpression(node); 127 | var dst = this.contexts[0].counter++; 128 | this.appendPopInstruction(this.createNumberArgument(dst)); 129 | return this.createVariableArgument(dst); 130 | case "StringLiteral": 131 | return this.createStringArgument(node.value); 132 | case "Identifier": 133 | if (this.isADependency(node.name)) { 134 | var pointer = this.getDependencyPointer(node.name); 135 | return this.createDependencyArgument(pointer); 136 | } 137 | var reg = this.contexts[0].variables.get(node.name); 138 | if (reg == undefined) { 139 | throw "UNKNOWN_SOURCE_VARIABLE"; 140 | } 141 | return this.createVariableArgument(reg); 142 | case "NumericLiteral": 143 | return this.createNumberArgument(node.value); 144 | default: 145 | console.log(node.type); 146 | throw "UNHANDLED_VALUE"; 147 | } 148 | } 149 | appendNotInstruction() { 150 | const instruction = { 151 | opcode: 'NOT', 152 | args: [] 153 | }; 154 | this.pushInstruction(instruction); 155 | } 156 | appendTypeofInstruction() { 157 | const instruction = { 158 | opcode: 'TYPEOF', 159 | args: [] 160 | }; 161 | this.pushInstruction(instruction); 162 | } 163 | appendAddInstruction() { 164 | const instruction = { 165 | opcode: 'ADD', 166 | args: [] 167 | }; 168 | this.pushInstruction(instruction); 169 | } 170 | appendStrictNotEqual() { 171 | const instruction = { 172 | opcode: 'ADD', 173 | args: [] 174 | }; 175 | this.pushInstruction(instruction); 176 | } 177 | appendStoreInstruction(args) { 178 | const instruction = { 179 | opcode: 'STORE', 180 | args: args 181 | }; 182 | this.pushInstruction(instruction); 183 | } 184 | appendGetPropertyInstruction() { 185 | const instruction = { 186 | opcode: 'GET_PROPERTY', 187 | args: [] 188 | }; 189 | this.pushInstruction(instruction); 190 | } 191 | appendCallMemberExpression() { 192 | const instruction = { 193 | opcode: 'CALL_MEMBER_EXPRESSION', 194 | args: [] 195 | }; 196 | this.pushInstruction(instruction); 197 | } 198 | appendPushInstruction(arg) { 199 | const instruction = { 200 | opcode: 'PUSH', 201 | args: [arg] 202 | }; 203 | this.pushInstruction(instruction); 204 | } 205 | appendPopInstruction(arg) { 206 | const instruction = { 207 | opcode: 'POP', 208 | args: [arg] 209 | }; 210 | this.pushInstruction(instruction); 211 | } 212 | appendCallInstruction() { 213 | const instruction = { 214 | opcode: 'CALL', 215 | args: [], 216 | }; 217 | this.pushInstruction(instruction); 218 | } 219 | appendApplyInstruction() { 220 | const instruction = { 221 | opcode: 'APPLY', 222 | args: [], 223 | }; 224 | this.pushInstruction(instruction); 225 | } 226 | appendInitInstruction(arg) { 227 | const instruction = { 228 | opcode: 'INIT_CONSTRUCTOR', 229 | args: [ 230 | arg 231 | ], 232 | }; 233 | this.pushInstruction(instruction); 234 | } 235 | appendInitArrayInstruction() { 236 | const instruction = { 237 | opcode: 'INIT_ARRAY', 238 | args: [] 239 | }; 240 | this.pushInstruction(instruction); 241 | } 242 | appendJmpIfInstruction(arg) { 243 | const instruction = { 244 | opcode: 'JMP_IF', 245 | args: [arg] 246 | }; 247 | this.pushInstruction(instruction); 248 | } 249 | appendEqualInstruction() { 250 | const instruction = { 251 | opcode: 'EQUAL', 252 | args: [] 253 | }; 254 | this.pushInstruction(instruction); 255 | } 256 | // CISC instruction 257 | // defines a variable with empty array 258 | // returns the dst register 259 | declareArrVariable() { 260 | var dst = this.contexts[0].counter++; 261 | this.appendStoreInstruction([ 262 | this.createNumberArgument(dst), 263 | this.createArrayArgument() 264 | ]); 265 | return dst; 266 | } 267 | declareArrVariableWithValue(argument) { 268 | this.appendPushInstruction(this.translateExpression(argument)); 269 | this.appendInitArrayInstruction(); 270 | var dst = this.contexts[0].counter++; 271 | this.appendPopInstruction(this.createNumberArgument(dst)); 272 | return dst; 273 | } 274 | translateBinaryExpression(node) { 275 | if (node.left.type == "PrivateName") { 276 | throw "UNHANDLED_PRIVATE_NAME"; 277 | } 278 | const left = this.translateExpression(node.left); 279 | const right = this.translateExpression(node.right); 280 | this.appendPushInstruction(left); 281 | this.appendPushInstruction(right); 282 | switch (node.operator) { 283 | case "==": 284 | this.appendEqualInstruction(); 285 | break; 286 | case "+": 287 | this.appendAddInstruction(); 288 | break; 289 | case "!==": 290 | this.appendStrictNotEqual(); 291 | break; 292 | default: 293 | throw "UNHANDLED_OPERATOR_BINARY_EXPRESSION"; 294 | } 295 | } 296 | translateVariableDeclarator(node) { 297 | if (node.id.type != "Identifier") { 298 | throw "UNHANDLED_VARIABLE_DECL_ID"; 299 | } 300 | var dst = this.contexts[0].counter++; 301 | if (this.isVariableInitalized(node.id.name)) { 302 | const reg = this.contexts[0].variables.get(node.id.name); 303 | if (reg == undefined) { 304 | throw "UNHANDLED"; 305 | } 306 | dst = reg; 307 | } 308 | this.appendStoreInstruction([ 309 | this.createNumberArgument(dst), 310 | this.translateExpression(node.init), 311 | ]); 312 | this.initalizeVariable(node.id.name, dst); 313 | } 314 | pushMemberExpressionOntoStack(node) { 315 | switch (node.object.type) { 316 | case "Identifier": 317 | // Example: 318 | // console.log("test") turns into 319 | // var bb = console["log"] 320 | // bb("test") 321 | if (this.isADependency(node.object.name)) { 322 | const pointer = this.dependencies.indexOf(node.object.name); 323 | this.appendPushInstruction(this.createDependencyArgument(pointer)); 324 | } 325 | else { 326 | console.log(node.object.name); 327 | throw "BASE_NOT_DEPENDENCY"; 328 | } 329 | if (node.property.type != "Identifier") { 330 | throw "UNSUPPORTED PROPERTY TYPE"; 331 | } 332 | break; 333 | case "CallExpression": 334 | this.pushCallExpressionOntoStack(node.object); 335 | break; 336 | default: 337 | console.log(node.object); 338 | throw "UNHANDLED_MEMBER_EXPRESSION_STATE"; 339 | } 340 | if (node.property.type != "Identifier") { 341 | throw "UNHANDLED_PROPERTY_TYPE"; 342 | } 343 | this.appendPushInstruction(this.createStringArgument(node.property.name)); 344 | } 345 | // We translate call arguments by constructing an array of all elements 346 | // 1) Defining a new variable with empty array 347 | // 2) EXEC Push this variable reference onto stack 348 | // 3) EXEC Push "push" string onto stack 349 | // 4) EXEC Get_Property and pushes onto top of stack 350 | // 5) EXEC Push "argument" 351 | // 6) EXEC Call 352 | // returns a pointer to the arguments array 353 | pushCallArgumentsOntoStack(args) { 354 | // define argument array 355 | const argumentsArrayToCall = this.declareArrVariable(); 356 | args.forEach((argument) => { 357 | var initializedArrPointer = this.declareArrVariableWithValue(argument); 358 | // pushes a reference onto stack 359 | this.appendPushInstruction(this.createArrayArgument()); 360 | this.appendPushInstruction(this.createStringArgument('push')); 361 | this.appendGetPropertyInstruction(); 362 | this.appendPushInstruction(this.createVariableArgument(argumentsArrayToCall)); 363 | this.appendPushInstruction(this.createVariableArgument(initializedArrPointer)); 364 | this.appendApplyInstruction(); 365 | }); 366 | return argumentsArrayToCall; 367 | } 368 | pushCallExpressionOntoStack(node) { 369 | var dstOfCallArguments = this.pushCallArgumentsOntoStack(node.arguments); 370 | switch (node.callee.type) { 371 | case "MemberExpression": 372 | this.pushMemberExpressionOntoStack(node.callee); 373 | this.appendPushInstruction(this.createVariableArgument(dstOfCallArguments)); 374 | this.appendCallMemberExpression(); 375 | break; 376 | case "Identifier": 377 | var arg = this.translateExpression(node.callee); 378 | this.appendPushInstruction(arg); 379 | this.appendPushInstruction(this.createVariableArgument(dstOfCallArguments)); 380 | this.appendCallInstruction(); 381 | break; 382 | default: 383 | console.log(node.callee.type); 384 | throw "UNHANDLED_CALL_EXPRESSION_TYPE"; 385 | } 386 | // 387 | } 388 | translateVariableDeclaration(node) { 389 | for (var i = 0; i < node.declarations.length; i++) { 390 | var declaration = node.declarations[i]; 391 | this.translateVariableDeclarator(declaration); 392 | } 393 | } 394 | newBlock() { 395 | } 396 | translateWhileLoop(node) { 397 | } 398 | translateIfStatement(node) { 399 | if (node.consequent.type == "BlockStatement") { 400 | var block = { 401 | instructions: [], 402 | inheritsContext: true, 403 | }; 404 | const label = `if_${node.start}:${node.end}`; 405 | // push the expression onto the stack 406 | this.appendPushInstruction(this.translateExpression(node.test)); 407 | this.appendJmpIfInstruction(this.createStringArgument(label)); 408 | this.ir[label] = block; 409 | this.blocks.unshift(block); 410 | this.constructIR(node.consequent.body); 411 | this.blocks.shift(); 412 | } 413 | if (node.alternate && node.alternate.type == "BlockStatement") { 414 | var block = { 415 | instructions: [], 416 | inheritsContext: true, 417 | }; 418 | const label = `else_${node.start}:${node.end}`; 419 | // push the expression onto the stack 420 | this.appendPushInstruction(this.translateExpression(node.test)); 421 | this.appendNotInstruction(); 422 | this.appendJmpIfInstruction(this.createStringArgument(label)); 423 | this.ir[label] = block; 424 | this.blocks.unshift(block); 425 | this.constructIR(node.alternate.body); 426 | this.blocks.shift(); 427 | } 428 | } 429 | constructIR(statements) { 430 | for (var i = 0; i < statements.length; i++) { 431 | var node = statements[i]; 432 | console.log("translating: ", (0, generator_1.default)(node).code); 433 | switch (node.type) { 434 | case "IfStatement": 435 | this.translateIfStatement(node); 436 | break; 437 | case "VariableDeclaration": 438 | this.translateVariableDeclaration(node); 439 | break; 440 | case "ExpressionStatement": 441 | switch (node.expression.type) { 442 | case "CallExpression": 443 | this.pushCallExpressionOntoStack(node.expression); 444 | break; 445 | default: 446 | console.log(node.expression.type); 447 | throw "UNHANDLED_EXPRESSION_STATEMENT"; 448 | } 449 | break; 450 | default: 451 | console.log(node.type); 452 | throw "UNHANDLED_NODE"; 453 | } 454 | } 455 | } 456 | compile() { 457 | this.constructIR(this.ast.program.body); 458 | return this.ir; 459 | } 460 | } 461 | exports.Compiler = Compiler; 462 | -------------------------------------------------------------------------------- /compiler/dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const fs_1 = __importDefault(require("fs")); 7 | const bytecode_1 = require("./bytecode"); 8 | const compiler_1 = require("./compiler"); 9 | const src = fs_1.default.readFileSync('./input/basic/test.js').toString(); 10 | const compiler = new compiler_1.Compiler(src); 11 | const ir = compiler.compile(); 12 | const bytecodeCompiler = new bytecode_1.BytecodeCompiler(ir); 13 | const vmArguments = bytecodeCompiler.compile(); 14 | console.log(vmArguments); 15 | fs_1.default.writeFileSync('./input/basic/test.args', JSON.stringify(vmArguments, null, 4)); 16 | fs_1.default.writeFileSync('./input/basic/test.jsvm', JSON.stringify(ir, null, 4)); 17 | -------------------------------------------------------------------------------- /compiler/input/basic-browserchecks/test.args: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "BgEAAAAAAAAAAAYVAAAAAAAAAAAAQzRpSlpkUEUYFgEBAAAAAAAAABUGFQABAAAAAAAAAHhxOUJpTnhKBxUDABUDASEVBAAVAAIAAAAAAAAAZ0lqcEo0QlUVAwAiFQQCFQADAAAAAAAAAFVraWJ4UU9lBxYBAwAAAAAAAAAGAQIAAAAAAAAAAwMGAQUAAAAAAAAABhUEAxUABAAAAAAAAABzd0Z0OTRaehUDBSIVAAUAAAAAAAAAV0ZLS082aFIHFgEGAAAAAAAAAAYBBAAAAAAAAAADBhUEAhUABgAAAAAAAABvejA3MG9JTQcWAQgAAAAAAAAABgEHAAAAAAAAAAMIFQQCFQAHAAAAAAAAAEk3RnQ0RG1pBxYBCgAAAAAAAAAGAQkAAAAAAAAAAwoVBAIVAAgAAAAAAAAAN2xuVTkyNmgHFgEMAAAAAAAAAAYBCwAAAAAAAAADDAYBDQAAAAAAAAAGFQMCGBYBDgAAAAAAAAAVBhUACQAAAAAAAABMZzE5OXRPdwcVAw0VAw4hFQMEGBYBDwAAAAAAAAAVBhUACgAAAAAAAAAwNWdpTkFqWQcVAw0VAw8hFQMHGBYBEAAAAAAAAAAVBhUACwAAAAAAAAB3cWI3MWN1SQcVAw0VAxAhFQMJGBYBEQAAAAAAAAAVBhUADAAAAAAAAABiZ2w1YWdETwcVAw0VAxEhFQMLGBYBEgAAAAAAAAAVBhUADQAAAAAAAAA4dE9OTXVQMQcVAw0VAxIhFQQAFQAOAAAAAAAAAHBhdFRrWXpqFQMNIhk=", 3 | "encryptedStrings": [ 4 | "\u0000\\\f)1\r>\"cv\b93\u0007p\u00071[\u001e9?\u0016p\u00151[\u0019/(\u00109 0", 5 | "\b\u0004J*", 6 | "\u000b&\r", 7 | "4\u001b\u00194\u001d#<\f:\u0005", 8 | "\u0007\u0018\u0015\u0000K]4\u001d", 9 | ";#%,;^", 10 | "\u000b\u001fF^S\n\u0004(\u0002\u0015BN", 11 | "%V(\u0013A%\n\f:", 12 | "_\r\u001c1NSD\rt\u0003\u00006L@D\rY\u000f\u0017", 13 | "<\u0012BQ", 14 | "@@\u0014\u0001", 15 | "\u0007\u0004\u0011_", 16 | "\u0012\u0012\u001f]", 17 | "H\u0001<&", 18 | "\u001c\u000e\u0013" 19 | ], 20 | "lookUpTable": { 21 | "\u0001\u0000\u000b\u000b": 0 22 | } 23 | } -------------------------------------------------------------------------------- /compiler/input/basic-browserchecks/test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | console.log("Checking Basic Browser Properties") 4 | 5 | var appVersion = navigator.appVersion 6 | var evalLength = eval.toString().length 7 | // var accelerometerPermission = typeof DeviceMotionEvent !== 'undefined' && typeof DeviceMotionEvent.requestPermission === 'function' 8 | var deviceMemory = navigator.deviceMemory 9 | var languages = navigator.languages 10 | var concurrency = navigator.hardwareConcurrency 11 | 12 | 13 | 14 | console.log(appVersion, evalLength, deviceMemory, languages, concurrency) -------------------------------------------------------------------------------- /compiler/input/basic-browserchecks/test.jsvm: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "instructions": [ 4 | { 5 | "opcode": "STORE", 6 | "args": [ 7 | { 8 | "type": "number", 9 | "value": 0 10 | }, 11 | { 12 | "type": "array", 13 | "value": null 14 | } 15 | ] 16 | }, 17 | { 18 | "opcode": "PUSH", 19 | "args": [ 20 | { 21 | "type": "string", 22 | "value": "Checking Basic Browser Properties" 23 | } 24 | ] 25 | }, 26 | { 27 | "opcode": "INIT_ARRAY", 28 | "args": [] 29 | }, 30 | { 31 | "opcode": "POP", 32 | "args": [ 33 | { 34 | "type": "number", 35 | "value": 1 36 | } 37 | ] 38 | }, 39 | { 40 | "opcode": "PUSH", 41 | "args": [ 42 | { 43 | "type": "array", 44 | "value": null 45 | } 46 | ] 47 | }, 48 | { 49 | "opcode": "PUSH", 50 | "args": [ 51 | { 52 | "type": "string", 53 | "value": "push" 54 | } 55 | ] 56 | }, 57 | { 58 | "opcode": "GET_PROPERTY", 59 | "args": [] 60 | }, 61 | { 62 | "opcode": "PUSH", 63 | "args": [ 64 | { 65 | "type": "variable", 66 | "value": 0 67 | } 68 | ] 69 | }, 70 | { 71 | "opcode": "PUSH", 72 | "args": [ 73 | { 74 | "type": "variable", 75 | "value": 1 76 | } 77 | ] 78 | }, 79 | { 80 | "opcode": "APPLY", 81 | "args": [] 82 | }, 83 | { 84 | "opcode": "PUSH", 85 | "args": [ 86 | { 87 | "type": "dependency", 88 | "value": 0 89 | } 90 | ] 91 | }, 92 | { 93 | "opcode": "PUSH", 94 | "args": [ 95 | { 96 | "type": "string", 97 | "value": "log" 98 | } 99 | ] 100 | }, 101 | { 102 | "opcode": "PUSH", 103 | "args": [ 104 | { 105 | "type": "variable", 106 | "value": 0 107 | } 108 | ] 109 | }, 110 | { 111 | "opcode": "CALL_MEMBER_EXPRESSION", 112 | "args": [] 113 | }, 114 | { 115 | "opcode": "PUSH", 116 | "args": [ 117 | { 118 | "type": "dependency", 119 | "value": 2 120 | } 121 | ] 122 | }, 123 | { 124 | "opcode": "PUSH", 125 | "args": [ 126 | { 127 | "type": "string", 128 | "value": "appVersion" 129 | } 130 | ] 131 | }, 132 | { 133 | "opcode": "GET_PROPERTY", 134 | "args": [] 135 | }, 136 | { 137 | "opcode": "POP", 138 | "args": [ 139 | { 140 | "type": "number", 141 | "value": 3 142 | } 143 | ] 144 | }, 145 | { 146 | "opcode": "STORE", 147 | "args": [ 148 | { 149 | "type": "number", 150 | "value": 2 151 | }, 152 | { 153 | "type": "variable", 154 | "value": 3 155 | } 156 | ] 157 | }, 158 | { 159 | "opcode": "STORE", 160 | "args": [ 161 | { 162 | "type": "number", 163 | "value": 5 164 | }, 165 | { 166 | "type": "array", 167 | "value": null 168 | } 169 | ] 170 | }, 171 | { 172 | "opcode": "PUSH", 173 | "args": [ 174 | { 175 | "type": "dependency", 176 | "value": 3 177 | } 178 | ] 179 | }, 180 | { 181 | "opcode": "PUSH", 182 | "args": [ 183 | { 184 | "type": "string", 185 | "value": "toString" 186 | } 187 | ] 188 | }, 189 | { 190 | "opcode": "PUSH", 191 | "args": [ 192 | { 193 | "type": "variable", 194 | "value": 5 195 | } 196 | ] 197 | }, 198 | { 199 | "opcode": "CALL_MEMBER_EXPRESSION", 200 | "args": [] 201 | }, 202 | { 203 | "opcode": "PUSH", 204 | "args": [ 205 | { 206 | "type": "string", 207 | "value": "length" 208 | } 209 | ] 210 | }, 211 | { 212 | "opcode": "GET_PROPERTY", 213 | "args": [] 214 | }, 215 | { 216 | "opcode": "POP", 217 | "args": [ 218 | { 219 | "type": "number", 220 | "value": 6 221 | } 222 | ] 223 | }, 224 | { 225 | "opcode": "STORE", 226 | "args": [ 227 | { 228 | "type": "number", 229 | "value": 4 230 | }, 231 | { 232 | "type": "variable", 233 | "value": 6 234 | } 235 | ] 236 | }, 237 | { 238 | "opcode": "PUSH", 239 | "args": [ 240 | { 241 | "type": "dependency", 242 | "value": 2 243 | } 244 | ] 245 | }, 246 | { 247 | "opcode": "PUSH", 248 | "args": [ 249 | { 250 | "type": "string", 251 | "value": "deviceMemory" 252 | } 253 | ] 254 | }, 255 | { 256 | "opcode": "GET_PROPERTY", 257 | "args": [] 258 | }, 259 | { 260 | "opcode": "POP", 261 | "args": [ 262 | { 263 | "type": "number", 264 | "value": 8 265 | } 266 | ] 267 | }, 268 | { 269 | "opcode": "STORE", 270 | "args": [ 271 | { 272 | "type": "number", 273 | "value": 7 274 | }, 275 | { 276 | "type": "variable", 277 | "value": 8 278 | } 279 | ] 280 | }, 281 | { 282 | "opcode": "PUSH", 283 | "args": [ 284 | { 285 | "type": "dependency", 286 | "value": 2 287 | } 288 | ] 289 | }, 290 | { 291 | "opcode": "PUSH", 292 | "args": [ 293 | { 294 | "type": "string", 295 | "value": "languages" 296 | } 297 | ] 298 | }, 299 | { 300 | "opcode": "GET_PROPERTY", 301 | "args": [] 302 | }, 303 | { 304 | "opcode": "POP", 305 | "args": [ 306 | { 307 | "type": "number", 308 | "value": 10 309 | } 310 | ] 311 | }, 312 | { 313 | "opcode": "STORE", 314 | "args": [ 315 | { 316 | "type": "number", 317 | "value": 9 318 | }, 319 | { 320 | "type": "variable", 321 | "value": 10 322 | } 323 | ] 324 | }, 325 | { 326 | "opcode": "PUSH", 327 | "args": [ 328 | { 329 | "type": "dependency", 330 | "value": 2 331 | } 332 | ] 333 | }, 334 | { 335 | "opcode": "PUSH", 336 | "args": [ 337 | { 338 | "type": "string", 339 | "value": "hardwareConcurrency" 340 | } 341 | ] 342 | }, 343 | { 344 | "opcode": "GET_PROPERTY", 345 | "args": [] 346 | }, 347 | { 348 | "opcode": "POP", 349 | "args": [ 350 | { 351 | "type": "number", 352 | "value": 12 353 | } 354 | ] 355 | }, 356 | { 357 | "opcode": "STORE", 358 | "args": [ 359 | { 360 | "type": "number", 361 | "value": 11 362 | }, 363 | { 364 | "type": "variable", 365 | "value": 12 366 | } 367 | ] 368 | }, 369 | { 370 | "opcode": "STORE", 371 | "args": [ 372 | { 373 | "type": "number", 374 | "value": 13 375 | }, 376 | { 377 | "type": "array", 378 | "value": null 379 | } 380 | ] 381 | }, 382 | { 383 | "opcode": "PUSH", 384 | "args": [ 385 | { 386 | "type": "variable", 387 | "value": 2 388 | } 389 | ] 390 | }, 391 | { 392 | "opcode": "INIT_ARRAY", 393 | "args": [] 394 | }, 395 | { 396 | "opcode": "POP", 397 | "args": [ 398 | { 399 | "type": "number", 400 | "value": 14 401 | } 402 | ] 403 | }, 404 | { 405 | "opcode": "PUSH", 406 | "args": [ 407 | { 408 | "type": "array", 409 | "value": null 410 | } 411 | ] 412 | }, 413 | { 414 | "opcode": "PUSH", 415 | "args": [ 416 | { 417 | "type": "string", 418 | "value": "push" 419 | } 420 | ] 421 | }, 422 | { 423 | "opcode": "GET_PROPERTY", 424 | "args": [] 425 | }, 426 | { 427 | "opcode": "PUSH", 428 | "args": [ 429 | { 430 | "type": "variable", 431 | "value": 13 432 | } 433 | ] 434 | }, 435 | { 436 | "opcode": "PUSH", 437 | "args": [ 438 | { 439 | "type": "variable", 440 | "value": 14 441 | } 442 | ] 443 | }, 444 | { 445 | "opcode": "APPLY", 446 | "args": [] 447 | }, 448 | { 449 | "opcode": "PUSH", 450 | "args": [ 451 | { 452 | "type": "variable", 453 | "value": 4 454 | } 455 | ] 456 | }, 457 | { 458 | "opcode": "INIT_ARRAY", 459 | "args": [] 460 | }, 461 | { 462 | "opcode": "POP", 463 | "args": [ 464 | { 465 | "type": "number", 466 | "value": 15 467 | } 468 | ] 469 | }, 470 | { 471 | "opcode": "PUSH", 472 | "args": [ 473 | { 474 | "type": "array", 475 | "value": null 476 | } 477 | ] 478 | }, 479 | { 480 | "opcode": "PUSH", 481 | "args": [ 482 | { 483 | "type": "string", 484 | "value": "push" 485 | } 486 | ] 487 | }, 488 | { 489 | "opcode": "GET_PROPERTY", 490 | "args": [] 491 | }, 492 | { 493 | "opcode": "PUSH", 494 | "args": [ 495 | { 496 | "type": "variable", 497 | "value": 13 498 | } 499 | ] 500 | }, 501 | { 502 | "opcode": "PUSH", 503 | "args": [ 504 | { 505 | "type": "variable", 506 | "value": 15 507 | } 508 | ] 509 | }, 510 | { 511 | "opcode": "APPLY", 512 | "args": [] 513 | }, 514 | { 515 | "opcode": "PUSH", 516 | "args": [ 517 | { 518 | "type": "variable", 519 | "value": 7 520 | } 521 | ] 522 | }, 523 | { 524 | "opcode": "INIT_ARRAY", 525 | "args": [] 526 | }, 527 | { 528 | "opcode": "POP", 529 | "args": [ 530 | { 531 | "type": "number", 532 | "value": 16 533 | } 534 | ] 535 | }, 536 | { 537 | "opcode": "PUSH", 538 | "args": [ 539 | { 540 | "type": "array", 541 | "value": null 542 | } 543 | ] 544 | }, 545 | { 546 | "opcode": "PUSH", 547 | "args": [ 548 | { 549 | "type": "string", 550 | "value": "push" 551 | } 552 | ] 553 | }, 554 | { 555 | "opcode": "GET_PROPERTY", 556 | "args": [] 557 | }, 558 | { 559 | "opcode": "PUSH", 560 | "args": [ 561 | { 562 | "type": "variable", 563 | "value": 13 564 | } 565 | ] 566 | }, 567 | { 568 | "opcode": "PUSH", 569 | "args": [ 570 | { 571 | "type": "variable", 572 | "value": 16 573 | } 574 | ] 575 | }, 576 | { 577 | "opcode": "APPLY", 578 | "args": [] 579 | }, 580 | { 581 | "opcode": "PUSH", 582 | "args": [ 583 | { 584 | "type": "variable", 585 | "value": 9 586 | } 587 | ] 588 | }, 589 | { 590 | "opcode": "INIT_ARRAY", 591 | "args": [] 592 | }, 593 | { 594 | "opcode": "POP", 595 | "args": [ 596 | { 597 | "type": "number", 598 | "value": 17 599 | } 600 | ] 601 | }, 602 | { 603 | "opcode": "PUSH", 604 | "args": [ 605 | { 606 | "type": "array", 607 | "value": null 608 | } 609 | ] 610 | }, 611 | { 612 | "opcode": "PUSH", 613 | "args": [ 614 | { 615 | "type": "string", 616 | "value": "push" 617 | } 618 | ] 619 | }, 620 | { 621 | "opcode": "GET_PROPERTY", 622 | "args": [] 623 | }, 624 | { 625 | "opcode": "PUSH", 626 | "args": [ 627 | { 628 | "type": "variable", 629 | "value": 13 630 | } 631 | ] 632 | }, 633 | { 634 | "opcode": "PUSH", 635 | "args": [ 636 | { 637 | "type": "variable", 638 | "value": 17 639 | } 640 | ] 641 | }, 642 | { 643 | "opcode": "APPLY", 644 | "args": [] 645 | }, 646 | { 647 | "opcode": "PUSH", 648 | "args": [ 649 | { 650 | "type": "variable", 651 | "value": 11 652 | } 653 | ] 654 | }, 655 | { 656 | "opcode": "INIT_ARRAY", 657 | "args": [] 658 | }, 659 | { 660 | "opcode": "POP", 661 | "args": [ 662 | { 663 | "type": "number", 664 | "value": 18 665 | } 666 | ] 667 | }, 668 | { 669 | "opcode": "PUSH", 670 | "args": [ 671 | { 672 | "type": "array", 673 | "value": null 674 | } 675 | ] 676 | }, 677 | { 678 | "opcode": "PUSH", 679 | "args": [ 680 | { 681 | "type": "string", 682 | "value": "push" 683 | } 684 | ] 685 | }, 686 | { 687 | "opcode": "GET_PROPERTY", 688 | "args": [] 689 | }, 690 | { 691 | "opcode": "PUSH", 692 | "args": [ 693 | { 694 | "type": "variable", 695 | "value": 13 696 | } 697 | ] 698 | }, 699 | { 700 | "opcode": "PUSH", 701 | "args": [ 702 | { 703 | "type": "variable", 704 | "value": 18 705 | } 706 | ] 707 | }, 708 | { 709 | "opcode": "APPLY", 710 | "args": [] 711 | }, 712 | { 713 | "opcode": "PUSH", 714 | "args": [ 715 | { 716 | "type": "dependency", 717 | "value": 0 718 | } 719 | ] 720 | }, 721 | { 722 | "opcode": "PUSH", 723 | "args": [ 724 | { 725 | "type": "string", 726 | "value": "log" 727 | } 728 | ] 729 | }, 730 | { 731 | "opcode": "PUSH", 732 | "args": [ 733 | { 734 | "type": "variable", 735 | "value": 13 736 | } 737 | ] 738 | }, 739 | { 740 | "opcode": "CALL_MEMBER_EXPRESSION", 741 | "args": [] 742 | } 743 | ], 744 | "inheritsContext": true 745 | } 746 | } -------------------------------------------------------------------------------- /compiler/input/basic/test.args: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "BgEAAAAAAAAAAAAAAAAAAAAAADQyVU15TnFiBgEBAAAAAAAAAAABAAAAAAAAAEgzVENYa1k4BgECAAAAAAAAAAMABgEAAAAAAAAAAAEFAAAAAAAAAAYBBAAAAAAAAAAGFQMAGBYBBQAAAAAAAAAVBhUAAgAAAAAAAABRMDl1WmUybgcVAwQVAwUhFQQAFQADAAAAAAAAADRHYTVMclhRFQMEIgYBBgAAAAAAAAABBQAAAAAAAAAGAQcAAAAAAAAAAAQAAAAAAAAAOHd3MUU2cGoGAQgAAAAAAAAABhUDBxUABQAAAAAAAAA1NzZMVERITQAWAQkAAAAAAAAAFQMJGBYBCgAAAAAAAAAVBhUABgAAAAAAAAByNlhtbHpzeQcVAwgVAwohFQQAFQAHAAAAAAAAADdwcDRObW80FQMIIgYBDAAAAAAAAAAGFQEFAAAAAAAAABgWAQ0AAAAAAAAAFQYVAAgAAAAAAAAAZmxDVTZkVjQHFQMMFQMNIRUEARUDDA0WAQ4AAAAAAAAABgELAAAAAAAAAAMOBgEPAAAAAAAAAAYVAwsYFgEQAAAAAAAAABUGFQAJAAAAAAAAAE1OeE1IaTFDBxUDDxUDECEVBAAVAAoAAAAAAAAAUWM5N1pwN2kVAw8iFQEFAAAAAAAAABUBBQAAAAAAAAAOFgERAAAAAAAAABUDERUACwAAAAAAAABMRHVWRUw0URMVAQUAAAAAAAAAFQEFAAAAAAAAAA4WARQAAAAAAAAAFQMUFBUADAAAAAAAAAA5ZDJxRGhJeBMGARcAAAAAAAAAAQUAAAAAAAAAGQYBEgAAAAAAAAAGFQANAAAAAAAAAExneEo4SXB5GBYBEwAAAAAAAAAVBhUADgAAAAAAAABhRDN6djVrUAcVAxIVAxMhFQQAFQAPAAAAAAAAAER4cndiSkh2FQMSIhkGARUAAAAAAAAABhUAEAAAAAAAAABNUk1GWnhTORgWARYAAAAAAAAAFQYVABEAAAAAAAAAZ0ZmcnJ2YW0HFQMVFQMWIRUEABUAEgAAAAAAAABJajA2ZjlMZBUDFSIZ", 3 | "encryptedStrings": [ 4 | "V]!>=!6\rv@'", 5 | "([ 5 | ['ADD', 0], 6 | ['SUB', 1], 7 | ['MUL', 2], 8 | ['DIV', 3], 9 | ['MOD', 4], 10 | ['NEG', 5], 11 | 12 | 13 | // Store a value into local variable 14 | ['STORE', 6], 15 | ['GET_PROPERTY', 7], 16 | ['SET_PROPERTY', 8], 17 | ['EXISTS', 9], 18 | ['DELETE_PROPERTY', 10], 19 | ['INSTANCE_OF', 11], 20 | ['TYPEOF', 12], 21 | ['CALL', 13], 22 | ['EQUAL', 14], 23 | ['NOT_EQUAL', 15], 24 | ['LESS_THAN', 16], 25 | ['LESS_THAN_EQUAL', 17], 26 | ['STRICT_NOT_EQUAL', 18], 27 | ['JMP_IF', 19], 28 | ['NOT', 20], 29 | ['PUSH', 21], 30 | ['POP', 22], 31 | ['INIT_CONSTRUCTOR', 23], 32 | ['INIT_ARRAY', 24], 33 | ['EXIT', 25], 34 | ['APPLY', 33], 35 | ['CALL_MEMBER_EXPRESSION', 34] 36 | ]) 37 | 38 | const Headers = new Map([ 39 | ['string', 0], 40 | ['number', 1], 41 | ['stack', 2], 42 | ['variable', 3], 43 | ['dependency', 4], 44 | ['undefined', 5], 45 | ['array', 6], 46 | ['object', 7] 47 | ]) 48 | export interface VirtualMachineArguments { 49 | bytecode: string 50 | encryptedStrings: string[] 51 | lookUpTable: LookUpTable 52 | 53 | } 54 | export interface LookUpTable { 55 | [Label: string]: number; 56 | } 57 | 58 | export class BytecodeCompiler { 59 | private ir: IntermediateRepresentation 60 | private encryptedStrings: string[] 61 | bytecode: number[] 62 | lookUpTable: LookUpTable 63 | 64 | 65 | 66 | constructor(ir: IntermediateRepresentation) { 67 | this.ir = ir 68 | this.encryptedStrings = [] 69 | this.bytecode = [] 70 | 71 | this.lookUpTable = {} 72 | } 73 | private encryptXor(text: string, key: string) { 74 | var result = ''; 75 | 76 | for (var i = 0; i < text.length; i++) { 77 | result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); 78 | } 79 | return result; 80 | } 81 | private longToByteArray(long: number) { 82 | // we want to represent the input as a 8-bytes array 83 | var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; 84 | 85 | for ( var index = 0; index < byteArray.length; index ++ ) { 86 | var byte = long & 0xff; 87 | byteArray [ index ] = byte; 88 | long = (long - byte) / 256 ; 89 | } 90 | 91 | return byteArray; 92 | }; 93 | 94 | private makeid() { 95 | var text = ""; 96 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 97 | 98 | for (var i = 0; i < 8; i++) 99 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 100 | 101 | return text; 102 | } 103 | 104 | private stringToByteArray(key: string): number[] { 105 | 106 | var bytes = [] 107 | for (var i = 0; i < key.length; i++) { 108 | bytes.push(key.charCodeAt(i)) 109 | } 110 | return bytes 111 | } 112 | 113 | 114 | 115 | 116 | private compileInstructionArgument(arg: InstructionArgument): number[] { 117 | 118 | const header = Headers.get(arg.type) 119 | if (header == undefined) { 120 | console.log(arg.type) 121 | throw 'UNKNOWN_HEADER' 122 | } 123 | 124 | switch (arg.type) { 125 | case "undefined": 126 | return [header] 127 | case "object": 128 | return [header] 129 | case "array": 130 | return [header] 131 | case "string": 132 | 133 | 134 | var key = this.makeid() 135 | var keyArray = this.stringToByteArray(key) 136 | 137 | const encoded = this.encryptXor(arg.value, key) 138 | 139 | 140 | this.encryptedStrings.push(encoded) 141 | 142 | var stringPointer = this.longToByteArray(this.encryptedStrings.length-1) 143 | 144 | 145 | 146 | return [header, ...stringPointer, ...keyArray] 147 | case "number": 148 | // console.log(this.longToByteArray(arg.value), arg.value)' 149 | // console.log(arg.value) 150 | // console.log(this.byteArrayToLong(this.longToByteArray(arg.value))) 151 | return [header, ...this.longToByteArray(arg.value)] 152 | case "stack": 153 | return [] 154 | case "variable": 155 | return [header, arg.value] 156 | case "dependency": 157 | return [header, arg.value] 158 | } 159 | } 160 | private compileBlock(block: Block, bytes: number[]) { 161 | 162 | for (var i=0; i 8 | counter: number 9 | 10 | } 11 | 12 | export type ArgumentHeader = 'variable'|'string'|'number'|'stack'|'dependency'|'undefined'|'array'|'object' 13 | 14 | export interface InstructionArgument { 15 | type: ArgumentHeader 16 | value: any 17 | } 18 | 19 | 20 | 21 | 22 | export interface Instruction { 23 | opcode: Opcode 24 | args: InstructionArgument[] 25 | 26 | } 27 | 28 | export interface Block { 29 | instructions: Instruction[] 30 | inheritsContext: boolean 31 | 32 | } 33 | export interface IntermediateRepresentation { 34 | [Label: string]: Block; 35 | } 36 | // Compiler is in charge of compiling the specified javascript code into raw bytecode 37 | // Compiler will first construct a basic IR 38 | export class Compiler { 39 | ast: babel.types.File 40 | contexts: Context[] 41 | 42 | dependencies: string[] 43 | 44 | 45 | 46 | blocks: Block[] 47 | ir: IntermediateRepresentation 48 | 49 | constructor(src: string) { 50 | this.ast = parse(src) 51 | this.dependencies = ['console', 'Array', 'navigator', 'eval'] 52 | this.contexts = [ 53 | { 54 | variables: new Map(), 55 | counter: 0, 56 | }, 57 | ] 58 | 59 | var block: Block = { 60 | instructions: [], 61 | inheritsContext: true, 62 | } 63 | this.blocks = [block] 64 | this.ir = { 65 | 'main': block 66 | } 67 | 68 | } 69 | private isVariableInitalized(name: string): boolean { 70 | return this.contexts[0].variables.has(name) 71 | } 72 | private initalizeVariable(name: string, dst: number) { 73 | this.contexts[0].variables.set(name, dst) 74 | } 75 | private isADependency(name: string) { 76 | return this.dependencies.includes(name) 77 | } 78 | private getDependencyPointer(name: string) { 79 | return this.dependencies.indexOf(name) 80 | } 81 | 82 | private pushInstruction(instruction: Instruction) { 83 | this.blocks[0].instructions.push(instruction) 84 | } 85 | 86 | 87 | private createNumberArgument(dst: number): InstructionArgument { 88 | return { 89 | type: 'number', 90 | value: dst 91 | } 92 | } 93 | private createArrayArgument(): InstructionArgument { 94 | return { 95 | type: 'array', 96 | value: null 97 | } 98 | } 99 | private createObjectArgument(): InstructionArgument { 100 | return { 101 | type: 'object', 102 | value: null 103 | } 104 | } 105 | private createUndefinedArgument(): InstructionArgument { 106 | return { 107 | type: 'undefined', 108 | value: null 109 | } 110 | } 111 | private createDependencyArgument(pointer: number): InstructionArgument { 112 | return { 113 | type: 'dependency', 114 | value: pointer 115 | } 116 | } 117 | private createStringArgument(value: string): InstructionArgument { 118 | return { 119 | type: 'string', 120 | value: value 121 | } 122 | } 123 | private createVariableArgument(dst: number): InstructionArgument { 124 | return { 125 | type: 'variable', 126 | value: dst 127 | } 128 | } 129 | private translateUnaryExpression(node: babel.types.UnaryExpression) { 130 | 131 | 132 | this.appendPushInstruction( 133 | this.translateExpression(node.argument) 134 | ) 135 | 136 | switch (node.operator) { 137 | case "typeof": 138 | 139 | 140 | this.appendTypeofInstruction() 141 | break 142 | 143 | 144 | 145 | case "!": 146 | this.appendNotInstruction() 147 | break 148 | default: 149 | console.log(node.operator) 150 | throw "UNSUPPORTED_UNARY_TYPE" 151 | } 152 | } 153 | 154 | private translateExpression(node: babel.types.Expression| babel.types.SpreadElement | babel.types.JSXNamespacedName | babel.types.ArgumentPlaceholder|undefined|null): InstructionArgument { 155 | if (node == undefined || node == null) { 156 | return { 157 | type: 'undefined', 158 | value: null 159 | } 160 | } 161 | switch (node.type) { 162 | case "UnaryExpression": 163 | this.translateUnaryExpression(node) 164 | var dst = this.contexts[0].counter++ 165 | 166 | 167 | this.appendPopInstruction(this.createNumberArgument(dst)) 168 | return this.createVariableArgument(dst) 169 | 170 | 171 | case "CallExpression": 172 | this.pushCallExpressionOntoStack(node) 173 | 174 | var dst = this.contexts[0].counter++ 175 | 176 | 177 | this.appendPopInstruction(this.createNumberArgument(dst)) 178 | return this.createVariableArgument(dst) 179 | case "MemberExpression": 180 | this.pushMemberExpressionOntoStack(node) 181 | this.appendGetPropertyInstruction() 182 | 183 | 184 | var dst = this.contexts[0].counter++ 185 | 186 | this.appendPopInstruction(this.createNumberArgument(dst)) 187 | return this.createVariableArgument(dst) 188 | 189 | 190 | 191 | 192 | case "BinaryExpression": 193 | 194 | this.translateBinaryExpression(node) 195 | 196 | var dst = this.contexts[0].counter++ 197 | 198 | 199 | this.appendPopInstruction(this.createNumberArgument(dst)) 200 | return this.createVariableArgument(dst) 201 | 202 | case "StringLiteral": 203 | return this.createStringArgument(node.value) 204 | case "Identifier": 205 | 206 | if (this.isADependency(node.name)) { 207 | var pointer = this.getDependencyPointer(node.name) 208 | 209 | return this.createDependencyArgument(pointer) 210 | } 211 | 212 | 213 | var reg = this.contexts[0].variables.get(node.name) 214 | if (reg == undefined) { 215 | throw "UNKNOWN_SOURCE_VARIABLE" 216 | } 217 | 218 | return this.createVariableArgument(reg) 219 | case "NumericLiteral": 220 | return this.createNumberArgument(node.value) 221 | 222 | default: 223 | console.log(node.type) 224 | throw "UNHANDLED_VALUE" 225 | } 226 | } 227 | private appendNotInstruction() { 228 | const instruction: Instruction = { 229 | opcode: 'NOT', 230 | args: [] 231 | } 232 | 233 | 234 | this.pushInstruction(instruction) 235 | } 236 | private appendTypeofInstruction() { 237 | const instruction: Instruction = { 238 | opcode: 'TYPEOF', 239 | args: [] 240 | } 241 | 242 | 243 | this.pushInstruction(instruction) 244 | } 245 | private appendAddInstruction() { 246 | const instruction: Instruction = { 247 | opcode: 'ADD', 248 | args: [] 249 | } 250 | 251 | 252 | this.pushInstruction(instruction) 253 | } 254 | private appendStrictNotEqual() { 255 | const instruction: Instruction = { 256 | opcode: 'ADD', 257 | args: [] 258 | } 259 | 260 | 261 | this.pushInstruction(instruction) 262 | } 263 | private appendStoreInstruction(args: InstructionArgument[]) { 264 | 265 | const instruction: Instruction = { 266 | opcode: 'STORE', 267 | args: args 268 | } 269 | 270 | 271 | this.pushInstruction(instruction) 272 | } 273 | private appendGetPropertyInstruction() { 274 | const instruction: Instruction = { 275 | opcode: 'GET_PROPERTY', 276 | args: [] 277 | } 278 | 279 | 280 | this.pushInstruction(instruction) 281 | } 282 | private appendCallMemberExpression() { 283 | const instruction: Instruction = { 284 | opcode: 'CALL_MEMBER_EXPRESSION', 285 | args: [] 286 | } 287 | 288 | 289 | this.pushInstruction(instruction) 290 | } 291 | private appendPushInstruction(arg: InstructionArgument) { 292 | const instruction: Instruction = { 293 | opcode: 'PUSH', 294 | args: [arg] 295 | } 296 | 297 | 298 | this.pushInstruction(instruction) 299 | } 300 | private appendPopInstruction(arg: InstructionArgument) { 301 | const instruction: Instruction = { 302 | opcode: 'POP', 303 | args: [arg] 304 | } 305 | 306 | 307 | this.pushInstruction(instruction) 308 | } 309 | 310 | private appendCallInstruction() { 311 | const instruction: Instruction = { 312 | opcode: 'CALL', 313 | args: [], 314 | } 315 | this.pushInstruction(instruction) 316 | } 317 | private appendApplyInstruction() { 318 | const instruction: Instruction = { 319 | opcode: 'APPLY', 320 | args: [], 321 | } 322 | this.pushInstruction(instruction) 323 | } 324 | private appendInitInstruction(arg: InstructionArgument) { 325 | const instruction: Instruction = { 326 | opcode: 'INIT_CONSTRUCTOR', 327 | args: [ 328 | arg 329 | ], 330 | } 331 | this.pushInstruction(instruction) 332 | } 333 | private appendInitArrayInstruction() { 334 | const instruction: Instruction = { 335 | opcode: 'INIT_ARRAY', 336 | args: [] 337 | } 338 | this.pushInstruction(instruction) 339 | } 340 | private appendJmpIfInstruction(arg: InstructionArgument) { 341 | const instruction: Instruction = { 342 | opcode: 'JMP_IF', 343 | args: [arg] 344 | } 345 | this.pushInstruction(instruction) 346 | } 347 | 348 | private appendEqualInstruction() { 349 | const instruction: Instruction = { 350 | opcode: 'EQUAL', 351 | args: [] 352 | } 353 | this.pushInstruction(instruction) 354 | } 355 | 356 | 357 | // CISC instruction 358 | // defines a variable with empty array 359 | // returns the dst register 360 | private declareArrVariable(): number { 361 | var dst = this.contexts[0].counter++ 362 | 363 | this.appendStoreInstruction([ 364 | this.createNumberArgument(dst), 365 | this.createArrayArgument() 366 | 367 | ]) 368 | return dst 369 | 370 | } 371 | 372 | 373 | private declareArrVariableWithValue(argument: babel.types.Expression| babel.types.SpreadElement | babel.types.JSXNamespacedName | babel.types.ArgumentPlaceholder|undefined|null): number { 374 | 375 | this.appendPushInstruction( 376 | this.translateExpression(argument) 377 | ) 378 | this.appendInitArrayInstruction() 379 | 380 | 381 | 382 | var dst = this.contexts[0].counter++ 383 | this.appendPopInstruction(this.createNumberArgument(dst)) 384 | 385 | return dst 386 | } 387 | 388 | private translateBinaryExpression(node: babel.types.BinaryExpression) { 389 | if (node.left.type =="PrivateName") { 390 | throw "UNHANDLED_PRIVATE_NAME" 391 | } 392 | 393 | const left = this.translateExpression(node.left) 394 | 395 | const right = this.translateExpression(node.right) 396 | 397 | this.appendPushInstruction(left) 398 | this.appendPushInstruction(right) 399 | 400 | switch (node.operator) { 401 | case "==": 402 | 403 | this.appendEqualInstruction() 404 | break 405 | case "+": 406 | 407 | 408 | this.appendAddInstruction() 409 | 410 | 411 | break 412 | case "!==": 413 | this.appendStrictNotEqual() 414 | break 415 | 416 | 417 | default: 418 | throw "UNHANDLED_OPERATOR_BINARY_EXPRESSION" 419 | } 420 | 421 | } 422 | 423 | 424 | private translateVariableDeclarator(node: babel.types.VariableDeclarator) { 425 | if (node.id.type != "Identifier") { 426 | throw "UNHANDLED_VARIABLE_DECL_ID" 427 | } 428 | 429 | var dst = this.contexts[0].counter++ 430 | if (this.isVariableInitalized(node.id.name)) { 431 | const reg = this.contexts[0].variables.get(node.id.name) 432 | 433 | 434 | if (reg == undefined) { 435 | throw "UNHANDLED" 436 | } 437 | dst = reg 438 | 439 | } 440 | 441 | 442 | 443 | this.appendStoreInstruction([ 444 | this.createNumberArgument(dst), 445 | this.translateExpression(node.init), 446 | 447 | ]) 448 | this.initalizeVariable(node.id.name, dst) 449 | 450 | } 451 | 452 | 453 | private pushMemberExpressionOntoStack(node: babel.types.MemberExpression) { 454 | 455 | switch (node.object.type) { 456 | case "Identifier": 457 | // Example: 458 | // console.log("test") turns into 459 | // var bb = console["log"] 460 | // bb("test") 461 | 462 | 463 | if (this.isADependency(node.object.name)) { 464 | 465 | const pointer = this.dependencies.indexOf(node.object.name) 466 | this.appendPushInstruction( 467 | this.createDependencyArgument(pointer), 468 | ) 469 | 470 | } else { 471 | console.log(node.object.name) 472 | throw "BASE_NOT_DEPENDENCY" 473 | } 474 | 475 | 476 | if (node.property.type != "Identifier") { 477 | throw "UNSUPPORTED PROPERTY TYPE" 478 | } 479 | break 480 | case "CallExpression": 481 | this.pushCallExpressionOntoStack(node.object) 482 | break 483 | default: 484 | console.log(node.object) 485 | throw "UNHANDLED_MEMBER_EXPRESSION_STATE" 486 | 487 | } 488 | 489 | if (node.property.type != "Identifier") { 490 | throw "UNHANDLED_PROPERTY_TYPE" 491 | } 492 | 493 | 494 | 495 | 496 | 497 | this.appendPushInstruction( 498 | this.createStringArgument(node.property.name) 499 | ) 500 | 501 | 502 | 503 | 504 | } 505 | 506 | 507 | 508 | // We translate call arguments by constructing an array of all elements 509 | // 1) Defining a new variable with empty array 510 | // 2) EXEC Push this variable reference onto stack 511 | // 3) EXEC Push "push" string onto stack 512 | // 4) EXEC Get_Property and pushes onto top of stack 513 | // 5) EXEC Push "argument" 514 | // 6) EXEC Call 515 | // returns a pointer to the arguments array 516 | private pushCallArgumentsOntoStack(args: Array): number { 517 | // define argument array 518 | const argumentsArrayToCall = this.declareArrVariable() 519 | 520 | 521 | 522 | 523 | args.forEach((argument) => { 524 | 525 | 526 | 527 | var initializedArrPointer = this.declareArrVariableWithValue(argument) 528 | 529 | 530 | 531 | // pushes a reference onto stack 532 | this.appendPushInstruction( 533 | this.createArrayArgument() 534 | ) 535 | 536 | this.appendPushInstruction( 537 | this.createStringArgument('push') 538 | ) 539 | 540 | this.appendGetPropertyInstruction() 541 | this.appendPushInstruction( 542 | this.createVariableArgument(argumentsArrayToCall) 543 | ) 544 | this.appendPushInstruction( 545 | this.createVariableArgument(initializedArrPointer) 546 | ) 547 | 548 | 549 | this.appendApplyInstruction() 550 | 551 | 552 | 553 | 554 | }) 555 | 556 | return argumentsArrayToCall 557 | } 558 | 559 | private pushCallExpressionOntoStack(node: babel.types.CallExpression) { 560 | var dstOfCallArguments = this.pushCallArgumentsOntoStack(node.arguments) 561 | switch (node.callee.type) { 562 | case "MemberExpression": 563 | 564 | 565 | 566 | this.pushMemberExpressionOntoStack(node.callee) 567 | 568 | this.appendPushInstruction( 569 | this.createVariableArgument(dstOfCallArguments) 570 | ) 571 | this.appendCallMemberExpression() 572 | 573 | 574 | break 575 | 576 | case "Identifier": 577 | 578 | var arg = this.translateExpression(node.callee) 579 | 580 | this.appendPushInstruction(arg) 581 | this.appendPushInstruction( 582 | this.createVariableArgument(dstOfCallArguments) 583 | ) 584 | 585 | this.appendCallInstruction() 586 | 587 | break 588 | 589 | 590 | 591 | default: 592 | console.log(node.callee.type) 593 | throw "UNHANDLED_CALL_EXPRESSION_TYPE" 594 | } 595 | 596 | // 597 | 598 | 599 | 600 | 601 | } 602 | private translateVariableDeclaration(node: babel.types.VariableDeclaration) { 603 | for (var i = 0; i's from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | "outDir": "dist", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | // "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": true, /* Enable all strict type-checking options. */ 80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /vm/basic-test/vm.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | const headers = { 5 | 'LOAD_STRING': 0, 6 | 'LOAD_NUMBER': 1, 7 | 8 | 'POP_STACK': 2, 9 | 'FETCH_VARIABLE': 3, 10 | 11 | 'FETCH_DEPENDENCY': 4, 12 | 'LOAD_UNDEFINED': 5, 13 | 'LOAD_ARRAY': 6, 14 | 'LOAD_OBJECT': 7, 15 | } 16 | 17 | // Boiler template for VM 18 | const opcodes = { 19 | // Arithmitic 20 | 'ADD': 0, 21 | 'SUB': 1, 22 | 'MUL': 2, 23 | 'DIV': 3, 24 | 'MOD': 4, 25 | 'NEG': 5, 26 | 27 | 28 | // Store a value into local variable 29 | 'STORE': 6, 30 | 'GET_PROPERTY': 7, 31 | 'SET_PROPERTY': 8, 32 | 'EXISTS': 9, 33 | 'DELETE_PROPERTY': 10, 34 | 'INSTANCE_OF': 11, 35 | 'TYPEOF': 12, 36 | 'CALL': 13, 37 | 'EQUAL': 14, 38 | 'NOT_EQUAL': 15, 39 | 'LESS_THAN': 16, 40 | 'LESS_THAN_EQUAL': 17, 41 | 'STRICT_NOT_EQUAL': 18, 42 | 'JMP_IF': 19, 43 | 'NOT':20, 44 | 'PUSH': 21, 45 | 'POP': 22, 46 | 'INIT_CONSTRUCTOR': 23, 47 | 'INIT_ARRAY': 24, 48 | 'EXIT':25, 49 | 'VOID': 26, 50 | 'THROW': 27, 51 | 'DELETE': 28, 52 | 'UADD': 29, 53 | 'UMINUS': 30, 54 | 'BNOT': 31, 55 | 'AND': 32, 56 | 'APPLY': 33, 57 | 'CALL_MEMBER_EXPRESSION': 34, 58 | 59 | } 60 | 61 | 62 | class VM { 63 | constructor(encodedBytecode, encryptedStrings, dependencies, lookUpTable) { 64 | this.decodedBytecode = this.decodeBytecode(encodedBytecode) 65 | // console.log(this.decodedBytecode) 66 | 67 | this.dependencies = dependencies 68 | this.encryptedStrings = encryptedStrings 69 | this.numbers = [] 70 | this.opcodeHandlers = [] 71 | this.stack = [] 72 | this.localVariables = [] 73 | this.lookUpTable = lookUpTable 74 | 75 | this.exitToPreviousContext = [function(vm) { 76 | // if we call this function from main context then we just exit 77 | vm.programCounter = vm.decodedBytecode.length +1 78 | // vm.programCounter = +inf 79 | }] 80 | this.programCounter = 0 81 | this.initOpcodeHandlers() 82 | } 83 | 84 | 85 | decodeBytecode(encodedBytecode) { 86 | 87 | if (typeof window !== "undefined") { 88 | var decodedBase64 = atob(encodedBytecode) 89 | } else { 90 | var decodedBase64 = Buffer.from(encodedBytecode, 'base64').toString('ascii') 91 | 92 | } 93 | 94 | var intArr = [] 95 | for (var i=0; i= 0; i--) { 104 | value = (value * 256) + byteArray[i]; 105 | } 106 | 107 | return value; 108 | } 109 | 110 | decryptXor(text, key) { 111 | var result = ''; 112 | 113 | for (var i = 0; i < text.length; i++) { 114 | result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); 115 | } 116 | return result; 117 | } 118 | load8ByteArray() { 119 | var byteArray = [] 120 | for (var i = 0; i<8; i++) { 121 | const numPointer = this.decodedBytecode[this.programCounter++] 122 | byteArray.push(numPointer) 123 | } 124 | 125 | return byteArray 126 | } 127 | byteArrayToString(byteArray) { 128 | var value = ""; 129 | for (var i =0;i =!6\rv@'", 436 | '= 0; i--) { 102 | value = (value * 256) + byteArray[i]; 103 | } 104 | 105 | return value; 106 | } 107 | 108 | decryptXor(text, key) { 109 | var result = ''; 110 | 111 | for (var i = 0; i < text.length; i++) { 112 | result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); 113 | } 114 | return result; 115 | } 116 | load8ByteArray() { 117 | var byteArray = [] 118 | for (var i = 0; i<8; i++) { 119 | const numPointer = this.decodedBytecode[this.programCounter++] 120 | byteArray.push(numPointer) 121 | } 122 | 123 | return byteArray 124 | } 125 | byteArrayToString(byteArray) { 126 | var value = ""; 127 | for (var i =0;i "cv\b93\x07p\x071[\x1E9?\x16p\x151[\x19/(\x109 0', 433 | '\b\x04J*', 434 | '\x0B&\r', 435 | '4\x1B\x194\x1D#<\f:\x05', 436 | '\x07\x18\x15\x00K]4\x1D', 437 | ';#%,;^', 438 | '\x0B\x1FF^S\n\x04(\x02\x15BN', 439 | '%V(\x13A%\n\f:', 440 | '_\r\x1C1NSD\rt\x03\x006L@D\rY\x0F\x17', 441 | '<\x12BQ', 442 | '@@\x14\x01', 443 | '\x07\x04\x11_', 444 | '\x12\x12\x1F]', 445 | 'H\x01<&', 446 | '\x1C\x0E\x13' 447 | ], 448 | lookUpTable: { '\x01\x00\x0B\x0B': 0 } 449 | } 450 | const vm = new VM(arg.bytecode, arg.encryptedStrings, [console, Array, navigator, eval], arg.lookUpTable) 451 | 452 | vmStart(vm) -------------------------------------------------------------------------------- /vm/vm.js: -------------------------------------------------------------------------------- 1 | 2 | const headers = { 3 | 'LOAD_STRING': 0, 4 | 'LOAD_NUMBER': 1, 5 | 6 | 'POP_STACK': 2, 7 | 'FETCH_VARIABLE': 3, 8 | 9 | 'FETCH_DEPENDENCY': 4, 10 | 'LOAD_UNDEFINED': 5, 11 | 'LOAD_ARRAY': 6, 12 | 'LOAD_OBJECT': 7, 13 | } 14 | 15 | // Boiler template for VM 16 | const opcodes = { 17 | // Arithmitic 18 | 'ADD': 0, 19 | 'SUB': 1, 20 | 'MUL': 2, 21 | 'DIV': 3, 22 | 'MOD': 4, 23 | 'NEG': 5, 24 | 25 | 26 | // Store a value into local variable 27 | 'STORE': 6, 28 | 'GET_PROPERTY': 7, 29 | 'SET_PROPERTY': 8, 30 | 'EXISTS': 9, 31 | 'DELETE_PROPERTY': 10, 32 | 'INSTANCE_OF': 11, 33 | 'TYPEOF': 12, 34 | 'CALL': 13, 35 | 'EQUAL': 14, 36 | 'NOT_EQUAL': 15, 37 | 'LESS_THAN': 16, 38 | 'LESS_THAN_EQUAL': 17, 39 | 'STRICT_NOT_EQUAL': 18, 40 | 'JMP_IF': 19, 41 | 'NOT':20, 42 | 'PUSH': 21, 43 | 'POP': 22, 44 | 'INIT_CONSTRUCTOR': 23, 45 | 'INIT_ARRAY': 24, 46 | 'EXIT':25, 47 | 'VOID': 26, 48 | 'THROW': 27, 49 | 'DELETE': 28, 50 | 'UADD': 29, 51 | 'UMINUS': 30, 52 | 'BNOT': 31, 53 | 'AND': 32, 54 | 'APPLY': 33, 55 | 'CALL_MEMBER_EXPRESSION': 34, 56 | 57 | } 58 | 59 | 60 | class VM { 61 | constructor(encodedBytecode, encryptedStrings, dependencies, lookUpTable) { 62 | this.decodedBytecode = this.decodeBytecode(encodedBytecode) 63 | // console.log(this.decodedBytecode) 64 | 65 | this.dependencies = dependencies 66 | this.encryptedStrings = encryptedStrings 67 | this.numbers = [] 68 | this.opcodeHandlers = [] 69 | this.stack = [] 70 | this.localVariables = [] 71 | this.lookUpTable = lookUpTable 72 | 73 | this.exitToPreviousContext = [function(vm) { 74 | // if we call this function from main context then we just exit 75 | vm.programCounter = vm.decodedBytecode.length +1 76 | // vm.programCounter = +inf 77 | }] 78 | this.programCounter = 0 79 | this.initOpcodeHandlers() 80 | } 81 | 82 | 83 | decodeBytecode(encodedBytecode) { 84 | 85 | if (typeof window !== "undefined") { 86 | var decodedBase64 = atob(encodedBytecode) 87 | } else { 88 | var decodedBase64 = Buffer.from(encodedBytecode, 'base64').toString('ascii') 89 | 90 | } 91 | 92 | var intArr = [] 93 | for (var i=0; i= 0; i--) { 102 | value = (value * 256) + byteArray[i]; 103 | } 104 | 105 | return value; 106 | } 107 | 108 | decryptXor(text, key) { 109 | var result = ''; 110 | 111 | for (var i = 0; i < text.length; i++) { 112 | result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); 113 | } 114 | return result; 115 | } 116 | load8ByteArray() { 117 | var byteArray = [] 118 | for (var i = 0; i<8; i++) { 119 | const numPointer = this.decodedBytecode[this.programCounter++] 120 | byteArray.push(numPointer) 121 | } 122 | 123 | return byteArray 124 | } 125 | byteArrayToString(byteArray) { 126 | var value = ""; 127 | for (var i =0;i