js-confuser-vm 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +514 -0
  3. package/dist/build-runtime.js +15 -2
  4. package/dist/compiler.js +98 -51
  5. package/dist/runtime.js +5 -1
  6. package/dist/transforms/bytecode/aliasedOpcodes.js +2 -8
  7. package/dist/transforms/bytecode/macroOpcodes.js +21 -19
  8. package/dist/transforms/bytecode/microOpcodes.js +236 -0
  9. package/dist/transforms/bytecode/resolveContants.js +5 -11
  10. package/dist/transforms/bytecode/resolveLabels.js +5 -3
  11. package/dist/transforms/bytecode/specializedOpcodes.js +21 -16
  12. package/dist/transforms/runtime/internalVariables.js +202 -0
  13. package/dist/transforms/runtime/macroOpcodes.js +30 -18
  14. package/dist/transforms/runtime/microOpcodes.js +76 -0
  15. package/dist/transforms/runtime/specializedOpcodes.js +20 -18
  16. package/dist/utils/op-utils.js +15 -8
  17. package/index.ts +3 -2
  18. package/jest.config.js +2 -0
  19. package/package.json +1 -1
  20. package/src/build-runtime.ts +18 -3
  21. package/src/compiler.ts +152 -65
  22. package/src/options.ts +1 -0
  23. package/src/runtime.ts +5 -1
  24. package/src/transforms/bytecode/aliasedOpcodes.ts +2 -12
  25. package/src/transforms/bytecode/macroOpcodes.ts +28 -29
  26. package/src/transforms/bytecode/microOpcodes.ts +291 -0
  27. package/src/transforms/bytecode/resolveContants.ts +6 -13
  28. package/src/transforms/bytecode/resolveLabels.ts +5 -4
  29. package/src/transforms/bytecode/specializedOpcodes.ts +38 -28
  30. package/src/transforms/runtime/internalVariables.ts +270 -0
  31. package/src/transforms/runtime/macroOpcodes.ts +47 -20
  32. package/src/transforms/runtime/microOpcodes.ts +93 -0
  33. package/src/transforms/runtime/specializedOpcodes.ts +27 -32
  34. package/src/types.ts +1 -1
  35. package/src/utils/op-utils.ts +21 -8
  36. package/README.MD +0 -450
  37. package/src/utilts.ts +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,58 @@
