js-confuser-vm 0.0.1 → 0.0.2

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/README.MD ADDED
@@ -0,0 +1,191 @@
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)
4
+
5
+
6
+ - **Requires Node v24.13.1 or higher**
7
+ - ES5 support only. No complex features: async, generator, and even try..catch are beyond scope.
8
+ - Experimental. Expect issues.
9
+
10
+ ### Installation
11
+
12
+ ```shell
13
+ $ npm install js-confuser-vm
14
+ ```
15
+
16
+ ### Usage
17
+
18
+ ```js
19
+ import JsConfuserVM from "js-confuser-vm";
20
+
21
+ JsConfuserVM.obfuscate(`
22
+ function fibonacci(num){
23
+ var a = 0, b = 1, c = num;
24
+ while (num-- > 1) {
25
+ c = a + b;
26
+ a = b;
27
+ b = c;
28
+ }
29
+ return c;
30
+ }
31
+
32
+ for ( var i = 1; i <= 25; i++ ) {
33
+ console.log(i, fibonacci(i))
34
+ }
35
+ `, {
36
+ target: "browser", // or "node"
37
+ randomizeOpcodes: true, // randomize opcode values in OP mapping?
38
+ shuffleOpcodes: true, // shuffle order of opcode handlers in the runtime?
39
+ encodeBytecode: true, // encode bytecode? when off, comments for instructions are added
40
+ selfModifying: true, // do self-modifying bytecode for function bodies?
41
+ timingChecks: true, // add timing checks to detect debuggers?
42
+ }).then(result => {
43
+ console.log(result.code)
44
+ })
45
+
46
+ /*
47
+ function d(a){a=typeof Buffer!=="undefined"?Buffer.from(a,"base64"):Uint8Array.from(atob(a),function(b){return b.charCodeAt(0)});for(var h=new Int32Array(a.length/4),c=0;c<h.length;c++)h[c]=a[c*4]|a[c*4+1]<<8|a[c*4+2]<<16|a[c*4+3]<<24;return h}var f=Symbol();function l(a,h){this.g=a;this.m=h;this.n=!1;this.p=void 0}function m(a){return a.n?a.p:a.g.b[a.m]}function n(a,h){a.n?a.p=h:a.g.b[a.m]=h}function p(a){this.f=a;this.l=[];this.prototype={}}
48
+ function w(a,h){this.r=a;this.b=Array(a.f.t).fill(void 0);this.d=a.f.u;this.x=h!==void 0?h:void 0;this.o=null}function x(a,h,c){this.q=a;this.e=h;this.i=c;this.a=[];this.h=[];this.j=[];this.c=new w(new p({k:0,t:0,u:0}))}function y(a,h){a.a.push(h)}function z(a){return a.a.pop()}function A(a){return a.a[a.a.length-1]}function E(a,h,c){for(var b=0;b<a.j.length;b++){var e=a.j[b];if(e.g===h&&e.m===c)return e}e=new l(h,c);a.j.push(e);return e}
49
+ function F(a,h){a.j=a.j.filter(function(c){return c.g===h?(c.p=c.g.b[c.m],c.n=!0,!1):!0})}
50
+ function G(a){for(var h=performance.now();;){var c=a.c,b=a.q;if(c.d>=b.length)break;b=b[c.d++];var e=b&255;b>>>=8;var g=performance.now(),k=g-h>1E3;h=g;k&&(e=149);switch(e){case 142:b=z(a);y(a,z(a)in b);break;case 109:b=z(a);c=[];if(b!==null&&b!==void 0)for(e=Object.create(null),g=Object(b);g!==null;){k=Object.getOwnPropertyNames(g);for(b=0;b<k.length;b++){var q=k[b];if(!(q in e)){e[q]=!0;var B=Object.getOwnPropertyDescriptor(g,q);B&&B.enumerable&&c.push(q)}}g=Object.getPrototypeOf(g)}y(a,{keys:c,
51
+ s:0});break;default:throw Error("Unknown opcode: "+e+" at pc "+(c.d-1));case 243:z(a);y(a);break;case 114:b=z(a);y(a,z(a)^b);break;case 81:y(a,m(c.r.l[b]));break;case 6:A(a)?c.d=b:z(a);break;case 158:c=a.a.splice(a.a.length-b);e=z(a);b=z(a);if(e&&e[f]){e=e[f];g=new w(e,b);for(b=0;b<c.length;b++)g.b[b]=c[b];g.b[e.f.k]=c;a.h.push(a.c);a.c=g}else y(a,e.apply(b,c));break;case 77:y(a,!z(a));break;case 161:y(a,a.i[a.e[b]]);break;case 231:b=z(a);y(a,z(a)-b);break;case 35:y(a,a.e[b]);break;case 168:b=z(a);
52
+ y(a,z(a)>b);break;case 224:g=a.e[b];e=new p(g);for(b=0;b<g.v.length;b++)k=g.v[b],k.y?e.l.push(E(a,c,k.w)):e.l.push(c.r.l[k.w]);var t=a;b=function(C){return function(){for(var u=Array.prototype.slice.call(arguments),D=new x(t.q,t.e,t.i),v=new w(C,this),r=0;r<u.length;r++)v.b[r]=u[r];v.b[C.f.k]=u;D.c=v;return G(D)}}(e);b[f]=e;b.prototype=e.prototype;y(a,b);break;case 49:c=z(a);b=z(a);if(typeof c==="function")y(a,b instanceof c);else{c=c.prototype;b=Object.getPrototypeOf(b);for(e=!1;b!==null;){if(b===
53
+ c){e=!0;break}b=Object.getPrototypeOf(b)}y(a,e)}break;case 115:b=z(a);y(a,z(a)|b);break;case 82:c=z(a);b=z(a);y(a,delete b[c]);break;case 60:n(c.r.l[b],z(a));break;case 182:y(a,z(a));break;case 177:c=a.a.splice(a.a.length-b);if((e=z(a))&&e[f]){e=e[f];b=Object.create(e.prototype||null);g=new w(e,b);g.o=b;for(b=0;b<c.length;b++)g.b[b]=c[b];g.b[e.f.k]=c;a.h.push(a.c);a.c=g}else y(a,Reflect.construct(e,c));break;case 72:y(a,c.x);break;case 53:b=z(a);y(a,z(a)===b);break;case 25:b=z(a);y(a,z(a)<=b);break;
54
+ case 75:c=z(a);b=A(a);y(a,b[c]);break;case 225:e=z(a);e.s>=e.keys.length?c.d=b:y(a,e.keys[e.s++]);break;case 181:b=z(a);y(a,z(a)%b);break;case 42:c.d=b;break;case 134:e=z(a);c=z(a);b=z(a);b[c]=e;y(a,e);break;case 67:b=z(a);y(a,z(a)*b);break;case 43:b=z(a);y(a,z(a)<b);break;case 52:c.b[b]=z(a);break;case 68:z(a);break;case 143:c=z(a);e=a.e[b];e=d(e);for(b=0;b<e.length;b++)a.q[c+b]=e[b];break;case 244:y(a,~z(a));break;case 23:c=a.a.splice(a.a.length-b);if((e=z(a))&&e[f]){e=e[f];g=new w(e);for(b=0;b<
55
+ c.length;b++)g.b[b]=c[b];g.b[e.f.k]=c;a.h.push(a.c);a.c=g}else y(a,e.apply(null,c));break;case 123:b=z(a);y(a,z(a)!=b);break;case 14:b=z(a);y(a,z(a)<<b);break;case 238:A(a)?z(a):c.d=b;break;case 180:c=a.a.splice(a.a.length-b*2);e={};for(b=0;b<c.length;b+=2)e[c[b]]=c[b+1];y(a,e);break;case 4:b=z(a);y(a,z(a)>>>b);break;case 19:b=z(a);y(a,z(a)&b);break;case 211:c=z(a);b=z(a);y(a,b[c]);break;case 97:b=z(a);y(a,z(a)!==b);break;case 255:b=z(a);y(a,z(a)==b);break;case 101:b=z(a);y(a,z(a)+b);break;case 122:throw z(a);
56
+ case 157:y(a,-z(a));break;case 149:b=z(a);F(a,c);if(a.h.length===0)return b;c.o===null||typeof b==="object"&&b!==null||(b=c.o);a.c=a.h.pop();y(a,b);break;case 34:y(a,A(a));break;case 213:a.i[a.e[b]]=z(a);break;case 173:z(a)||(c.d=b);break;case 216:y(a,typeof z(a));break;case 39:b=z(a);y(a,z(a)>>b);break;case 210:y(a,c.b[b]);break;case 190:b=z(a);y(a,z(a)/b);break;case 150:b=z(a);y(a,z(a)>=b);break;case 172:b=z(a);e=Object.prototype.hasOwnProperty.call(a.i,b)?a.i[b]:void 0;y(a,typeof e);break;case 113:b=
57
+ a.a.splice(a.a.length-b),y(a,b)}}}var H={},I;for(I of Object.getOwnPropertyNames(globalThis))H[I]=globalThis[I];typeof window!=="undefined"&&(H.window=window);H.undefined=void 0;H.Infinity=Infinity;H.NaN=NaN;
58
+ G(new x(d("4AMAANUEAAAjAQAA1QUAAKEFAAAjBgAAGQAAAK0YAAChBwAAIwgAAEsAAAChBQAAoQQAAKEFAAAXAQAAngIAAEQAAAChBQAAIgAAACMBAABlAAAA1QUAAEQAAAAqBAAAlQAAACMKAACPCQAARPEAAOcLAACeRQAA54YAALZ9AAA8jAAAbdUAALFkAAArdAAAcysAAI+3AACxbwAAqMAAABPbAACO1wAA88sAAJ3DAACeGgAAsVIAAOCMAACOVwAA85sAANIEAABRwAAAUSIAANicAADVgAAAj1UAANjtAAC1DAAArJMAADy0AAAnBwAAregAAA=="),[0,1,void 0,{k:1,t:5,v:[],u:25},"fibonacci","i",25,"console","log","IwAAADQCAAAjAQAANAMAANIAAAA0BAAA0gAAACIAAAAjAQAA5wAAADQAAAAjAQAAqAAAAK04AADSAgAA0gMAAGUAAAA0BAAA0gQAAEQAAADSAwAANAIAANICAABEAAAA0gQAADQDAADSAwAARAAAACohAADSBAAAlQAAACMCAACVAAAAlQAAAA==",
59
+ 27],H));
60
+ */
61
+ ```
62
+
63
+ ### Features
64
+
65
+ - [x] functions: call, arguments, return
66
+ - [x] closures and nested functions
67
+ - [x] literals
68
+ - [x] binary expressions
69
+ - [x] unary expressions
70
+ - [x] update expressions
71
+ - [x] if statements
72
+ - [x] while, do-while, for loops
73
+ - [x] get property
74
+ - [x] logical expressions
75
+ - [x] array, object expression
76
+ - [x] function expression
77
+ - [x] default arguments in functions
78
+ - [x] sequence expression
79
+ - [x] conditional expression (ternary operator)
80
+ - [x] delete operator
81
+ - [x] in / instanceof
82
+ - [x] this, new expression
83
+ - [x] arguments
84
+ - [x] Infinity, NaN
85
+ - [x] break/continue
86
+ - [x] switch statement
87
+ - [x] throw statement
88
+ - [x] labeled statements
89
+ - [x] for..in loop
90
+ - [x] RegExp literals
91
+
92
+ ### Missing
93
+
94
+ - [ ] try..catch
95
+ - [ ] with statement
96
+ - [ ] arguments.callee, argument parameter syncing
97
+ - [ ] getter/setters
98
+
99
+ ### Hardening
100
+
101
+ - [x] opcode randomization per build
102
+ - [x] property name concealment of vm internals
103
+ - - Google Closure Compiler aggressively renames our class props
104
+ - [x] shuffled handler order
105
+ - [ ] dead handlers
106
+ - [ ] dead bytecode insertion
107
+ - [ ] macro opcodes
108
+ - [x] encoded bytecode array and words
109
+ - [x] self-modifying bytecode
110
+ - [x] timing checks
111
+ - [ ] low-level bytecode obfuscations
112
+ - [ ] stack protection
113
+ - [ ] control flow integrity
114
+
115
+ ### Minification
116
+
117
+ The `minify` option uses Google Closure Compiler to minify the JS VM and renames (most) VM property names. This approach helps keep the VM Compiler simple and lets Google do the heavy lifting.
118
+
119
+ ### No Try Catch
120
+
121
+ Try..Catch is complex operator that may not get added. You can use Try..Catch by defining an outside helper function:
122
+
123
+ ```js
124
+ function TryCatch(cb) {
125
+ try {
126
+ return { value: cb() };
127
+ } catch (error) {
128
+ return { error };
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### ES5 Only
134
+
135
+ 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.
136
+
137
+ Please transpile your code down first using [Babel](https://github.com/babel/babel).
138
+
139
+ ### Project
140
+
141
+ - Stack based VM
142
+ - Lua-style closure and upvalue model
143
+ - CPython-style opcodes and codegen
144
+ - Compiler is in src/compiler.ts
145
+ - Runtime is in src/runtime.ts
146
+ - "Typescript"
147
+ - - This "Typescript" project uses Node's new flag `--experimental-strip-types`. This means we can run `node index.ts` directly!
148
+
149
+ ### Use with JS-Confuser
150
+
151
+ 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.
152
+
153
+ ```js
154
+ import JsConfuser from "js-confuser";
155
+ import JsConfuserVM from "js-confuser-vm";
156
+ import { readFile, writeFile } from "fs/promises";
157
+
158
+ async function main() {
159
+ // Read input code
160
+ const sourceCode = await readFile("input.js", "utf8");
161
+
162
+ const { code: virtualized } = await JsConfuserVM.obfuscate(sourceCode, {
163
+ target: "browser",
164
+ randomizeOpcodes: true
165
+ });
166
+ const { code: obfuscated } = await JsConfuser.obfuscate(virtualized, {
167
+ target: "browser",
168
+ preset: "medium",
169
+ pack: false,
170
+ globalConcealing: false,
171
+ });
172
+
173
+ // Write output file
174
+ await writeFile("output.js", obfuscated, "utf8");
175
+ }
176
+
177
+ main().catch(console.error);
178
+ ```
179
+
180
+ ### WIP
181
+
182
+ - 146 tests, 91.18% coverage
183
+ - [Test262 (es5-tests)](https://github.com/tc39/test262/tree/es5-tests) percentage: 52.39%
184
+
185
+ ### Made with AI
186
+
187
+ This project has been created with the help of AI. Expect issues.
188
+
189
+ ### License
190
+
191
+ MIT License
@@ -0,0 +1,24 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "targets": {
7
+ "node": "18"
8
+ },
9
+ "modules": false
10
+ }
11
+ ],
12
+ [
13
+ "@babel/preset-typescript"
14
+ ]
15
+ ],
16
+ "plugins": [
17
+ [
18
+ "replace-import-extension",
19
+ {
20
+ "extMapping": { ".ts": ".js" }
21
+ }
22
+ ]
23
+ ]
24
+ }