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/README.MD DELETED
@@ -1,450 +0,0 @@
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
- specializedOpcodes: true, // create specialized opcodes for commonly used opcode+operand pairs?
45
- aliasedOpcodes: true, // create duplicate opcodes for commonly used opcodes?
46
- timingChecks: true, // add timing checks to detect debuggers?
47
- minify: true // pass final output through Google Closure Compiler? (Renames VM class properties)
48
- }).then(result => {
49
- console.log(result.code)
50
- })
51
-
52
- /*
53
- var c=Symbol();function ha(b,k){this.A=b;this.H=k;this.I=!1;this.J=void 0}function ia(b){return b.I?b.J:b.A.g[b.H]}function ja(b,k){b.I?b.J=k:b.A.g[b.H]=k}function y(b){this.v=b;this.D=[];this.prototype={}}function B(b,k,a){this.K=b;this.g=Array(b.v.L).fill(void 0);this.i=b.v.P;this.V=k!==void 0?k:void 0;this.G=a!==void 0?a:0;this.m=null;this.F=[]}function C(b,k,a,g,t){this.o=b;this.l=g;this.u=t;this.j=[];this.B=[];this.h=new B(new y({C:0,L:a,P:k}),void 0,0)}function D(b){return b.o[b.h.i++]}
54
- function ka(b,k,a){for(var g=0;g<b.B.length;g++){var t=b.B[g];if(t.A===k&&t.H===a)return t}t=new ha(k,a);b.B.push(t);return t}function R(b,k){b.B=b.B.filter(function(a){return a.A===k?(a.J=a.A.g[a.H],a.I=!0,!1):!0})}
55
- function S(b){for(var k=performance.now();;){var a=b.h;if(a.i>=b.o.length)break;var g=b.o[a.i++],t=performance.now(),la=t-k>1E3;k=t;if(la){for(var e=0;e<b.o.length;e++)b.o[e]=0;a.g.fill(void 0);g=50848;a.i=b.o.length}try{switch(g){case 6517:var d=D(b),f=a.g[D(b)];a.g[d]=f===a.g[D(b)];break;case 50054:d=D(b);var p=a.g[D(b)],x=D(b),m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){var v=p[c],r=new B(v,b.u,d);for(e=0;e<m.length;e++)r.g[e]=m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=p.apply(null,
56
- m);break;case 5504:var l=a.g[D(b)],n=a.g[D(b)],ma=a.g[D(b)],z=Object.getOwnPropertyDescriptor(l,n);a={set:ma,configurable:!0,enumerable:!0};z&&typeof z.get==="function"&&(a.get=z.get);Object.defineProperty(l,n,a);break;case 36488:d=6;a.g[d]=b.l[1];break;case 29667:d=D(b);f=a.g[D(b)];a.g[d]=f|a.g[D(b)];break;case 29500:d=D(b);a.g[d]=+a.g[D(b)];break;case 16142:d=D(b);var V=a.g[D(b)];p=a.g[D(b)];x=D(b);m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){v=p[c];r=new B(v,V,d);for(e=0;e<m.length;e++)r.g[e]=
57
- m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=p.apply(V,m);break;case 2036:d=D(b);l=a.g[D(b)];n=a.g[D(b)];a.g[d]=l[n];break;case 50848:a.i=D(b);break;case 46964:d=3;a.g[d]=b.l[3];break;case 17567:d=D(b);f=a.g[D(b)];a.g[d]=f/a.g[D(b)];break;case 46176:var h=a.g[3];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);var u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 40235:d=D(b);f=a.g[D(b)];a.g[d]=f!=a.g[D(b)];break;case 2835:d=5;a.g[d]=b.l[0];break;case 53433:var na=D(b),
58
- W=D(b),oa=D(b);for(a=W;a<oa;a++)b.o[na+(a-W)]=b.o[a];break;case 42352:l=a.g[D(b)];n=a.g[D(b)];var E=a.g[D(b)];Reflect.set(l,n,E);break;case 23743:d=4;a.g[d]=a.g[5];break;case 50800:d=1;a.g[d]=a.g[3];break;case 36349:throw a.g[D(b)];case 53101:d=4;a.g[d]=b.l[1];break;case 545:d=D(b);f=a.g[D(b)];a.g[d]=f>a.g[D(b)];break;case 55196:var pa=D(b);b.u[b.l[pa]]=a.g[D(b)];break;case 22757:var w=4,q=76;a.g[w]||(a.i=q);break;case 58785:d=3;a.g[d]=b.l[1];break;case 25598:d=D(b);a.g[d]=a.V;break;default:throw Error("Unknown opcode: "+
59
- g+" at pc "+(a.i-1));case 53665:d=5;l=a.g[3];n=a.g[4];a.g[d]=l[n];break;case 9914:d=3;a.g[d]=a.g[4];break;case 14527:d=D(b);f=a.g[D(b)];a.g[d]=f^a.g[D(b)];break;case 4831:d=D(b);a.g[d]=-a.g[D(b)];break;case 41053:d=D(b);a.g[d]=typeof a.g[D(b)];break;case 23985:d=D(b);D(b);a.g[d]=void 0;break;case 9348:d=3;a.g[d]=b.u[b.l[4]];break;case 1904:a.F.push({T:D(b),R:D(b),S:b.j.length});break;case 14261:w=9;q=107;a.g[w]||(a.i=q);break;case 44576:d=D(b);l=a.g[D(b)];n=a.g[D(b)];a.g[d]=delete l[n];break;case 6789:w=
60
- D(b);q=D(b);a.g[w]||(a.i=q);break;case 17207:w=D(b);q=D(b);a.g[w]&&(a.i=q);break;case 21753:d=D(b);var X=b.l[D(b)];E=Object.prototype.hasOwnProperty.call(b.u,X)?b.u[X]:void 0;a.g[d]=typeof E;break;case 9073:d=3;a.g[d]=a.g[6];break;case 26633:d=D(b);a.g[d]=b.l[D(b)];break;case 57348:d=D(b);f=a.g[D(b)];a.g[d]=f>=a.g[D(b)];break;case 43802:d=D(b);var Y=D(b),Z=Array(Y);for(e=0;e<Y;e++)Z[e]=a.g[D(b)];a.g[d]=Z;break;case 38611:d=4;a.g[d]=b.l[5];break;case 43097:d=5;a.g[d]=b.l[2];break;case 16365:d=D(b);
61
- a.g[d]=b.u[b.l[D(b)]];break;case 63592:d=D(b);p=a.g[D(b)];x=D(b);m=Array(x);for(e=0;e<x;e++)m[e]=a.g[D(b)];if(p&&p[c]){v=p[c];var aa=Object.create(v.prototype||null);r=new B(v,aa,d);r.m=aa;for(e=0;e<m.length;e++)r.g[e]=m[e];r.g[v.v.C]=m;b.j.push(b.h);b.h=r}else a.g[d]=Reflect.construct(p,m);break;case 56057:d=3;a.g[d]=b.l[2];break;case 20064:d=3;a.g[d]=a.g[2];break;case 46958:d=D(b);f=a.g[D(b)];a.g[d]=f==a.g[D(b)];break;case 5066:d=D(b);f=a.g[D(b)];a.g[d]=f-a.g[D(b)];break;case 16426:h=a.g[4];R(b,
62
- a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 47875:a.i=45;break;case 51418:d=2;a.g[d]=a.g[5];break;case 25520:d=D(b);f=a.g[D(b)];a.g[d]=f*a.g[D(b)];break;case 41854:d=D(b);l=a.g[D(b)];g=[];if(l!==null&&l!==void 0)for(var ba=Object.create(null),F=Object(l);F!==null;){var ca=Object.getOwnPropertyNames(F);for(e=0;e<ca.length;e++){var H=ca[e];if(!(H in ba)){ba[H]=!0;var da=Object.getOwnPropertyDescriptor(F,H);da&&da.enumerable&&
63
- g.push(H)}}F=Object.getPrototypeOf(F)}a.g[d]={N:g,O:0};break;case 45737:d=D(b);var qa=D(b);g={};for(e=0;e<qa;e++)n=a.g[D(b)],E=a.g[D(b)],g[n]=E;a.g[d]=g;break;case 3520:d=D(b);f=a.g[D(b)];a.g[d]=f<<a.g[D(b)];break;case 34870:d=D(b);a.g[d]=!a.g[D(b)];break;case 11596:d=D(b);a.g[d]=~a.g[D(b)];break;case 16818:w=9;q=25;a.g[w]||(a.i=q);break;case 38378:d=D(b);a.g[d]=ia(a.K.D[D(b)]);break;case 50935:d=D(b);f=a.g[D(b)];a.g[d]=f>>>a.g[D(b)];break;case 7720:d=0;a.g[d]=a.g[7];break;case 61003:d=8;a.g[d]=b.l[1];
64
- break;case 40007:d=D(b);f=a.g[D(b)];a.g[d]=f&a.g[D(b)];break;case 48619:debugger;break;case 21396:d=D(b);a.g[d]=a.g[D(b)];break;case 34467:d=D(b);a.g[d]=D(b);break;case 26361:h=a.g[D(b)];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 7585:l=a.g[D(b)];n=a.g[D(b)];var ra=a.g[D(b)];z=Object.getOwnPropertyDescriptor(l,n);a={get:ra,configurable:!0,enumerable:!0};z&&typeof z.set==="function"&&(a.set=z.set);Object.defineProperty(l,
65
- n,a);break;case 23121:var sa=D(b);ja(a.K.D[sa],a.g[D(b)]);break;case 51231:d=D(b);f=a.g[D(b)];a.g[d]=f%a.g[D(b)];break;case 22797:d=D(b);var I=a.g[D(b)],ta=D(b);I.O>=I.N.length?a.i=ta:a.g[d]=I.N[I.O++];break;case 37593:a.i=9;break;case 504:d=D(b);f=a.g[D(b)];a.g[d]=f>>a.g[D(b)];break;case 5174:h=a.g[5];R(b,a);if(b.j.length===0)return h;a.m===null||typeof h==="object"&&h!==null||(h=a.m);u=b.j.pop();u.g[a.G]=h;b.h=u;break;case 9939:d=5;a.g[d]=a.g[0];break;case 41972:d=5;f=a.g[2];a.g[d]=f+a.g[4];break;
66
- case 49424:d=7;f=a.g[0];a.g[d]=f-a.g[6];break;case 26807:d=D(b);f=a.g[D(b)];a.g[d]=f<a.g[D(b)];break;case 21843:d=4;a.g[d]=a.g[0];break;case 19103:d=D(b);f=a.g[D(b)];a.g[d]=f in a.g[D(b)];break;case 40335:d=D(b);l=a.g[D(b)];var M=a.g[D(b)];if(typeof M==="function")a.g[d]=l instanceof M;else{var ua=M.prototype;q=Object.getPrototypeOf(l);for(g=!1;q!==null;){if(q===ua){g=!0;break}q=Object.getPrototypeOf(q)}a.g[d]=g}break;case 47743:d=D(b);f=a.g[D(b)];a.g[d]=f+a.g[D(b)];break;case 27740:d=4;f=a.g[2];
67
- a.g[d]=f<=a.g[3];break;case 51318:d=D(b);f=a.g[D(b)];a.g[d]=f!==a.g[D(b)];break;case 64752:d=2;a.g[d]=a.g[3];break;case 1927:a.i=91;break;case 22484:d=5;f=a.g[2];a.g[d]=f+a.g[3];break;case 64028:d=D(b);var va=D(b),wa=D(b),xa=D(b),ea=D(b),J=Array(ea);for(e=0;e<ea;e++){var ya=D(b),za=D(b);J[e]={U:ya,M:za}}var G=new y({C:wa,L:xa,P:va,W:J});for(e=0;e<J.length;e++){var N=J[e];N.U?G.D.push(ka(b,a,N.M)):G.D.push(a.K.D[N.M])}var K=b,Q=function(A){return function(){for(var O=Array.prototype.slice.call(arguments),
68
- fa=new C(K.o,0,A.v.L,K.l,K.u),P=new B(A,this==null?K.u:this,0),L=0;L<O.length;L++)P.g[L]=O[L];P.g[A.v.C]=O;fa.h=P;return S(fa)}}(G);Q[c]=G;Q.prototype=G.prototype;a.g[d]=Q;break;case 30313:d=9;f=a.g[5];a.g[d]=f>a.g[8];break;case 50428:a.F.pop();break;case 35999:d=D(b),f=a.g[D(b)],a.g[d]=f<=a.g[D(b)]}}catch(A){a=null;for(g=b.h;;){if(g.F.length>0){a=g;break}R(b,g);if(b.j.length===0)break;g=b.j.pop();b.h=g}if(!a)throw A;g=a.F.pop();b.j.length=g.S;a.g[g.R]=A;a.i=g.T;b.h=a}}}var T={},U;
69
- for(U of Object.getOwnPropertyNames(globalThis))T[U]=globalThis[U];typeof window!=="undefined"&&(T.window=window);T.undefined=void 0;T.Infinity=Infinity;T.NaN=NaN;
70
- S(new C(function(b){b=typeof Buffer!=="undefined"?Buffer.from(b,"base64"):Uint8Array.from(atob(b),function(g){return g.charCodeAt(0)});for(var k=new Uint16Array(b.length/2),a=0;a<k.length;a++)k[a]=b[a*2]|b[a*2+1]<<8;return k}("udAEAHIAdwAOP263K53rveu9udANAHcAgwCUUyud6pWUUx/IHPo3Q/2Nj50hArdobre50B0AgwCGAMoT+WYc+rnQJACGAI8AIQJo+Dxzo4afRO0/98YhAqDGudAxAI8AoQCwY7FdoR0NWX+6PHNRWuu9f7qUU7dot2ifRJ+MBOCAFYAVt2i50EcAoQCmAJ+M/Y1ut3ClDVm50FAApgCoAF2gHPq50FYAqACtAONzoMbfEvQHqbK50F8ArQC5AONzNoj4AXAH43P3xpRT/MSc14bDdsjADbnQbwC5ALwAPHM3Q+0/EwvayIiOcSNTVdMmiI4QwSgeS+5pdrJB1Fe/XPD8uibZkipAWag2FBz6AwBSAAEACgAAAHDGoeXw/HS3XGzlWIQk05ah0YbDBgABAAEAAgAOPwcAAwAFAAIAAgAGAGBObc/0o9rIA7v52mC0EwvayIiOcSNTVdMmiI4QwSgeS+5pdrU31Fe/XPD8uiaHBypAWag2FA=="),32,
71
- 0,[0,1,void 0,25,"console","log"],T));
72
- */
73
- ```
74
-
75
- ### Features
76
-
77
- - [x] functions: call, arguments, return
78
- - [x] closures and nested functions
79
- - [x] literals
80
- - [x] binary expressions
81
- - [x] unary expressions
82
- - [x] update expressions
83
- - [x] if statements
84
- - [x] while, do-while, for loops
85
- - [x] get property
86
- - [x] logical expressions
87
- - [x] array, object expression
88
- - [x] function expression
89
- - [x] default arguments in functions
90
- - [x] sequence expression
91
- - [x] conditional expression (ternary operator)
92
- - [x] delete operator
93
- - [x] in / instanceof
94
- - [x] this, new expression
95
- - [x] arguments
96
- - [x] Infinity, NaN
97
- - [x] break/continue
98
- - [x] switch statement
99
- - [x] throw statement
100
- - [x] labeled statements
101
- - [x] for..in loop
102
- - [x] RegExp literals
103
- - [x] try..catch
104
- - [x] getter/setters
105
- - [x] debugger;
106
-
107
- ### Missing
108
-
109
- - [ ] try..finally
110
- - [ ] with statement
111
- - [ ] arguments.callee, argument parameter syncing
112
-
113
- ### Hardening
114
-
115
- - [x] opcode randomization per build
116
- - [x] property name concealment of vm internals
117
- - - Google Closure Compiler aggressively renames our class props
118
- - [x] shuffled handler order
119
- - [ ] dead handlers
120
- - [ ] dead bytecode insertion
121
- - [x] macro opcodes (Combine multiple opcodes into a "macro opcode")
122
- - [x] specialized opcodes (Create specific opcodes for opcode+operand pairs)
123
- - [x] aliased opcodes (create duplicate opcodes, including variants with shuffled operand order)
124
- - [x] encoded bytecode array
125
- - [x] self-modifying bytecode
126
- - [x] timing checks
127
- - [ ] low-level bytecode obfuscations
128
- - [ ] stack protection
129
- - [ ] control flow integrity
130
-
131
- ### Options
132
-
133
- #### `target` ("node"/"browser")
134
-
135
- Currently has no effect.
136
-
137
- #### `randomizeOpcodes` (true/false)
138
-
139
- Randomizes the opcode numbers.
140
-
141
- #### `shuffleOpcodes` (true/false)
142
-
143
- Shuffles the order of opcode handlers in the VM runtime.
144
-
145
- #### `encodeBytecode` (true/false)
146
-
147
- Encodes the bytecode array.
148
-
149
- ```js
150
- // Before
151
- 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];
152
-
153
- // After
154
- var BYTECODE = "AgABAAAAAAACAAEACAADAAEAAgAAAAQAAgArAAUAAQADAAEABAAAAAEAAwAtAAEA";
155
- ```
156
-
157
- #### `concealConstants` (true/false)
158
-
159
- Conceals strings and integers in the constant pool.
160
-
161
- ```js
162
- // Before
163
- var CONSTANTS = [/* 0 */"console", /* 1 */"log", /* 2 */"Hello world!", /* 3 */undefined];
164
-
165
- // After
166
- var CONSTANTS = [/* 0 */"DaQApB6kAqQdpB+kEaQ=", /* 1 */"TCFOIUUh", /* 2 */"kKK8orait6Kzov2iqaKwopKijaKGosKi", /* 3 */undefined];
167
- ```
168
-
169
- #### `macroOpcodes` (true/false)
170
-
171
- Combines multiple opcodes commonly used from your bytecode.
172
-
173
- ```js
174
- // Input Code
175
- console.log("Hello world!");
176
- console.log("Hello world!");
177
-
178
- // Before
179
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 2:0-2:7
180
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 2:0-2:27
181
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 2:0-2:27
182
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 2:12-2:26
183
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)2:0-2:27
184
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 3:0-3:7
185
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 3:0-3:27
186
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 3:0-3:27
187
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 3:12-3:26
188
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)3:0-3:27
189
-
190
- // After
191
- // [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
192
- // [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
193
-
194
- // What the opcode "LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST,CALL_METHOD" (5074) looks like:
195
- case 5074:
196
- // LOAD_GLOBAL
197
- var dst = this._operand();
198
- frame.regs[dst] = this.globals[this.constants[this._operand()]];
199
- // LOAD_CONST
200
- var dst = this._operand();
201
- frame.regs[dst] = this.constants[this._operand()];
202
- // GET_PROP
203
- // dst = regs[obj][regs[key]]
204
- var dst = this._operand();
205
- var obj = frame.regs[this._operand()];
206
- var key = frame.regs[this._operand()];
207
- frame.regs[dst] = obj[key];
208
- // LOAD_CONST
209
- var dst = this._operand();
210
- frame.regs[dst] = this.constants[this._operand()];
211
- // CALL_METHOD
212
- // dst, receiverReg, calleeReg, argc, [argReg...]
213
- var dst = this._operand();
214
- var receiver = frame.regs[this._operand()];
215
- var callee = frame.regs[this._operand()];
216
- var argc = this._operand();
217
- var args = new Array(argc);
218
- for (var i = 0; i < argc; i++) args[i] = frame.regs[this._operand()];
219
- if (callee && callee[CLOSURE_SYM]) {
220
- var c = callee[CLOSURE_SYM];
221
- var f = new Frame(c, frame._pc, frame, receiver, dst);
222
- for (var i = 0; i < args.length; i++) f.regs[i] = args[i];
223
- f.regs[c.fn.paramCount] = args;
224
- this._frameStack.push(this._currentFrame);
225
- this._currentFrame = f;
226
- } else {
227
- frame.regs[dst] = callee.apply(receiver, args);
228
- }
229
- break;
230
- ```
231
-
232
- #### `specializedOpcodes` (true/false)
233
-
234
- Creates specialized opcodes for commonly used opcode+operand pairs.
235
-
236
- ```js
237
- // Input Code
238
- console.log("Hello world!");
239
-
240
- // Before
241
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
242
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:27
243
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:27
244
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
245
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
246
-
247
- // What the opcode "LOAD_GLOBAL" looks like:
248
- case OP.LOAD_CONST:
249
- var dst = this._operand();
250
- frame.regs[dst] = this.constants[this._operand()];
251
- break;
252
-
253
- // After
254
- // [16316], LOAD_GLOBAL_1_0 1:0-1:7
255
- // [43765], LOAD_CONST_2_1 1:0-1:27
256
- // [58568], GET_PROP_3_1_2 1:0-1:27
257
- // [35110], LOAD_CONST_4_2 1:12-1:26
258
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:27
259
-
260
- // What the opcode "LOAD_GLOBAL_1_0" (16316) looks like:
261
- case 16316:
262
- // LOAD_GLOBAL_1_0 (specialized)
263
- var dst = 1;
264
- frame.regs[dst] = this.globals[this.constants[0]];
265
- break;
266
- ```
267
-
268
- #### `aliasedOpcodes` (true/false)
269
-
270
- Creates duplicate opcodes, including variants with shuffled operand order.
271
-
272
- ```js
273
- // Input Code
274
- console.log("Hello, world!");
275
-
276
- // Before
277
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
278
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
279
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
280
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
281
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
282
- // [0, 1, 3], LOAD_CONST reg[1] = undefined
283
- // [45, 1], RETURN reg[1]
284
-
285
- // What the opcode "LOAD_GLOBAL" looks like:
286
- case OP.LOAD_GLOBAL:
287
- var dst = this._operand();
288
- frame.regs[dst] = this.globals[this.constants[this._operand()]];
289
- break;
290
-
291
- // After
292
- // [52040, 0, 1], ALIAS_LOAD_GLOBAL_1_0 [0, 1] 1:0-1:7
293
- // [24862, 1, 2], ALIAS_LOAD_CONST_1_0 [1, 2] 1:0-1:28
294
- // [25202, 1, 2, 3], ALIAS_GET_PROP_1_2_0 [1, 2, 3] 1:0-1:28
295
- // [24862, 2, 4], ALIAS_LOAD_CONST_1_0 [2, 4] 1:12-1:27
296
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
297
- // [24862, 3, 1], ALIAS_LOAD_CONST_1_0 [3, 1]
298
- // [51807, 1], ALIAS_RETURN_0 1
299
-
300
- // What the opcode "ALIAS_LOAD_GLOBAL_1_0" (52040) looks like:
301
- case 52040:
302
- // ALIAS_LOAD_GLOBAL_1_0 (order: [1,0])
303
- let _unsortedOperands = [this._operand(), this._operand()];
304
- let _operands = [_unsortedOperands[1], _unsortedOperands[0]];
305
- var dst = _operands[0];
306
- frame.regs[dst] = this.globals[this.constants[_operands[1]]];
307
- break;
308
- ```
309
-
310
- #### `selfModifying` (true/false)
311
-
312
- Function bodies are replaced upon runtime entry to the real bytecode.
313
-
314
- ```diff
315
- // Input Code
316
- console.log("Hello, world!");
317
-
318
- // Before
319
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
320
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
321
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
322
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
323
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
324
- // [0, 1, 3], LOAD_CONST reg[1] = undefined
325
- // [45, 1], RETURN reg[1]
326
-
327
- // After
328
- // [56, 4, 28, 50], PATCH [4, 28, 50]
329
- -// [52], FOR_IN_SETUP <-- 22 ints of garbage code
330
- -// [39], JUMP
331
- -// [12], SUB
332
- -// [12], SUB
333
- -// [2], LOAD_GLOBAL
334
- -// [28], LOOSE_EQ
335
- -// [12], SUB
336
- -// [46], THROW
337
- -// [18], BXOR
338
- -// [8], GET_PROP
339
- -// [5], MOVE
340
- -// [55], TRY_END
341
- -// [57], DEBUGGER
342
- -// [46], THROW
343
- -// [23], GT
344
- -// [48], BUILD_ARRAY
345
- -// [28], LOOSE_EQ
346
- -// [18], BXOR
347
- -// [2], LOAD_GLOBAL
348
- -// [50], DEFINE_GETTER
349
- -// [4], LOAD_THIS
350
- -// [24], LTE
351
- // [45, 1], RETURN reg[1]
352
- +// [2, 1, 0], LOAD_GLOBAL reg[1] = console <-- 22 ints of real code
353
- +// [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
354
- +// [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
355
- +// [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
356
- +// [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
357
- +// [0, 1, 3], LOAD_CONST reg[1] = undefined
358
- ```
359
-
360
- #### `timingChecks` (true/false)
361
-
362
- Detects the use of debuggers by checking for >1second pauses. May break code with slow sync tasks.
363
-
364
-
365
- #### `minify` (true/false)
366
-
367
- Minifies the final code with Google Closure Compiler. Renames the VM class properties.
368
-
369
- ### No Try Finally
370
-
371
- While Try Catch is supported, Try..Finally is not. You can use Try..Finally by defining an outside helper function:
372
-
373
- ```js
374
- function TryFinally(cb, _finally) {
375
- try {
376
- return { value: cb() }
377
- } catch (error) {
378
- return { error };
379
- } finally {
380
- _finally()
381
- }
382
- }
383
- ```
384
-
385
- ### ES5 Only
386
-
387
- 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.
388
-
389
- Please transpile your code down first using [Babel](https://github.com/babel/babel).
390
-
391
- ### Project
392
-
393
- - Register base VM
394
- - Lua-style closure and upvalue model
395
- - CPython-style opcodes and codegen
396
- - Compiler is in src/compiler.ts
397
- - Runtime is in src/runtime.ts
398
- - "Typescript"
399
- - - This "Typescript" project uses Node's new flag `--experimental-strip-types`. This means we can run `node index.ts` directly!
400
-
401
- ### Best practices
402
-
403
- - **Don't rely on "function.length"**
404
- - Avoid undeclared variables
405
- - Don't rely on "function.name"
406
- - Don't use eval() to reference or modify local variables
407
-
408
- ### Use with JS-Confuser
409
-
410
- 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.
411
-
412
- ```js
413
- import JsConfuser from "js-confuser";
414
- import JsConfuserVM from "js-confuser-vm";
415
- import { readFile, writeFile } from "fs/promises";
416
-
417
- async function main() {
418
- // Read input code
419
- const sourceCode = await readFile("input.js", "utf8");
420
-
421
- const { code: virtualized } = await JsConfuserVM.obfuscate(sourceCode, {
422
- target: "browser",
423
- randomizeOpcodes: true
424
- });
425
- const { code: obfuscated } = await JsConfuser.obfuscate(virtualized, {
426
- target: "browser",
427
- preset: "medium",
428
- pack: false,
429
- globalConcealing: false,
430
- });
431
-
432
- // Write output file
433
- await writeFile("output.js", obfuscated, "utf8");
434
- }
435
-
436
- main().catch(console.error);
437
- ```
438
-
439
- ### WIP
440
-
441
- - 178 tests, 91.18% coverage
442
- - [Test262 (es5-tests)](https://github.com/tc39/test262/tree/es5-tests) percentage: 66.67%
443
-
444
- ### Made with AI
445
-
446
- This project has been created with the help of AI. Expect issues.
447
-
448
- ### License
449
-
450
- MIT License
package/src/utilts.ts DELETED
@@ -1,3 +0,0 @@
1
- export function escapeRegex(s: string): string {
2
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3
- }