1
+ ## `0.0.7` Micro Opcodes
2
+
3
+ - Added new option `microOpcodes` which breaks opcodes into multiple sub-opcodes.
4
+
5
+ ```js
6
+ // Input Code
7
+ console.log("Hello world!");
8
+
9
+ // Before
10
+ // [2, 1, 0, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
11
+ // [0, 2, 1, 0], LOAD_CONST reg[2] = "log" 1:0-1:27
12
+ // [8, 3, 1, 2], GET_PROP reg[3] = reg[1][reg[2]] 1:0-1:27
13
+ // [0, 4, 2, 0], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
14
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
15
+
16
+ // What the opcode "LOAD_CONST" looks like:
17
+ case OP.LOAD_CONST:
18
+ var dst = this._operand();
19
+ frame.regs[dst] = this._constant();
20
+ break;
21
+
22
+ // After
23
+ // [60, 1], MICRO_LOAD_GLOBAL_0 1 1:0-1:7
24
+ // [61, 0, 0], MICRO_LOAD_GLOBAL_1 [0, 0]
25
+ // [62], MICRO_LOAD_GLOBAL_2
26
+ // [63], MICRO_LOAD_GLOBAL_3
27
+ // [58, 2], MICRO_LOAD_CONST_0 2 1:0-1:27
28
+ // [59, 1, 0], MICRO_LOAD_CONST_1 [1, 0]
29
+ // [64, 3], MICRO_GET_PROP_0 3 1:0-1:27
30
+ // [65, 1], MICRO_GET_PROP_1 1
31
+ // [66, 2], MICRO_GET_PROP_2 2
32
+ // [67], MICRO_GET_PROP_3
33
+ // [58, 4], MICRO_LOAD_CONST_0 4 1:12-1:26
34
+ // [59, 2, 0], MICRO_LOAD_CONST_1 [2, 0]
35
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
36
+
37
+ // What the opcodes "MICRO_LOAD_CONST_0" (58) and "MICRO_LOAD_CONST_1" (59) looks like:
38
+ case 58:
39
+ // MICRO_LOAD_CONST_0
40
+ this._internals[0] = this._operand();
41
+ break;
42
+ case 59:
43
+ // MICRO_LOAD_CONST_1
44
+ frame.regs[this._internals[0]] = this._constant();
45
+ break;
46
+ ```
47
+
48
+ - Fixed `Macro Opcodes` possibly clashing variables when merging opcode handlers.
49
+
50
+ - Added support for update expressions on member expressions (`object.prop++`, `object.prop--`)
51
+
52
+ - Added support for Template literals (ES6 feature added for convenience)
53
+
54
+ - Added programs [cash.min.js](https://github.com/fabiospampinato/cash/blob/master/dist/cash.min.js) and [sha256.js](https://gist.github.com/bryanchow/1649353) to the test suite
55
+
1
56
  ## `0.0.6` Register based
2
57
 
3
58
  - Switched from stack-based to register-based VM.
package/README.md ADDED
@@ -0,0 +1,514 @@
1
+ # JS Confuser VM
2
+
3
+ [![NPM](https://img.shields.io/badge/NPM-%23000000.svg?style=for-the-badge&logo=npm&logoColor=white)](https://npmjs.com/package/js-confuser-vm) [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/MichaelXF/js-confuser-vm) [![Netlify](https://img.shields.io/badge/netlify-%23000000.svg?style=for-the-badge&logo=netlify&logoColor=#00C7B7)](https://development--confuser.netlify.app/vm)
4
+
5
+
6
+ - **Requires Node v24.13.1 or higher**
7
+ - ES5 support only. No complex features: async, generator, and even try..finally aren't supported.
8
+ - Experimental. Expect issues.
9
+ - [Try the web version.](https://development--confuser.netlify.app/vm)
10
+
11
+ ### Installation
12
+
13
+ ```shell
14
+ $ npm install js-confuser-vm
15
+ ```
16
+
17
+ ### Usage
18
+
19
+ ```js
20
+ import JsConfuserVM from "js-confuser-vm";
21
+
22
+ JsConfuserVM.obfuscate(`
23
+ function fibonacci(num){
24
+ var a = 0, b = 1, c = num;
25
+ while (num-- > 1) {
26
+ c = a + b;
27
+ a = b;
28
+ b = c;
29
+ }
30
+ return c;
31
+ }
32
+
33
+ for ( var i = 1; i <= 25; i++ ) {
34
+ console.log(i, fibonacci(i))
35
+ }
36
+ `, {
37
+ target: "browser", // or "node"
38
+ randomizeOpcodes: true, // randomize the opcode numbers?
39
+ shuffleOpcodes: true, // shuffle order of opcode handlers in the runtime?
40
+ encodeBytecode: true, // encode the bytecode array?
41
+ concealConstants: true, // conceal strings and integers in the constant pool?
42
+ selfModifying: true, // do self-modifying bytecode for function bodies?
43
+ macroOpcodes: true, // create combined opcodes for repeated instruction sequences?
44
+ microOpcodes: true, // break opcodes into sub-opcodes?
45
+ specializedOpcodes: true, // create specialized opcodes for commonly used opcode+operand pairs?
46
+ aliasedOpcodes: true, // create duplicate opcodes for commonly used opcodes?
47
+ timingChecks: true, // add timing checks to detect debuggers?
48
+ minify: true // pass final output through Google Closure Compiler? (Renames VM class properties)
49
+ }).then(result => {
50
+ console.log(result.code)
51
+ })
52
+
53
+ /*
54
+ var c=Symbol();function f(a,e){this.A=a;this.H=e;this.I=!1;this.J=void 0}function h(a){return a.I?a.J:a.A.h[a.H]}function k(a,e){a.I?a.J=e:a.A.h[a.H]=e}function m(a){this.v=a;this.D=[];this.prototype={}}function n(a,e,b){this.K=a;this.h=Array(a.v.L).fill(void 0);this.l=a.v.R;this.W=e!==void 0?e:void 0;this.G=b!==void 0?b:0;this.o=null;this.F=[]}function p(a,e,b,d){this.u=a;this.O=b;this.m=d;this.j=[];this.B=[];this.i=new n(new m({C:0,L:e,R:0}),void 0,0);this.g={}}
55
+ function v(a){return a.u[a.i.l++]}function w(a,e,b){for(var d=0;d<a.B.length;d++){var g=a.B[d];if(g.A===e&&g.H===b)return g}g=new f(e,b);a.B.push(g);return g}function x(a,e,b){e=e??v(a);b=b??v(a);a=a.O[e];if(!b)return a;if(typeof a==="number")return a^b;a=typeof Buffer!=="undefined"?Buffer.from(a,"base64"):Uint8Array.from(atob(a),function(g){return g.charCodeAt(0)});e="";for(var d=0;d<a.length/2;d++)e+=String.fromCharCode((a[d*2]|a[d*2+1]<<8)^b+d&65535);return e}
56
+ function z(a,e){a.B=a.B.filter(function(b){return b.A===e?(b.J=b.A.h[b.H],b.I=!0,!1):!0})}
57
+ function A(a){for(var e=performance.now();;){var b=a.i;if(b.l>=a.u.length)break;var d=b.l++;d=a.u[d];var g=performance.now(),D=g-e>1E3;e=g;if(D){for(d=0;d<a.u.length;d++)a.u[d]=0;b.h.fill(void 0);d=51142;b.l=a.u.length}try{switch(d){case 48376:a.g[16541]=v(a);a.g[36877]=b.h[v(a)];b.h[a.g[16541]]=a.g[36877]^b.h[v(a)];break;case 24807:a.g[32755]=v(a);a.g[30211]=b.h[v(a)];b.h[a.g[32755]]=a.g[30211]>>>b.h[v(a)];break;case 25812:a.g[11979]=3;b.h[a.g[11979]]=b.h[4];break;case 21199:a.g[16541]=4;b.h[a.g[16541]]=
58
+ x(a,0,19941);break;case 48511:a.g[38915]=b.h[5];z(a,b);if(a.j.length===0)return a.g[38915];b.o===null||typeof a.g[38915]==="object"&&a.g[38915]!==null||(a.g[38915]=b.o);a.g[25255]=a.j.pop();a.g[25255].h[b.G]=a.g[38915];a.i=a.g[25255];break;case 26119:a.g[16541]=v(a);a.g[3839]=b.h[v(a)];b.h[a.g[16541]]=a.g[3839]>b.h[v(a)];break;case 7802:a.g[16541]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[16541]]=a.g[62634]!==b.h[v(a)];break;case 29164:a.g[16541]=3;b.h[a.g[16541]]=x(a,0,19941);break;case 45474:a.g[19089]=
59
+ v(a);a.g[25428]=b.h[v(a)];a.g[7422]=b.h[v(a)];a.g[40522]=v(a);a.g[28705]=Array(a.g[40522]);for(a.g[12462]=0;a.g[12462]<a.g[40522];a.g[12462]++)a.g[28705][a.g[12462]]=b.h[v(a)];if(a.g[7422]&&a.g[7422][c]){a.g[7358]=a.g[7422][c];a.g[58907]=new n(a.g[7358],a.g[25428],a.g[19089]);for(a.g[12462]=0;a.g[12462]<a.g[28705].length;a.g[12462]++)a.g[58907].h[a.g[12462]]=a.g[28705][a.g[12462]];a.g[58907].h[a.g[7358].v.C]=a.g[28705];a.j.push(a.i);a.i=a.g[58907]}else b.h[a.g[19089]]=a.g[7422].apply(a.g[25428],a.g[28705]);
60
+ break;case 47786:a.g[3766]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[3766]]=a.g[62634]!=b.h[v(a)];break;case 45528:a.g[16541]=2;b.h[a.g[16541]]=b.h[5];break;case 37324:a.g[38915]=b.h[v(a)];z(a,b);if(a.j.length===0)return a.g[38915];b.o===null||typeof a.g[38915]==="object"&&a.g[38915]!==null||(a.g[38915]=b.o);a.g[23776]=a.j.pop();a.g[23776].h[b.G]=a.g[38915];a.i=a.g[23776];break;case 37593:a.g[21276]=0;b.h[a.g[21276]]=b.h[7];break;case 16492:a.g[16541]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[16541]]=a.g[62634]<=
61
+ b.h[v(a)];break;case 1900:b.l=13;break;case 42013:a.m[x(a)]=b.h[v(a)];break;case 63795:a.g[28560]=v(a);b.h[a.g[28560]]=v(a);break;case 54788:a.g[2920]=b.h[v(a)];a.g[37378]=b.h[v(a)];a.g[35839]=b.h[v(a)];a.g[27080]=Object.getOwnPropertyDescriptor(a.g[2920],a.g[37378]);a.g[39506]={set:a.g[35839],configurable:!0,enumerable:!0};a.g[27080]&&typeof a.g[27080].get==="function"&&(a.g[39506].get=a.g[27080].get);Object.defineProperty(a.g[2920],a.g[37378],a.g[39506]);break;case 12182:a.g[29248]=v(a);a.g[52124]=
62
+ b.h[v(a)];a.g[47986]=b.h[v(a)];b.h[a.g[29248]]=delete a.g[52124][a.g[47986]];break;case 3376:a.g[16541]=5;a.g[62634]=b.h[2];b.h[a.g[16541]]=a.g[62634]+b.h[3];break;case 4123:a.g[63658]=5;a.g[40080]=b.h[3];a.g[37378]=b.h[4];b.h[a.g[63658]]=a.g[40080][a.g[37378]];break;case 27100:a.g[16541]=v(a);b.h[a.g[16541]]=-b.h[v(a)];break;case 35987:a.g[16541]=3;b.h[a.g[16541]]=b.h[6];break;case 59134:a.g[11440]=5;b.h[a.g[11440]]=x(a,4,0);break;case 47879:a.g[59573]=v(a);v(a);b.h[a.g[59573]]=void 0;break;case 59967:a.g[16541]=
63
+ 7;a.g[54385]=b.h[0];b.h[a.g[16541]]=a.g[54385]-b.h[6];break;case 37122:a.g[16541]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[16541]]=a.g[62634]>=b.h[v(a)];break;case 18522:a.g[1489]=v(a);a.g[38065]=b.h[v(a)];b.h[a.g[1489]]=a.g[38065]===b.h[v(a)];break;case 60376:a.g[19925]=b.h[3];z(a,b);if(a.j.length===0)return a.g[19925];b.o===null||typeof a.g[19925]==="object"&&a.g[19925]!==null||(a.g[19925]=b.o);a.g[53519]=a.j.pop();a.g[53519].h[b.G]=a.g[19925];a.i=a.g[53519];break;case 28102:a.g[16541]=v(a);a.g[10179]=
64
+ v(a);a.g[38805]=Array(a.g[10179]);for(a.g[37753]=0;a.g[37753]<a.g[10179];a.g[37753]++)a.g[38805][a.g[37753]]=b.h[v(a)];b.h[a.g[16541]]=a.g[38805];break;case 55907:a.g[249]=v(a);a.g[13793]=v(a);a.g[7488]=v(a);a.g[33443]=v(a);a.g[59975]=v(a);a.g[44466]=Array(a.g[59975]);for(a.g[33125]=0;a.g[33125]<a.g[59975];a.g[33125]++)a.g[5413]=v(a),a.g[61217]=v(a),a.g[44466][a.g[33125]]={V:a.g[5413],M:a.g[61217]};a.g[29776]={C:a.g[7488],L:a.g[33443],R:a.g[13793],X:a.g[44466]};a.g[32226]=new m(a.g[29776]);for(a.g[33125]=
65
+ 0;a.g[33125]<a.g[44466].length;a.g[33125]++)a.g[49053]=a.g[44466][a.g[33125]],a.g[49053].V?a.g[32226].D.push(w(a,b,a.g[49053].M)):a.g[32226].D.push(b.K.D[a.g[49053].M]);var q=a;a.g[31999]=function(l){return function(){for(var t=Array.prototype.slice.call(arguments),y=new p(q.u,l.v.L,q.O,q.m),u=new n(l,this==null?q.m:this,0),r=0;r<t.length;r++)u.h[r]=t[r];u.h[l.v.C]=t;y.i=u;return A(y)}}(a.g[32226]);a.g[31999][c]=a.g[32226];a.g[31999].prototype=a.g[32226].prototype;b.h[a.g[249]]=a.g[31999];break;case 138:a.g[16541]=
66
+ 5;b.h[a.g[16541]]=x(a,5,58082);break;case 48909:a.g[16541]=v(a);a.g[41461]=b.h[v(a)];b.h[a.g[16541]]=a.g[41461]%b.h[v(a)];break;case 13149:a.g[14138]=v(a);a.g[52124]=b.h[v(a)];a.g[37378]=b.h[v(a)];b.h[a.g[14138]]=a.g[52124][a.g[37378]];break;case 63724:a.g[55552]=4;a.g[21339]=44;b.h[a.g[55552]]||(b.l=a.g[21339]);break;case 25433:a.g[16541]=4;b.h[a.g[16541]]=b.h[5];break;case 5005:a.g[52124]=b.h[v(a)];a.g[37378]=b.h[v(a)];a.g[3547]=b.h[v(a)];Reflect.set(a.g[52124],a.g[37378],a.g[3547]);break;case 44357:b.F.push({U:v(a),
67
+ S:v(a),T:a.j.length});break;case 52184:a.g[16541]=v(a);b.h[a.g[16541]]=~b.h[v(a)];break;case 17926:b.F.pop();break;case 17061:a.g[16541]=v(a);b.h[a.g[16541]]=+b.h[v(a)];break;case 739:a.g[22118]=v(a);a.g[36985]=b.h[v(a)];a.g[41836]=v(a);a.g[59944]=Array(a.g[41836]);for(a.g[33125]=0;a.g[33125]<a.g[41836];a.g[33125]++)a.g[59944][a.g[33125]]=b.h[v(a)];if(a.g[36985]&&a.g[36985][c]){a.g[6850]=a.g[36985][c];a.g[30557]=Object.create(a.g[6850].prototype||null);a.g[58907]=new n(a.g[6850],a.g[30557],a.g[22118]);
68
+ a.g[58907].o=a.g[30557];for(a.g[33125]=0;a.g[33125]<a.g[59944].length;a.g[33125]++)a.g[58907].h[a.g[33125]]=a.g[59944][a.g[33125]];a.g[58907].h[a.g[6850].v.C]=a.g[59944];a.j.push(a.i);a.i=a.g[58907]}else b.h[a.g[22118]]=Reflect.construct(a.g[36985],a.g[59944]);break;case 16825:a.g[16541]=v(a);a.g[3993]=b.h[v(a)];b.h[a.g[16541]]=a.g[3993]in b.h[v(a)];break;case 62846:a.g[16541]=6;b.h[a.g[16541]]=x(a,0,19941);break;case 41878:a.g[16541]=v(a);a.g[35268]=b.h[v(a)];b.h[a.g[16541]]=a.g[35268]==b.h[v(a)];
69
+ break;case 46432:a.g[29035]=4;a.g[62634]=b.h[2];b.h[a.g[29035]]=a.g[62634]<=b.h[3];break;case 51142:b.l=v(a);break;case 29761:a.g[44199]=v(a);a.g[7487]=b.h[v(a)];b.h[a.g[44199]]=a.g[7487]>>b.h[v(a)];break;case 47732:a.g[16541]=v(a);b.h[a.g[16541]]=b.W;break;case 21541:a.g[16076]=9;a.g[21339]=75;b.h[a.g[16076]]||(b.l=a.g[21339]);break;case 14784:a.g[16541]=3;b.h[a.g[16541]]=x(a,1,30115);break;case 51607:a.g[16541]=v(a);a.g[65339]=b.h[v(a)];a.g[47106]=b.h[v(a)];if(typeof a.g[47106]==="function")b.h[a.g[16541]]=
70
+ a.g[65339]instanceof a.g[47106];else{a.g[5038]=a.g[47106].prototype;a.g[21339]=Object.getPrototypeOf(a.g[65339]);for(a.g[25853]=!1;a.g[21339]!==null;){if(a.g[21339]===a.g[5038]){a.g[25853]=!0;break}a.g[21339]=Object.getPrototypeOf(a.g[21339])}b.h[a.g[16541]]=a.g[25853]}break;case 55835:a.g[16541]=v(a);b.h[a.g[16541]]=h(b.K.D[v(a)]);break;case 29844:a.g[42713]=v(a);a.g[1817]=x(a);if(!(a.g[1817]in a.m))throw new ReferenceError(`${a.g[1817]} is not defined`);b.h[a.g[42713]]=a.m[a.g[1817]];break;case 24699:a.g[16541]=
71
+ 5;a.g[62634]=b.h[2];b.h[a.g[16541]]=a.g[62634]+b.h[4];break;case 44361:a.g[16541]=v(a);a.g[52124]=b.h[v(a)];a.g[15140]=[];if(a.g[52124]!==null&&a.g[52124]!==void 0)for(a.g[23643]=Object.create(null),a.g[61494]=Object(a.g[52124]);a.g[61494]!==null;){a.g[46538]=Object.getOwnPropertyNames(a.g[61494]);for(a.g[33125]=0;a.g[33125]<a.g[46538].length;a.g[33125]++)a.g[7176]=a.g[46538][a.g[33125]],a.g[7176]in a.g[23643]||(a.g[23643][a.g[7176]]=!0,a.g[34638]=Object.getOwnPropertyDescriptor(a.g[61494],a.g[7176]),
72
+ a.g[34638]&&a.g[34638].enumerable&&a.g[15140].push(a.g[7176]));a.g[61494]=Object.getPrototypeOf(a.g[61494])}b.h[a.g[16541]]={N:a.g[15140],P:0};break;case 49820:a.g[16541]=v(a);b.h[a.g[16541]]=x(a);break;case 51817:a.g[38915]=b.h[4];z(a,b);if(a.j.length===0)return a.g[38915];b.o===null||typeof a.g[38915]==="object"&&a.g[38915]!==null||(a.g[38915]=b.o);a.g[23776]=a.j.pop();a.g[23776].h[b.G]=a.g[38915];a.i=a.g[23776];break;case 64967:a.g[52124]=b.h[v(a)];a.g[37378]=b.h[v(a)];a.g[11470]=b.h[v(a)];a.g[12672]=
73
+ Object.getOwnPropertyDescriptor(a.g[52124],a.g[37378]);a.g[60061]={get:a.g[11470],configurable:!0,enumerable:!0};a.g[12672]&&typeof a.g[12672].set==="function"&&(a.g[60061].set=a.g[12672].set);Object.defineProperty(a.g[52124],a.g[37378],a.g[60061]);break;case 45309:a.g[7138]=v(a);a.g[18365]=v(a);a.g[49429]=v(a);for(a.g[9236]=a.g[18365];a.g[9236]<a.g[49429];a.g[9236]++)a.u[a.g[7138]+(a.g[9236]-a.g[18365])]=a.u[a.g[9236]];break;case 49746:a.g[12540]=v(a);k(b.K.D[a.g[12540]],b.h[v(a)]);break;case 7780:a.g[16541]=
74
+ 3;a.g[1817]=x(a,2,31810);if(!(a.g[1817]in a.m))throw new ReferenceError(`${a.g[1817]} is not defined`);b.h[a.g[16541]]=a.m[a.g[1817]];break;case 20641:a.g[16541]=v(a);a.g[7422]=b.h[v(a)];a.g[11407]=v(a);a.g[59944]=Array(a.g[11407]);for(a.g[33125]=0;a.g[33125]<a.g[11407];a.g[33125]++)a.g[59944][a.g[33125]]=b.h[v(a)];if(a.g[7422]&&a.g[7422][c]){a.g[7358]=a.g[7422][c];a.g[58907]=new n(a.g[7358],a.m,a.g[16541]);for(a.g[33125]=0;a.g[33125]<a.g[59944].length;a.g[33125]++)a.g[58907].h[a.g[33125]]=a.g[59944][a.g[33125]];
75
+ a.g[58907].h[a.g[7358].v.C]=a.g[59944];a.j.push(a.i);a.i=a.g[58907]}else b.h[a.g[16541]]=a.g[7422].apply(null,a.g[59944]);break;case 5381:a.g[16541]=v(a);a.g[14415]=b.h[v(a)];b.h[a.g[16541]]=a.g[14415]-b.h[v(a)];break;case 60964:a.g[1E3]=9;a.g[62634]=b.h[5];b.h[a.g[1E3]]=a.g[62634]>b.h[8];break;case 19072:a.g[12158]=v(a);a.g[21339]=v(a);b.h[a.g[12158]]&&(b.l=a.g[21339]);break;case 51960:a.g[16541]=3;b.h[a.g[16541]]=b.h[2];break;case 30357:a.g[16541]=v(a);a.g[63497]=b.h[v(a)];a.g[57844]=v(a);a.g[63497].P>=
76
+ a.g[63497].N.length?b.l=a.g[57844]:b.h[a.g[16541]]=a.g[63497].N[a.g[63497].P++];break;case 20745:a.g[16541]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[16541]]=a.g[62634]/b.h[v(a)];break;case 11726:a.g[9620]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[9620]]=a.g[62634]&b.h[v(a)];break;case 48568:a.g[5190]=3;b.h[a.g[5190]]=x(a,4,0);break;case 3645:a.g[22553]=v(a);a.g[807]=x(a);a.g[645]=Object.prototype.hasOwnProperty.call(a.m,a.g[807])?a.m[a.g[807]]:void 0;b.h[a.g[22553]]=typeof a.g[645];break;case 46061:a.g[34726]=v(a);
77
+ a.g[59851]=v(a);a.g[62133]={};for(a.g[33125]=0;a.g[33125]<a.g[59851];a.g[33125]++)a.g[37378]=b.h[v(a)],a.g[3547]=b.h[v(a)],a.g[62133][a.g[37378]]=a.g[3547];b.h[a.g[34726]]=a.g[62133];break;case 20491:debugger;break;case 24674:a.g[51590]=1;b.h[a.g[51590]]=b.h[3];break;case 60658:a.g[16541]=v(a);a.g[62634]=b.h[v(a)];b.h[a.g[16541]]=a.g[62634]<<b.h[v(a)];break;case 8845:a.g[16541]=2;b.h[a.g[16541]]=b.h[3];break;default:throw Error("Unknown opcode: "+d+" at pc "+(b.l-1));case 59199:a.g[16541]=v(a);a.g[62634]=
78
+ b.h[v(a)];b.h[a.g[16541]]=a.g[62634]+b.h[v(a)];break;case 5385:a.g[39284]=4;b.h[a.g[39284]]=x(a,3,32194);break;case 20918:a.g[16541]=v(a);a.g[16160]=b.h[v(a)];b.h[a.g[16541]]=a.g[16160]|b.h[v(a)];break;case 25652:a.g[16541]=8;b.h[a.g[16541]]=x(a,0,19941);break;case 51993:a.g[14091]=v(a);a.g[20393]=b.h[v(a)];b.h[a.g[14091]]=a.g[20393]*b.h[v(a)];break;case 29458:a.g[27021]=4;b.h[a.g[27021]]=b.h[0];break;case 38570:throw b.h[v(a)];case 21903:a.g[16541]=v(a);a.g[46842]=b.h[v(a)];b.h[a.g[16541]]=a.g[46842]<
79
+ b.h[v(a)];break;case 17068:a.g[16541]=5;b.h[a.g[16541]]=b.h[0];break;case 26163:a.g[55552]=v(a);a.g[16422]=v(a);b.h[a.g[55552]]||(b.l=a.g[16422]);break;case 28425:a.g[16541]=v(a);b.h[a.g[16541]]=typeof b.h[v(a)];break;case 63202:a.g[60330]=v(a);b.h[a.g[60330]]=b.h[v(a)];break;case 31805:a.g[53668]=v(a);b.h[a.g[53668]]=!b.h[v(a)];break;case 57225:b.l=59}}catch(l){b=null;for(d=a.i;;){if(d.F.length>0){b=d;break}z(a,d);if(a.j.length===0)break;d=a.j.pop();a.i=d}if(!b)throw l;d=b.F.pop();a.j.length=d.T;
80
+ b.h[d.S]=l;b.l=d.U;a.i=b}}}var B={},C;for(C of Object.getOwnPropertyNames(globalThis))B[C]=globalThis[C];if(typeof window!=="undefined"){B.window=window;for(C of Object.getOwnPropertyNames(window))B[C]=window[C]}B.undefined=void 0;B.Infinity=Infinity;B.NaN=NaN;
81
+ A(new p(function(a){a=typeof Buffer!=="undefined"?Buffer.from(a,"base64"):Uint8Array.from(atob(a),function(d){return d.charCodeAt(0)});for(var e=new Uint16Array(a.length/2),b=0;b<e.length;b++)e[b]=a[b*2]|a[b*2+1]<<8;return e}("/bAEAFIAWwCNE5aj8uz9sA2/zi2qlqqWY9r9sBEAWwBtAKFQ52DnYD0OPQ5FrTNmCVGPVRnLbEC2UZV2+Lz4vMyRB7sJb/2wJwBtAHIAtlE/58Zt/bDMkf2wMAByAHQAzJF6Hv2wNgB0AHkA3GnOLT18B2Y/5/2wPwB5AIUAB7v4vKKxBNYNv6KxPXxFrQTWY9o9fO2z/bBPAIUAiABJrVLCPXxj2gMAMgABAAoAAABiYOxxjSLAOWC17PhkHgkVGxChUAYAAQABAAIAorEHAAMABQACAAIABgD4ys9Se2DYsWwHuL3Y64oA2LF+9ZOMEnOsQn71P+rZkjRkJO4lVDANWWONItRkid9pyv7mf70="),0,
82
+ [19940,30138,"IXwsfCp8NnwpfCt8LXw=","rn2sfaN9",void 0,58082],B));
83
+ */
84
+ ```
85
+
86
+ ### Features
87
+
88
+ - [x] functions: call, arguments, return
89
+ - [x] closures and nested functions
90
+ - [x] literals
91
+ - [x] binary expressions
92
+ - [x] unary expressions
93
+ - [x] update expressions
94
+ - [x] if statements
95
+ - [x] while, do-while, for loops
96
+ - [x] get property
97
+ - [x] logical expressions
98
+ - [x] array, object expression
99
+ - [x] function expression
100
+ - [x] default arguments in functions
101
+ - [x] sequence expression
102
+ - [x] conditional expression (ternary operator)
103
+ - [x] delete operator
104
+ - [x] in / instanceof
105
+ - [x] this, new expression
106
+ - [x] arguments
107
+ - [x] Infinity, NaN
108
+ - [x] break/continue
109
+ - [x] switch statement
110
+ - [x] throw statement
111
+ - [x] labeled statements
112
+ - [x] for..in loop
113
+ - [x] RegExp literals
114
+ - [x] try..catch
115
+ - [x] getter/setters
116
+ - [x] debugger;
117
+ - [x] template literals (**ES6**)
118
+
119
+ ### Missing
120
+
121
+ - [ ] try..finally
122
+ - [ ] with statement
123
+ - [ ] arguments.callee, argument parameter syncing
124
+
125
+ ### Hardening
126
+
127
+ - [x] opcode randomization per build
128
+ - [x] property name concealment of vm internals
129
+ - - Google Closure Compiler aggressively renames our class props
130
+ - [x] shuffled handler order
131
+ - [ ] dead handlers
132
+ - [ ] dead bytecode insertion
133
+ - [x] macro opcodes (Combine multiple opcodes into a "macro opcode")
134
+ - [x] micro opcodes (Break opcodes into sub-opcodes)
135
+ - [x] specialized opcodes (Create specific opcodes for opcode+operand pairs)
136
+ - [x] aliased opcodes (Create duplicate opcodes, including variants with shuffled operand order)
137
+ - [x] encoded bytecode array
138
+ - [x] self-modifying bytecode
139
+ - [x] timing checks
140
+ - [ ] low-level bytecode obfuscations
141
+ - [ ] stack protection
142
+ - [ ] control flow integrity
143
+
144
+ ### Options
145
+
146
+ #### `target` ("node"/"browser")
147
+
148
+ Currently has no effect.
149
+
150
+ #### `randomizeOpcodes` (true/false)
151
+
152
+ Randomizes the opcode numbers.
153
+
154
+ #### `shuffleOpcodes` (true/false)
155
+
156
+ Shuffles the order of opcode handlers in the VM runtime.
157
+
158
+ #### `encodeBytecode` (true/false)
159
+
160
+ Encodes the bytecode array.
161
+
162
+ ```js
163
+ // Before
164
+ var BYTECODE = [2, 1, 0, 0, 2, 1, 8, 3, 1, 2, 0, 4, 2, 43, 5, 1, 3, 1, 4, 0, 1, 3, 45, 1];
165
+
166
+ // After
167
+ var BYTECODE = "AgABAAAAAAACAAEACAADAAEAAgAAAAQAAgArAAUAAQADAAEABAAAAAEAAwAtAAEA";
168
+ ```
169
+
170
+ #### `concealConstants` (true/false)
171
+
172
+ Conceals strings and integers in the constant pool.
173
+
174
+ ```js
175
+ // Before
176
+ var CONSTANTS = [/* 0 */"console", /* 1 */"log", /* 2 */"Hello world!", /* 3 */undefined];
177
+
178
+ // After
179
+ var CONSTANTS = [/* 0 */"DaQApB6kAqQdpB+kEaQ=", /* 1 */"TCFOIUUh", /* 2 */"kKK8orait6Kzov2iqaKwopKijaKGosKi", /* 3 */undefined];
180
+ ```
181
+
182
+ #### `macroOpcodes` (true/false)
183
+
184
+ Combines multiple opcodes commonly used from your bytecode.
185
+
186
+ ```js
187
+ // Input Code
188
+ console.log("Hello world!");
189
+ console.log("Hello world!");
190
+
191
+ // Before
192
+ // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
193
+ // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:27
194
+ // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:27
195
+ // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
196
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
197
+ // [2, 1, 0], LOAD_GLOBAL reg[1] = console 2:0-2:7
198
+ // [0, 2, 1], LOAD_CONST reg[2] = "log" 2:0-2:27
199
+ // [8, 3, 1, 2], GET_PROP [3, 1, 2] 2:0-2:27
200
+ // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 2:12-2:26
201
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)2:0-2:27
202
+
203
+ // After
204
+ // [5074, 1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD [1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4]2:0-2:7
205
+ // [5074, 1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD [1, 0, 2, 1, 3, 1, 2, 4, 2, 5, 1, 3, 1, 4]3:0-3:7
206
+
207
+ // What the opcode "LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD" (5074) looks like:
208
+ case 5074:
209
+ // LOAD_GLOBAL
210
+ var dst = this._operand();
211
+ var globalName = this._constant();
212
+ if (!(globalName in this.globals)) {
213
+ throw new ReferenceError(`${globalName} is not defined`);
214
+ }
215
+ frame.regs[dst] = this.globals[globalName];
216
+ // LOAD_CONST
217
+ var dst_1 = this._operand();
218
+ frame.regs[dst_1] = this._constant();
219
+ // GET_PROP
220
+ // dst = regs[obj][regs[key]]
221
+ var dst_2 = this._operand();
222
+ var obj = frame.regs[this._operand()];
223
+ var key = frame.regs[this._operand()];
224
+ frame.regs[dst_2] = obj[key];
225
+ // LOAD_CONST
226
+ var dst_3 = this._operand();
227
+ frame.regs[dst_3] = this._constant();
228
+ // CALL_METHOD
229
+ // dst, receiverReg, calleeReg, argc, [argReg...]
230
+ var dst_4 = this._operand();
231
+ var receiver = frame.regs[this._operand()];
232
+ var callee = frame.regs[this._operand()];
233
+ var argc = this._operand();
234
+ var args = new Array(argc);
235
+ for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
236
+ if (callee && callee[CLOSURE_SYM]) {
237
+ var c = callee[CLOSURE_SYM];
238
+ var f = new Frame(c, frame._pc, frame, receiver, dst_4);
239
+ for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
240
+ f.regs[c.fn.paramCount] = args;
241
+ this._frameStack.push(this._currentFrame);
242
+ this._currentFrame = f;
243
+ } else {
244
+ frame.regs[dst_4] = callee.apply(receiver, args);
245
+ }
246
+ break;
247
+ ```
248
+
249
+ #### `microOpcodes` (true/false)
250
+
251
+ Breaks opcodes into mulitple sub-opcodes.
252
+
253
+ ```js
254
+ // Input Code
255
+ console.log("Hello world!");
256
+
257
+ // Before
258
+ // [2, 1, 0, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
259
+ // [0, 2, 1, 0], LOAD_CONST reg[2] = "log" 1:0-1:27
260
+ // [8, 3, 1, 2], GET_PROP reg[3] = reg[1][reg[2]] 1:0-1:27
261
+ // [0, 4, 2, 0], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
262
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
263
+
264
+ // What the opcode "LOAD_CONST" looks like:
265
+ case OP.LOAD_CONST:
266
+ var dst = this._operand();
267
+ frame.regs[dst] = this._constant();
268
+ break;
269
+
270
+ // After
271
+ // [60, 1], MICRO_LOAD_GLOBAL_0 1 1:0-1:7
272
+ // [61, 0, 0], MICRO_LOAD_GLOBAL_1 [0, 0]
273
+ // [62], MICRO_LOAD_GLOBAL_2
274
+ // [63], MICRO_LOAD_GLOBAL_3
275
+ // [58, 2], MICRO_LOAD_CONST_0 2 1:0-1:27
276
+ // [59, 1, 0], MICRO_LOAD_CONST_1 [1, 0]
277
+ // [64, 3], MICRO_GET_PROP_0 3 1:0-1:27
278
+ // [65, 1], MICRO_GET_PROP_1 1
279
+ // [66, 2], MICRO_GET_PROP_2 2
280
+ // [67], MICRO_GET_PROP_3
281
+ // [58, 4], MICRO_LOAD_CONST_0 4 1:12-1:26
282
+ // [59, 2, 0], MICRO_LOAD_CONST_1 [2, 0]
283
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
284
+
285
+ // What the opcodes "MICRO_LOAD_CONST_0" (58) and "MICRO_LOAD_CONST_1" (59) looks like:
286
+ case 58:
287
+ // MICRO_LOAD_CONST_0
288
+ this._internals[0] = this._operand();
289
+ break;
290
+ case 59:
291
+ // MICRO_LOAD_CONST_1
292
+ frame.regs[this._internals[0]] = this._constant();
293
+ break;
294
+ ```
295
+
296
+ #### `specializedOpcodes` (true/false)
297
+
298
+ Creates specialized opcodes for commonly used opcode+operand pairs.
299
+
300
+ ```js
301
+ // Input Code
302
+ console.log("Hello world!");
303
+
304
+ // Before
305
+ // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
306
+ // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:27
307
+ // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:27
308
+ // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
309
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
310
+
311
+ // What the opcode "LOAD_GLOBAL" looks like:
312
+ case OP.LOAD_CONST:
313
+ var dst = this._operand();
314
+ frame.regs[dst] = this.constants[this._operand()];
315
+ break;
316
+
317
+ // After
318
+ // [16316], LOAD_GLOBAL_1_0 1:0-1:7
319
+ // [43765], LOAD_CONST_2_1 1:0-1:27
320
+ // [58568], GET_PROP_3_1_2 1:0-1:27
321
+ // [35110], LOAD_CONST_4_2 1:12-1:26
322
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
323
+
324
+ // What the opcode "LOAD_GLOBAL_1_0" (16316) looks like:
325
+ case 16316:
326
+ // LOAD_GLOBAL_1_0 (specialized)
327
+ var dst = 1;
328
+ frame.regs[dst] = this.globals[this.constants[0]];
329
+ break;
330
+ ```
331
+
332
+ #### `aliasedOpcodes` (true/false)
333
+
334
+ Creates duplicate opcodes, including variants with shuffled operand order.
335
+
336
+ ```js
337
+ // Input Code
338
+ console.log("Hello, world!");
339
+
340
+ // Before
341
+ // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
342
+ // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
343
+ // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
344
+ // [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
345
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
346
+ // [0, 1, 3], LOAD_CONST reg[1] = undefined
347
+ // [45, 1], RETURN reg[1]
348
+
349
+ // What the opcode "LOAD_GLOBAL" looks like:
350
+ case OP.LOAD_GLOBAL:
351
+ var dst = this._operand();
352
+ frame.regs[dst] = this.globals[this.constants[this._operand()]];
353
+ break;
354
+
355
+ // After
356
+ // [52040, 0, 1], ALIAS_LOAD_GLOBAL_1_0 [0, 1] 1:0-1:7
357
+ // [24862, 1, 2], ALIAS_LOAD_CONST_1_0 [1, 2] 1:0-1:28
358
+ // [25202, 1, 2, 3], ALIAS_GET_PROP_1_2_0 [1, 2, 3] 1:0-1:28
359
+ // [24862, 2, 4], ALIAS_LOAD_CONST_1_0 [2, 4] 1:12-1:27
360
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
361
+ // [24862, 3, 1], ALIAS_LOAD_CONST_1_0 [3, 1]
362
+ // [51807, 1], ALIAS_RETURN_0 1
363
+
364
+ // What the opcode "ALIAS_LOAD_GLOBAL_1_0" (52040) looks like:
365
+ case 52040:
366
+ // ALIAS_LOAD_GLOBAL_1_0 (order: [1,0])
367
+ let _unsortedOperands = [this._operand(), this._operand()];
368
+ let _operands = [_unsortedOperands[1], _unsortedOperands[0]];
369
+ var dst = _operands[0];
370
+ frame.regs[dst] = this.globals[this.constants[_operands[1]]];
371
+ break;
372
+ ```
373
+
374
+ #### `selfModifying` (true/false)
375
+
376
+ Function bodies are replaced upon runtime entry to the real bytecode.
377
+
378
+ ```diff
379
+ // Input Code
380
+ console.log("Hello, world!");
381
+
382
+ // Before
383
+ // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
384
+ // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
385
+ // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
386
+ // [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
387
+ // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
388
+ // [0, 1, 3], LOAD_CONST reg[1] = undefined
389
+ // [45, 1], RETURN reg[1]
390
+
391
+ // After
392
+ // [56, 4, 28, 50], PATCH [4, 28, 50]
393
+ -// [52], FOR_IN_SETUP <-- 22 ints of garbage code
394
+ -// [39], JUMP
395
+ -// [12], SUB
396
+ -// [12], SUB
397
+ -// [2], LOAD_GLOBAL
398
+ -// [28], LOOSE_EQ
399
+ -// [12], SUB
400
+ -// [46], THROW
401
+ -// [18], BXOR
402
+ -// [8], GET_PROP
403
+ -// [5], MOVE
404
+ -// [55], TRY_END
405
+ -// [57], DEBUGGER
406
+ -// [46], THROW
407
+ -// [23], GT
408
+ -// [48], BUILD_ARRAY
409
+ -// [28], LOOSE_EQ
410
+ -// [18], BXOR
411
+ -// [2], LOAD_GLOBAL
412
+ -// [50], DEFINE_GETTER
413
+ -// [4], LOAD_THIS
414
+ -// [24], LTE
415
+ // [45, 1], RETURN reg[1]
416
+ +// [2, 1, 0], LOAD_GLOBAL reg[1] = console <-- 22 ints of real code
417
+ +// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
418
+ +// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
419
+ +// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
420
+ +// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
421
+ +// [0, 1, 3], LOAD_CONST reg[1] = undefined
422
+ ```
423
+
424
+ #### `timingChecks` (true/false)
425
+
426
+ Detects the use of debuggers by checking for >1second pauses. May break code with slow sync tasks.
427
+
428
+
429
+ #### `minify` (true/false)
430
+
431
+ Minifies the final code with Google Closure Compiler. Renames the VM class properties.
432
+
433
+ ### No Try Finally
434
+
435
+ While Try Catch is supported, Try..Finally is not. You can use Try..Finally by defining an outside helper function:
436
+
437
+ ```js
438
+ function TryFinally(cb, _finally) {
439
+ try {
440
+ return { value: cb() }
441
+ } catch (error) {
442
+ return { error };
443
+ } finally {
444
+ _finally()
445
+ }
446
+ }
447
+ ```
448
+
449
+ ### ES5 Only
450
+
451
+ This VM Compiler only supports ES5 JavaScript. Most ES6+ features are syntax sugar that can be transpiled down relatively easily. This is a design decision to keep the VM wrapper simple and the bytecode more uniform. Having opcodes dedicated for classes and methods makes them standout more for attackers to debug easier. Keeping things simple enables easier hardening improvements.
452
+
453
+ Please transpile your code down first using [Babel](https://github.com/babel/babel).
454
+
455
+ ### Project
456
+
457
+ - Register based VM
458
+ - Lua-style closure and upvalue model
459
+ - CPython-style opcodes and codegen
460
+ - Compiler is in src/compiler.ts
461
+ - Runtime is in src/runtime.ts
462
+ - "Typescript"
463
+ - - This "Typescript" project uses Node's new flag `--experimental-strip-types`. This means we can run `node index.ts` directly!
464
+
465
+ ### Best practices
466
+
467
+ - **Don't rely on "function.length"**
468
+ - Avoid undeclared variables
469
+ - Don't rely on "function.name"
470
+ - Don't use eval() to reference or modify local variables
471
+
472
+ ### Use with JS-Confuser
473
+
474
+ JS-Confuser is recommended to be applied *after* virtualizing your source code. JS-Confuser's CFF can safeguard and obfuscate your VM internals - adding a layer of obscurity and preventing analysis of the opcodes.
475
+
476
+ ```js
477
+ import JsConfuser from "js-confuser";
478
+ import JsConfuserVM from "js-confuser-vm";
479
+ import { readFile, writeFile } from "fs/promises";
480
+
481
+ async function main() {
482
+ // Read input code
483
+ const sourceCode = await readFile("input.js", "utf8");
484
+
485
+ const { code: virtualized } = await JsConfuserVM.obfuscate(sourceCode, {
486
+ target: "browser",
487
+ randomizeOpcodes: true
488
+ });
489
+ const { code: obfuscated } = await JsConfuser.obfuscate(virtualized, {
490
+ target: "browser",
491
+ preset: "medium",
492
+ pack: false,
493
+ globalConcealing: false,
494
+ });
495
+
496
+ // Write output file
497
+ await writeFile("output.js", obfuscated, "utf8");
498
+ }
499
+
500
+ main().catch(console.error);
501
+ ```
502
+
503
+ ### WIP
504
+
505
+ - 178 tests, 91.18% coverage
506
+ - [Test262 (es5-tests)](https://github.com/tc39/test262/tree/es5-tests) percentage: 66.67%
507
+
508
+ ### Made with AI
509
+
510
+ This project has been created with the help of AI. Expect issues.
511
+
512
+ ### License
513
+
514
+ MIT License
@@ -1,11 +1,13 @@
1
1
  import { generate } from "@babel/generator";
2
2
  import { parse } from "@babel/parser";
3
3
  import { applyMacroOpcodes } from "./transforms/runtime/macroOpcodes.js";
4
+ import { applyMicroOpcodes } from "./transforms/runtime/microOpcodes.js";
5
+ import { applyInteralVariablesToRuntime } from "./transforms/runtime/internalVariables.js";
4
6
  import { applyShuffleOpcodes } from "./transforms/runtime/shuffleOpcodes.js";
5
7
  import { applyMinify } from "./transforms/runtime/minify.js";
6
8
  import { applySpecializedOpcodes } from "./transforms/runtime/specializedOpcodes.js";
7
9
  import { applyAliasedOpcodes } from "./transforms/runtime/aliasedOpcodes.js";
8
- export async function obfuscateRuntime(runtime, bytecode, options, compiler) {
10
+ export async function obfuscateRuntime(runtime, bytecode, options, compiler, generateBytecodeComment) {
9
11
  let ast;
10
12
  try {
11
13
  ast = parse(runtime, {
@@ -19,7 +21,15 @@ export async function obfuscateRuntime(runtime, bytecode, options, compiler) {
19
21
 
20
22
  // Specialized opcode cases must be applied BEFORE shuffleOpcodes
21
23
  if (options.specializedOpcodes) {
22
- applySpecializedOpcodes(ast, bytecode, compiler);
24
+ applySpecializedOpcodes(ast, compiler);
25
+ }
26
+ if (options.microOpcodes) {
27
+ applyInteralVariablesToRuntime(ast, compiler);
28
+ }
29
+
30
+ // Micro opcode cases must be applied BEFORE shuffleOpcodes
31
+ if (options.microOpcodes && Object.keys(compiler.MICRO_OPS).length > 0) {
32
+ applyMicroOpcodes(ast, compiler);
23
33
  }
24
34
 
25
35
  // Macro opcode cases must be applied BEFORE shuffleOpcodes
@@ -45,6 +55,9 @@ export async function obfuscateRuntime(runtime, bytecode, options, compiler) {
45
55
  });
46
56
  }
47
57
 
58
+ // Add comment here for more accurate opcode names
59
+ generated = generateBytecodeComment() + "\n" + generated;
60
+
48
61
  // Minify code?
49
62
  if (options.minify) {
50
63
  try {