js-confuser-vm 0.0.3 → 0.0.5
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.
- package/CHANGELOG.md +125 -28
- package/LICENSE +21 -21
- package/README.MD +370 -196
- package/babel-plugin-inline-runtime.cjs +34 -34
- package/babel.config.json +23 -23
- package/dist/build-runtime.js +53 -0
- package/dist/compiler.js +107 -117
- package/dist/runtime.js +78 -84
- package/dist/transforms/bytecode/macroOpcodes.js +152 -0
- package/dist/transforms/{resolveContants.js → bytecode/resolveContants.js} +16 -6
- package/dist/transforms/bytecode/resolveLabels.js +80 -0
- package/dist/transforms/{selfModifying.js → bytecode/selfModifying.js} +33 -33
- package/dist/transforms/bytecode/specializedOpcodes.js +103 -0
- package/dist/transforms/runtime/macroOpcodes.js +88 -0
- package/dist/transforms/runtime/minify.js +1 -0
- package/dist/transforms/runtime/shuffleOpcodes.js +20 -0
- package/dist/transforms/runtime/specializedOpcodes.js +102 -0
- package/dist/transforms/utils/op-utils.js +25 -0
- package/dist/{random.js → transforms/utils/random-utils.js} +3 -3
- package/dist/types.js +4 -2
- package/index.ts +34 -22
- package/jest-strip-types.js +10 -10
- package/jest.config.js +35 -28
- package/package.json +49 -48
- package/src/build-runtime.ts +57 -0
- package/src/compiler.ts +2069 -2066
- package/src/index.ts +14 -14
- package/src/minify.ts +21 -21
- package/src/options.ts +14 -12
- package/src/runtime.ts +771 -779
- package/src/transforms/bytecode/macroOpcodes.ts +177 -0
- package/src/transforms/bytecode/resolveContants.ts +62 -0
- package/src/transforms/bytecode/resolveLabels.ts +107 -0
- package/src/transforms/{selfModifying.ts → bytecode/selfModifying.ts} +37 -40
- package/src/transforms/bytecode/specializedOpcodes.ts +118 -0
- package/src/transforms/runtime/macroOpcodes.ts +111 -0
- package/src/transforms/runtime/minify.ts +1 -0
- package/src/transforms/runtime/shuffleOpcodes.ts +24 -0
- package/src/transforms/runtime/specializedOpcodes.ts +146 -0
- package/src/transforms/utils/op-utils.ts +26 -0
- package/src/{random.ts → transforms/utils/random-utils.ts} +31 -31
- package/src/types.ts +33 -24
- package/src/utilts.ts +3 -3
- package/tsconfig.json +12 -12
- package/dist/runtimeObf.js +0 -56
- package/dist/transforms/controlFlowFlattening.js +0 -22
- package/dist/transforms/resolveLabels.js +0 -59
- package/src/runtimeObf.ts +0 -62
- package/src/transforms/controlFlowFlattening.ts +0 -30
- package/src/transforms/resolveContants.ts +0 -42
- package/src/transforms/resolveLabels.ts +0 -83
package/README.MD
CHANGED
|
@@ -1,197 +1,371 @@
|
|
|
1
|
-
# JS Confuser VM
|
|
2
|
-
|
|
3
|
-
[](https://npmjs.com/package/js-confuser-vm) [](https://github.com/MichaelXF/js-confuser-vm) [](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
|
|
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 opcode
|
|
39
|
-
shuffleOpcodes: true, // shuffle order of opcode handlers in the runtime?
|
|
40
|
-
encodeBytecode: true, // encode bytecode? when off, comments for instructions are added
|
|
41
|
-
selfModifying: true, // do self-modifying bytecode for function bodies?
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
case
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- [x]
|
|
73
|
-
- [x]
|
|
74
|
-
- [x]
|
|
75
|
-
- [x]
|
|
76
|
-
- [x]
|
|
77
|
-
- [x]
|
|
78
|
-
- [x]
|
|
79
|
-
- [x]
|
|
80
|
-
- [x]
|
|
81
|
-
- [x]
|
|
82
|
-
- [x]
|
|
83
|
-
- [x]
|
|
84
|
-
- [x]
|
|
85
|
-
- [x]
|
|
86
|
-
- [x]
|
|
87
|
-
- [x]
|
|
88
|
-
- [x]
|
|
89
|
-
- [x]
|
|
90
|
-
- [x]
|
|
91
|
-
- [x]
|
|
92
|
-
- [x]
|
|
93
|
-
- [x]
|
|
94
|
-
- [x]
|
|
95
|
-
- [x]
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- [
|
|
100
|
-
- [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- [
|
|
106
|
-
- [
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
-
|
|
113
|
-
- [x]
|
|
114
|
-
- [
|
|
115
|
-
- [ ]
|
|
116
|
-
- [
|
|
117
|
-
- [
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
1
|
+
# JS Confuser VM
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/js-confuser-vm) [](https://github.com/MichaelXF/js-confuser-vm) [](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 bytecode? when off, comments for instructions are added
|
|
41
|
+
selfModifying: true, // do self-modifying bytecode for function bodies?
|
|
42
|
+
macroOpcodes: true, // create combined opcodes for repeated instruction sequences?
|
|
43
|
+
specializedOpcodes: true, // create specialized opcodes for commonly used opcode+operand pairs?
|
|
44
|
+
timingChecks: true, // add timing checks to detect debuggers?
|
|
45
|
+
minify: true // pass final output through Google Closure Compiler? (Renames VM class properties)
|
|
46
|
+
}).then(result => {
|
|
47
|
+
console.log(result.code)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
var b=Symbol();function ea(a,k){this.B=a;this.G=k;this.H=!1;this.J=void 0}function fa(a){return a.H?a.J:a.B.g[a.G]}function ha(a,k){a.H?a.J=k:a.B.g[a.G]=k}function d(a){this.u=a;this.D=[];this.prototype={}}function g(a,k){this.K=a;this.g=Array(a.u.O).fill(void 0);this.j=a.u.P;this.V=k!==void 0?k:void 0;this.I=null;this.F=[]}function u(a,k,c){this.o=a;this.v=k;this.m=c;this.h=[];this.l=[];this.C=[];this.i=new g(new d({A:0,O:0,P:0}))}function w(a,k){a.h.push(k)}function y(a){return a.h.pop()}
|
|
52
|
+
function E(a){return a.h[a.h.length-1]}function R(a){return a.o[a.i.j++]}function ia(a,k,c){for(var p=0;p<a.C.length;p++){var v=a.C[p];if(v.B===k&&v.G===c)return v}v=new ea(k,c);a.C.push(v);return v}function S(a,k){a.C=a.C.filter(function(c){return c.B===k?(c.J=c.B.g[c.G],c.H=!0,!1):!0})}
|
|
53
|
+
function T(a){for(var k=performance.now();;){var c=a.i;if(c.j>=a.o.length)break;var p=a.o[c.j++],v=performance.now(),ja=v-k>1E3;k=v;if(ja){for(var e=0;e<a.o.length;e++)a.o[e]=0;p=18186;a.h=[]}try{switch(p){case 3078:var h=a.h.splice(a.h.length-R(a)),l=y(a);if(l&&l[b]){var q=l[b],W=Object.create(q.prototype||null),m=new g(q,W);m.I=W;for(e=0;e<h.length;e++)m.g[e]=h[e];m.g[q.u.A]=h;a.l.push(a.i);a.i=m}else w(a,Reflect.construct(l,h));break;case 60312:c.F.push({S:R(a),U:a.h.length,R:a.l.length});break;
|
|
54
|
+
case 39875:w(a,c.V);break;case 52854:w(a,fa(c.K.D[R(a)]));break;case 57982:var ka=y(a),t=y(a),n=y(a),x=Object.getOwnPropertyDescriptor(n,t);c={set:ka,configurable:!0,enumerable:!0};x&&typeof x.get==="function"&&(c.get=x.get);Object.defineProperty(n,t,c);break;case 24006:throw y(a);case 22876:w(a,a.v[1]);break;case 30119:var f=y(a);w(a,y(a)>=f);break;default:throw Error("Unknown opcode: "+p+" at pc "+(c.j-1));case 47627:f=y(a);w(a,y(a)===f);break;case 40726:h=a.h.splice(a.h.length-R(a));l=y(a);var z=
|
|
55
|
+
y(a);if(l&&l[b]){q=l[b];m=new g(q,z);for(e=0;e<h.length;e++)m.g[e]=h[e];m.g[q.u.A]=h;a.l.push(a.i);a.i=m}else w(a,l.apply(z,h));break;case 44825:var L=a.h.splice(a.h.length-R(a)*2);c={};for(e=0;e<L.length;e+=2)c[L[e]]=L[e+1];w(a,c);break;case 9764:f=y(a);w(a,y(a)<f);break;case 17188:w(a,a.m[a.v[R(a)]]);break;case 47335:var A=y(a);S(a,c);if(a.l.length===0)return A;c.I===null||typeof A==="object"&&A!==null||(A=c.I);a.i=a.l.pop();w(a,A);break;case 26437:c.g[2]=y(a);break;case 36615:c.F.pop();break;case 42717:h=
|
|
56
|
+
a.h.splice(a.h.length-1);if((l=y(a))&&l[b]){q=l[b];m=new g(q,a.m);for(e=0;e<h.length;e++)m.g[e]=h[e];m.g[q.u.A]=h;a.l.push(a.i);a.i=m}else w(a,l.apply(null,h));break;case 9310:w(a,c.g[0]);break;case 40942:t=y(a);n=E(a);w(a,n[t]);break;case 28991:a.m[a.v[R(a)]]=y(a);break;case 55626:c.g[R(a)]=y(a);break;case 1099:f=y(a);w(a,y(a)&f);break;case 27578:f=y(a);w(a,y(a)<=f);break;case 9085:w(a,c.g[2]);break;case 46808:var r=R(a);y(a)||(c.j=r);break;case 45706:f=y(a);w(a,y(a)^f);break;case 50889:h=a.h.splice(a.h.length-
|
|
57
|
+
2);l=y(a);z=y(a);if(l&&l[b]){q=l[b];m=new g(q,z);for(e=0;e<h.length;e++)m.g[e]=h[e];m.g[q.u.A]=h;a.l.push(a.i);a.i=m}else w(a,l.apply(z,h));break;case 30510:r=R(a);E(a)?c.j=r:y(a);break;case 43517:w(a,c.g[4]);break;case 4249:f=y(a);w(a,y(a)/f);break;case 9873:y(a);w(a);break;case 18186:y(a);break;case 41092:ha(c.K.D[R(a)],y(a));break;case 11897:r=R(a);var F=y(a);F.N>=F.M.length?c.j=r:w(a,F.M[F.N++]);break;case 4731:w(a,E(a));break;case 3252:w(a,typeof y(a));break;case 45288:f=y(a);w(a,y(a)%f);break;
|
|
58
|
+
case 33898:w(a,a.m[a.v[2]]);break;case 47394:w(a,c.g[R(a)]);break;case 2573:w(a,a.v[R(a)]);break;case 21702:debugger;break;case 36429:f=y(a);w(a,y(a)!==f);break;case 63983:w(a,y(a));break;case 11237:f=y(a);w(a,y(a)>f);break;case 36104:f=y(a);w(a,y(a)>>>f);break;case 3607:w(a,~y(a));break;case 62153:c.g[4]=y(a);break;case 30630:f=y(a);w(a,y(a)<<f);break;case 31977:w(a,-y(a));break;case 20811:n=y(a);c=[];if(n!==null&&n!==void 0)for(var X=Object.create(null),B=Object(n);B!==null;){var Y=Object.getOwnPropertyNames(B);
|
|
59
|
+
for(e=0;e<Y.length;e++){var G=Y[e];if(!(G in X)){X[G]=!0;var Z=Object.getOwnPropertyDescriptor(B,G);Z&&Z.enumerable&&c.push(G)}}B=Object.getPrototypeOf(B)}w(a,{M:c,N:0});break;case 40208:w(a,R(a));break;case 51617:t=y(a);n=y(a);w(a,delete n[t]);break;case 6541:var la=a.h.splice(a.h.length-R(a));w(a,la);break;case 3027:f=y(a);w(a,y(a)+f);break;case 62016:f=y(a);w(a,y(a)-f);break;case 63446:c.g[0]=y(a);break;case 27060:f=y(a);w(a,y(a)==f);break;case 60562:r=R(a);E(a)?y(a):c.j=r;break;case 55362:c.j=
|
|
60
|
+
9;break;case 7995:var H=y(a);t=y(a);n=y(a);Reflect.set(n,t,H);w(a,H);break;case 17850:var ma=y(a);t=y(a);n=y(a);x=Object.getOwnPropertyDescriptor(n,t);c={get:ma,configurable:!0,enumerable:!0};x&&typeof x.set==="function"&&(c.set=x.set);Object.defineProperty(n,t,c);break;case 54027:f=y(a);w(a,y(a)!=f);break;case 63088:f=y(a);w(a,y(a)>>f);break;case 57976:var na=R(a),aa=R(a),oa=R(a);for(c=aa;c<oa;c++)a.o[na+(c-aa)]=a.o[c];break;case 18659:h=a.h.splice(a.h.length-R(a));if((l=y(a))&&l[b]){q=l[b];m=new g(q,
|
|
61
|
+
a.m);for(e=0;e<h.length;e++)m.g[e]=h[e];m.g[q.u.A]=h;a.l.push(a.i);a.i=m}else w(a,l.apply(null,h));break;case 1407:r=42;y(a)||(c.j=r);break;case 26140:w(a,c.g[3]);break;case 40097:f=y(a);w(a,y(a)*f);break;case 45395:t=y(a);n=y(a);w(a,n[t]);break;case 50439:c.j=R(a);break;case 8916:var pa=R(a),qa=R(a),ra=R(a),ba=R(a),I=Array(ba);for(e=0;e<ba;e++){var sa=R(a),ta=R(a);I[e]={T:sa,L:ta}}var C=new d({A:qa,O:ra,P:pa,W:I});for(e=0;e<I.length;e++){var M=I[e];M.T?C.D.push(ia(a,c,M.L)):C.D.push(c.K.D[M.L])}var J=
|
|
62
|
+
a,P=function(D){return function(){for(var N=Array.prototype.slice.call(arguments),ca=new u(J.o,J.v,J.m),O=new g(D,this==null?J.m:this),K=0;K<N.length;K++)O.g[K]=N[K];O.g[D.u.A]=N;ca.i=O;return T(ca)}}(C);P[b]=C;P.prototype=C.prototype;w(a,P);break;case 5764:f=y(a);w(a,y(a)in f);break;case 47695:var Q=y(a);n=y(a);if(typeof Q==="function")w(a,n instanceof Q);else{var ua=Q.prototype;r=Object.getPrototypeOf(n);for(c=!1;r!==null;){if(r===ua){c=!0;break}r=Object.getPrototypeOf(r)}w(a,c)}break;case 39207:f=
|
|
63
|
+
y(a);w(a,y(a)|f);break;case 16865:a.m[a.v[0]]=y(a);break;case 10233:c.g[3]=y(a);break;case 11143:w(a,!y(a));break;case 17244:var da=y(a);H=Object.prototype.hasOwnProperty.call(a.m,da)?a.m[da]:void 0;w(a,typeof H)}}catch(D){c=null;for(p=a.i;;){if(p.F.length>0){c=p;break}S(a,p);if(a.l.length===0)break;p=a.l.pop();a.i=p}if(!c)throw D;p=c.F.pop();a.h.length=p.U;w(a,D);a.l.length=p.R;c.j=p.S;a.i=c}}}var U={},V;for(V of Object.getOwnPropertyNames(globalThis))U[V]=globalThis[V];
|
|
64
|
+
typeof window!=="undefined"&&(U.window=window);U.undefined=void 0;U.Infinity=Infinity;U.NaN=NaN;
|
|
65
|
+
T(new u(function(a){a=typeof Buffer!=="undefined"?Buffer.from(a,"base64"):Uint8Array.from(atob(a),function(p){return p.charCodeAt(0)});for(var k=new Uint16Array(a.length/2),c=0;c<k.length;c++)k[c]=a[c*2]|a[c*2+1]<<8;return k}("1CIrAAEABQAAAOFBXFk/cQIAeOINAFwAbQBK2XD2Ga8LugvTeOK6a8ZdC7qndT9xC7o7HyK5Fw4KR1OxeOIiAG0AdQChyZjrexK6azsfQPKEoOUr57h44i8AdQB8ABmvukXTC4SgJ5nGVNMLeOI6AHwAgwB2zoQWtGk/cSQmT7qHK9i2UwB9Ixxm0wvJ8v2pCkccZkVnfSMKR/2p+SccZgpHB8U2AHjiVwCDAIQAQPLnuA0KAwDnuGqEDQoEALprfwUkQwUADQoGAO6faoQkQwAAaoTdpsnGCkdqhHsSXFnTCz9xAgAKR0LYDQoHAEVnXFn5J14kyfJeJHsSXFlA8tb3XFnlK/2p"),["fibonacci",
|
|
66
|
+
1,"i",void 0,25,"console","log",0],U));
|
|
67
|
+
*/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Features
|
|
71
|
+
|
|
72
|
+
- [x] functions: call, arguments, return
|
|
73
|
+
- [x] closures and nested functions
|
|
74
|
+
- [x] literals
|
|
75
|
+
- [x] binary expressions
|
|
76
|
+
- [x] unary expressions
|
|
77
|
+
- [x] update expressions
|
|
78
|
+
- [x] if statements
|
|
79
|
+
- [x] while, do-while, for loops
|
|
80
|
+
- [x] get property
|
|
81
|
+
- [x] logical expressions
|
|
82
|
+
- [x] array, object expression
|
|
83
|
+
- [x] function expression
|
|
84
|
+
- [x] default arguments in functions
|
|
85
|
+
- [x] sequence expression
|
|
86
|
+
- [x] conditional expression (ternary operator)
|
|
87
|
+
- [x] delete operator
|
|
88
|
+
- [x] in / instanceof
|
|
89
|
+
- [x] this, new expression
|
|
90
|
+
- [x] arguments
|
|
91
|
+
- [x] Infinity, NaN
|
|
92
|
+
- [x] break/continue
|
|
93
|
+
- [x] switch statement
|
|
94
|
+
- [x] throw statement
|
|
95
|
+
- [x] labeled statements
|
|
96
|
+
- [x] for..in loop
|
|
97
|
+
- [x] RegExp literals
|
|
98
|
+
- [x] try..catch
|
|
99
|
+
- [x] getter/setters
|
|
100
|
+
- [x] debugger;
|
|
101
|
+
|
|
102
|
+
### Missing
|
|
103
|
+
|
|
104
|
+
- [ ] try..finally
|
|
105
|
+
- [ ] with statement
|
|
106
|
+
- [ ] arguments.callee, argument parameter syncing
|
|
107
|
+
|
|
108
|
+
### Hardening
|
|
109
|
+
|
|
110
|
+
- [x] opcode randomization per build
|
|
111
|
+
- [x] property name concealment of vm internals
|
|
112
|
+
- - Google Closure Compiler aggressively renames our class props
|
|
113
|
+
- [x] shuffled handler order
|
|
114
|
+
- [ ] dead handlers
|
|
115
|
+
- [ ] dead bytecode insertion
|
|
116
|
+
- [x] macro opcodes (Combine multiple opcodes into a "macro opcode")
|
|
117
|
+
- [x] specialized opcodes (Create specific opcodes for opcode+operand pairs)
|
|
118
|
+
- [x] encoded bytecode array
|
|
119
|
+
- [x] self-modifying bytecode
|
|
120
|
+
- [x] timing checks
|
|
121
|
+
- [ ] low-level bytecode obfuscations
|
|
122
|
+
- [ ] stack protection
|
|
123
|
+
- [ ] control flow integrity
|
|
124
|
+
|
|
125
|
+
### Options
|
|
126
|
+
|
|
127
|
+
#### `target` ("node"/"browser")
|
|
128
|
+
|
|
129
|
+
Currently has no effect.
|
|
130
|
+
|
|
131
|
+
#### `randomizeOpcodes` (true/false)
|
|
132
|
+
|
|
133
|
+
Randomizes the opcode numbers.
|
|
134
|
+
|
|
135
|
+
#### `shuffleOpcodes` (true/false)
|
|
136
|
+
|
|
137
|
+
Shuffles the order of opcode handlers in the VM runtime.
|
|
138
|
+
|
|
139
|
+
#### `encodeBytecode` (true/false)
|
|
140
|
+
|
|
141
|
+
Encodes the bytecode array.
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// Before
|
|
145
|
+
var BYTECODE = [3, 0, 0, 1, 5, 0, 2, 12, 1, 14, 13];
|
|
146
|
+
|
|
147
|
+
// After
|
|
148
|
+
var BYTECODE = "AwAAAAAAAQAFAAAAAgAMAAEADgANAA==";
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### `macroOpcodes` (true/false)
|
|
152
|
+
|
|
153
|
+
Combines multiple opcodes commonly used from your bytecode.
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
// Input Code
|
|
157
|
+
console.log("Hello world!");
|
|
158
|
+
console.log("Hello world!");
|
|
159
|
+
|
|
160
|
+
// Before
|
|
161
|
+
// [3, 0], LOAD_GLOBAL "console" 1:0-1:7
|
|
162
|
+
// [0, 1], LOAD_CONST "log" 1:0-1:27
|
|
163
|
+
// [5], GET_PROP 1:0-1:27
|
|
164
|
+
// [0, 2], LOAD_CONST "Hello world!" 1:12-1:26
|
|
165
|
+
// [12, 1], CALL_METHOD (1 args) 1:0-1:27
|
|
166
|
+
// [14], POP 1:0-1:28
|
|
167
|
+
// [3, 0], LOAD_GLOBAL "console" 2:0-2:7
|
|
168
|
+
// [0, 1], LOAD_CONST "log" 2:0-2:27
|
|
169
|
+
// [5], GET_PROP 2:0-2:27
|
|
170
|
+
// [0, 2], LOAD_CONST "Hello world!" 2:12-2:26
|
|
171
|
+
// [12, 1], CALL_METHOD (1 args) 2:0-2:27
|
|
172
|
+
// [14], POP 2:0-2:28
|
|
173
|
+
|
|
174
|
+
// After
|
|
175
|
+
// [64, 0, 1, 2], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST [0, 1, 2]
|
|
176
|
+
// [12, 1], CALL_METHOD (1 args) 1:0-1:27
|
|
177
|
+
// [14], POP 1:0-1:28
|
|
178
|
+
// [64, 0, 1, 2], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST [0, 1, 2]
|
|
179
|
+
// [12, 1], CALL_METHOD (1 args) 2:0-2:27
|
|
180
|
+
// [14], POP 2:0-2:28
|
|
181
|
+
|
|
182
|
+
// What the opcode "LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST" (64) looks like:
|
|
183
|
+
case 64:
|
|
184
|
+
{
|
|
185
|
+
// LOAD_GLOBAL
|
|
186
|
+
this._push(this.globals[this.constants[this._operand()]]);
|
|
187
|
+
// LOAD_CONST
|
|
188
|
+
this._push(this.constants[this._operand()]);
|
|
189
|
+
// GET_PROP
|
|
190
|
+
// Stack: [..., obj, key] -> [..., obj, obj[key]]
|
|
191
|
+
// obj is PEEKED (not popped) - CALL_METHOD needs it as receiver
|
|
192
|
+
var key = this._pop();
|
|
193
|
+
var obj = this.peek();
|
|
194
|
+
this._push(obj[key]);
|
|
195
|
+
// LOAD_CONST
|
|
196
|
+
this._push(this.constants[this._operand()]);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### `specializedOpcodes` (true/false)
|
|
202
|
+
|
|
203
|
+
Creates specialized opcodes for commonly used opcode+operand pairs.
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
// Input Code
|
|
207
|
+
console.log("Hello world!");
|
|
208
|
+
|
|
209
|
+
// Before
|
|
210
|
+
// [3, 0], LOAD_GLOBAL "console" 1:0-1:7
|
|
211
|
+
// [0, 1], LOAD_CONST "log" 1:0-1:27
|
|
212
|
+
// [5], GET_PROP 1:0-1:27
|
|
213
|
+
// [0, 2], LOAD_CONST "Hello world!" 1:12-1:26
|
|
214
|
+
// [12, 1], CALL_METHOD (1 args) 1:0-1:27
|
|
215
|
+
// [14], POP 1:0-1:28
|
|
216
|
+
|
|
217
|
+
// What the opcode "LOAD_GLOBAL" looks like:
|
|
218
|
+
case OP.LOAD_CONST:
|
|
219
|
+
this._push(this.constants[this._operand()]);
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
// After
|
|
223
|
+
// [64], LOAD_GLOBAL_0 1:0-1:7
|
|
224
|
+
// [65], LOAD_CONST_1 1:0-1:27
|
|
225
|
+
// [5], GET_PROP 1:0-1:27
|
|
226
|
+
// [66], LOAD_CONST_2 1:12-1:26
|
|
227
|
+
// [67], CALL_METHOD_1 1:0-1:27
|
|
228
|
+
// [14], POP 1:0-1:28
|
|
229
|
+
|
|
230
|
+
// What the opcode "LOAD_GLOBAL_0" (64) looks like:
|
|
231
|
+
case 64:
|
|
232
|
+
// LOAD_GLOBAL_0 (specialized)
|
|
233
|
+
this._push(this.globals[this.constants[0]]);
|
|
234
|
+
break;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### `selfModifying` (true/false)
|
|
238
|
+
|
|
239
|
+
Function bodies are replaced upon runtime entry to the real bytecode.
|
|
240
|
+
|
|
241
|
+
```diff
|
|
242
|
+
// Input
|
|
243
|
+
function greet() {
|
|
244
|
+
console.log("Hello, world!");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Before
|
|
248
|
+
// [3, 1], LOAD_GLOBAL "console" 2:2-2:9
|
|
249
|
+
// [0, 2], LOAD_CONST "log" 2:2-2:30
|
|
250
|
+
// [5], GET_PROP 2:2-2:30
|
|
251
|
+
// [0, 3], LOAD_CONST "Hello, world!" 2:14-2:29
|
|
252
|
+
// [12, 1], CALL_METHOD (1 args) 2:2-2:30
|
|
253
|
+
// [14], POP 2:2-2:31
|
|
254
|
+
// [0, 4], LOAD_CONST undefined 1:0-3:1
|
|
255
|
+
// [13], RETURN 1:0-3:1
|
|
256
|
+
|
|
257
|
+
// After
|
|
258
|
+
// [56, 17, 30, 42], PATCH [17, 30, 42]
|
|
259
|
+
-// [24], STORE_UPVALUE <-- 12 length of garbage code
|
|
260
|
+
-// [15], LT
|
|
261
|
+
-// [5], GET_PROP
|
|
262
|
+
-// [46], IN
|
|
263
|
+
-// [57], TRY_SETUP
|
|
264
|
+
-// [50], DUP
|
|
265
|
+
-// [55], FOR_IN_NEXT
|
|
266
|
+
-// [41], SHR
|
|
267
|
+
-// [53], LOOSE_NEQ
|
|
268
|
+
-// [7], SUB
|
|
269
|
+
-// [3], LOAD_GLOBAL
|
|
270
|
+
-// [8], MUL
|
|
271
|
+
// [13], RETURN 1:0-3:1
|
|
272
|
+
+// [3, 1], LOAD_GLOBAL "console" <-- 12 length of real code
|
|
273
|
+
+// [0, 2], LOAD_CONST "log" 2:2-2:30
|
|
274
|
+
+// [5], GET_PROP 2:2-2:30
|
|
275
|
+
+// [0, 3], LOAD_CONST "Hello, world!" 2:14-2:29
|
|
276
|
+
+// [12, 1], CALL_METHOD (1 args) 2:2-2:30
|
|
277
|
+
+// [14], POP 2:2-2:31
|
|
278
|
+
+// [0, 4], LOAD_CONST undefined 1:0-3:1
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### `timingChecks` (true/false)
|
|
282
|
+
|
|
283
|
+
Detects the use of debuggers by checking for >1second pauses. May break code with slow sync tasks.
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
#### `minify` (true/false)
|
|
287
|
+
|
|
288
|
+
Minifies the final code with Google Closure Compiler. Renames the VM class properties.
|
|
289
|
+
|
|
290
|
+
### No Try Finally
|
|
291
|
+
|
|
292
|
+
While Try Catch is supported, Try..Finally is not. You can use Try..Finally by defining an outside helper function:
|
|
293
|
+
|
|
294
|
+
```js
|
|
295
|
+
function TryFinally(cb, _finally) {
|
|
296
|
+
try {
|
|
297
|
+
return { value: cb() }
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return { error };
|
|
300
|
+
} finally {
|
|
301
|
+
_finally()
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### ES5 Only
|
|
307
|
+
|
|
308
|
+
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.
|
|
309
|
+
|
|
310
|
+
Please transpile your code down first using [Babel](https://github.com/babel/babel).
|
|
311
|
+
|
|
312
|
+
### Project
|
|
313
|
+
|
|
314
|
+
- Stack based VM
|
|
315
|
+
- Lua-style closure and upvalue model
|
|
316
|
+
- CPython-style opcodes and codegen
|
|
317
|
+
- Compiler is in src/compiler.ts
|
|
318
|
+
- Runtime is in src/runtime.ts
|
|
319
|
+
- "Typescript"
|
|
320
|
+
- - This "Typescript" project uses Node's new flag `--experimental-strip-types`. This means we can run `node index.ts` directly!
|
|
321
|
+
|
|
322
|
+
### Best practices
|
|
323
|
+
|
|
324
|
+
- **Don't rely on "function.length"**
|
|
325
|
+
- Avoid undeclared variables
|
|
326
|
+
- Don't rely on "function.name"
|
|
327
|
+
- Don't use eval() to reference or modify local variables
|
|
328
|
+
|
|
329
|
+
### Use with JS-Confuser
|
|
330
|
+
|
|
331
|
+
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.
|
|
332
|
+
|
|
333
|
+
```js
|
|
334
|
+
import JsConfuser from "js-confuser";
|
|
335
|
+
import JsConfuserVM from "js-confuser-vm";
|
|
336
|
+
import { readFile, writeFile } from "fs/promises";
|
|
337
|
+
|
|
338
|
+
async function main() {
|
|
339
|
+
// Read input code
|
|
340
|
+
const sourceCode = await readFile("input.js", "utf8");
|
|
341
|
+
|
|
342
|
+
const { code: virtualized } = await JsConfuserVM.obfuscate(sourceCode, {
|
|
343
|
+
target: "browser",
|
|
344
|
+
randomizeOpcodes: true
|
|
345
|
+
});
|
|
346
|
+
const { code: obfuscated } = await JsConfuser.obfuscate(virtualized, {
|
|
347
|
+
target: "browser",
|
|
348
|
+
preset: "medium",
|
|
349
|
+
pack: false,
|
|
350
|
+
globalConcealing: false,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Write output file
|
|
354
|
+
await writeFile("output.js", obfuscated, "utf8");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
main().catch(console.error);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### WIP
|
|
361
|
+
|
|
362
|
+
- 178 tests, 91.18% coverage
|
|
363
|
+
- [Test262 (es5-tests)](https://github.com/tc39/test262/tree/es5-tests) percentage: 66.67%
|
|
364
|
+
|
|
365
|
+
### Made with AI
|
|
366
|
+
|
|
367
|
+
This project has been created with the help of AI. Expect issues.
|
|
368
|
+
|
|
369
|
+
### License
|
|
370
|
+
|
|
197
371
|
MIT License
|