js-confuser-vm 0.1.1 → 0.1.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.
Files changed (58) hide show
  1. package/README.md +242 -89
  2. package/dist/compiler.js +583 -208
  3. package/dist/disassembler.js +58 -8
  4. package/dist/runtime.js +93 -74
  5. package/dist/template.js +81 -76
  6. package/dist/transforms/bytecode/concealConstants.js +2 -2
  7. package/dist/transforms/bytecode/controlFlowFlattening.js +143 -25
  8. package/dist/transforms/bytecode/dispatcher.js +3 -3
  9. package/dist/transforms/bytecode/resolveRegisters.js +19 -4
  10. package/dist/transforms/bytecode/selfModifying.js +88 -21
  11. package/dist/transforms/bytecode/specializedOpcodes.js +6 -3
  12. package/dist/transforms/bytecode/stringConcealing.js +253 -75
  13. package/dist/utils/ast-utils.js +61 -0
  14. package/dist/utils/op-utils.js +1 -0
  15. package/package.json +7 -1
  16. package/.gitmodules +0 -4
  17. package/.prettierignore +0 -1
  18. package/CHANGELOG.md +0 -358
  19. package/babel-plugin-inline-runtime.cjs +0 -34
  20. package/babel.config.json +0 -23
  21. package/bench.ts +0 -146
  22. package/disassemble.ts +0 -12
  23. package/index.ts +0 -43
  24. package/jest-strip-types.js +0 -10
  25. package/jest.config.js +0 -64
  26. package/output.disassembled.js +0 -41
  27. package/src/build-runtime.ts +0 -113
  28. package/src/compiler.ts +0 -2703
  29. package/src/disassembler.ts +0 -329
  30. package/src/index.ts +0 -24
  31. package/src/minify.ts +0 -21
  32. package/src/options.ts +0 -24
  33. package/src/runtime.ts +0 -956
  34. package/src/template.ts +0 -265
  35. package/src/transforms/bytecode/aliasedOpcodes.ts +0 -151
  36. package/src/transforms/bytecode/concealConstants.ts +0 -52
  37. package/src/transforms/bytecode/controlFlowFlattening.ts +0 -566
  38. package/src/transforms/bytecode/dispatcher.ts +0 -292
  39. package/src/transforms/bytecode/macroOpcodes.ts +0 -193
  40. package/src/transforms/bytecode/resolveConstants.ts +0 -126
  41. package/src/transforms/bytecode/resolveLabels.ts +0 -112
  42. package/src/transforms/bytecode/resolveRegisters.ts +0 -226
  43. package/src/transforms/bytecode/selfModifying.ts +0 -121
  44. package/src/transforms/bytecode/specializedOpcodes.ts +0 -164
  45. package/src/transforms/bytecode/stringConcealing.ts +0 -130
  46. package/src/transforms/runtime/aliasedOpcodes.ts +0 -191
  47. package/src/transforms/runtime/classObfuscation.ts +0 -59
  48. package/src/transforms/runtime/macroOpcodes.ts +0 -138
  49. package/src/transforms/runtime/minify.ts +0 -1
  50. package/src/transforms/runtime/shuffleOpcodes.ts +0 -24
  51. package/src/transforms/runtime/specializedOpcodes.ts +0 -161
  52. package/src/types.ts +0 -134
  53. package/src/utils/ast-utils.ts +0 -19
  54. package/src/utils/op-utils.ts +0 -46
  55. package/src/utils/pass-utils.ts +0 -126
  56. package/src/utils/profile-utils.ts +0 -3
  57. package/src/utils/random-utils.ts +0 -31
  58. package/tsconfig.json +0 -12
package/CHANGELOG.md DELETED
@@ -1,358 +0,0 @@
1
- # Planned:
2
-
3
- - - Planned: Shuffle the order of constructor and method parameters
4
- - - Planned: Alias variables, and other function related obfuscations
5
-
6
- ## `0.1.1` Control Flow Flattening, String Concealing, and more
7
-
8
- - Added new option `controlFlowFlattening` which flattens the control flow of your program into a convoluted state machine
9
- - Added new option `stringConcealing` which involves encoding strings to conceal plain-text values.
10
-
11
- - Added new API method `JSConfuserVM.disassemble(sourceCode)` which returns a partial JS representation
12
- - - This only works if the parameter `sourceCode` contains the original bytecode comment
13
- - - This shouldn't be used with any options enabled, as the disassembler only supports default patterns
14
-
15
- - Added new option `classObfuscation` which obfuscates the VM runtime classes:
16
- - - Shuffles the order of declarations and methods
17
-
18
- - Added new option `verbose` which `console.log`'s useful info for debugging purposes.
19
-
20
- - Removed option `microOpcodes` - this option introduced scratch registers which left runtime values exposed, and were easily traceable by deobfuscators. The cons outweighed the pros and since it conflicts with the other obfuscations already present, it was removed.
21
-
22
- - Added support for rest parameters (ES6 feature)
23
-
24
- ## `0.1.0` Dispatcher, Virtual Registers, and more
25
-
26
- - Added new option `dispatcher` which creates a middleman block to process jumps.
27
-
28
- ```js
29
- // Input Code
30
- if (true) {
31
- console.log("Hello world!");
32
- }
33
-
34
- // Before
35
- // fn_0_0:
36
- // [0, 0, 0, 0], LOAD_CONST reg[0] = true 1:4-1:8
37
- // [40, 0, 29], JUMP_IF_FALSE [0, if_else_1] 1:0-3:1
38
- // [2, 0, 1, 0], LOAD_GLOBAL reg[0] = console 2:2-2:9
39
- // [0, 1, 2, 0], LOAD_CONST reg[1] = "log" 2:2-2:29
40
- // [8, 2, 0, 1], GET_PROP reg[2] = reg[0][reg[1]] 2:2-2:29
41
- // [0, 1, 3, 0], LOAD_CONST reg[1] = "Hello world!" 2:14-2:28
42
- // [43, 3, 0, 2, 1, 1], CALL_METHOD reg[3] = reg[2](recv=reg[0], 1 args) 2:2-2:29
43
- // if_else_1:
44
- // [0, 0, 4, 0], LOAD_CONST reg[0] = undefined
45
- // [45, 0], RETURN reg[0]
46
-
47
- // What this looks like decompiled:
48
- // fn_0_0:
49
- r0 = true
50
- if (!r0) goto if_else_1
51
- r0 = console
52
- r1 = "log"
53
- r2 = r0[r1] // console.log
54
- r1 = "Hello world!"
55
- r3 = r2.call(r0, r1) // console.log("Hello world!")
56
- // if_else_1:
57
- r0 = undefined
58
- return r0
59
-
60
- // After
61
- // fn_0_0:
62
- // [47, 2, 57, 2, 5, 0], MAKE_CLOSURE reg[2] PC=fn_2_3 (params=2 regs=5 upvalues=0)
63
- // [0, 3, 0, 0], LOAD_CONST reg[3] = true 1:4-1:8
64
- // [41, 3, 21], JUMP_IF_TRUE [3, if_else_1_skip_5]
65
- // [1, 0, 43020], LOAD_INT reg[0] = if_else_1
66
- // [1, 1, 40151], LOAD_INT reg[1] = 40151
67
- // [39, 49], JUMP dispatcher_4
68
- // if_else_1_skip_5:
69
- // [2, 3, 1, 0], LOAD_GLOBAL reg[3] = console 2:2-2:9
70
- // [0, 4, 2, 0], LOAD_CONST reg[4] = "log" 2:2-2:29
71
- // [8, 5, 3, 4], GET_PROP reg[5] = reg[3][reg[4]] 2:2-2:29
72
- // [0, 4, 3, 0], LOAD_CONST reg[4] = "Hello world!" 2:14-2:28
73
- // [43, 6, 3, 5, 1, 4], CALL_METHOD reg[6] = reg[5](recv=reg[3], 1 args) 2:2-2:29
74
- // if_else_1:
75
- // [0, 3, 4, 0], LOAD_CONST reg[3] = undefined
76
- // [45, 3], RETURN reg[3]
77
- // dispatcher_4:
78
- // [42, 0, 2, 2, 0, 1], CALL reg[0] = reg[2](reg[0], reg[1])
79
- // [58, 0], JUMP_REG PC = reg[0]
80
- // fn_2_3:
81
- // [18, 2, 0, 1], BXOR [2, 0, 1]
82
- // [0, 3, 5, 0], LOAD_CONST reg[3] = 52048
83
- // [11, 4, 2, 3], ADD [4, 2, 3]
84
- // [0, 2, 6, 0], LOAD_CONST reg[2] = 65535
85
- // [16, 3, 4, 2], BAND [3, 4, 2]
86
- // [45, 3], RETURN reg[3]
87
-
88
- // What this looks like decompiled:
89
- // fn_0_0:
90
- r2 = MakeClosure(fn_2_3, params=2)
91
- r3 = true
92
- if (r3) goto if_else_1_skip
93
- r0 = 43020
94
- r1 = 40151
95
- goto dispatcher
96
- // if_else_1_skip:
97
- r3 = console
98
- r4 = "log"
99
- r5 = r3[r4] // console.log
100
- r4 = "Hello world!"
101
- r6 = r5.call(r3, r4) // console.log("Hello world!")
102
- // if_else_1:
103
- r3 = undefined
104
- return r3
105
-
106
- // dispatcher:
107
- r0 = r2(r0, r1) // decode(encodedPC, siteKey)
108
- goto *r0 // indirect jump
109
-
110
- // fn_2_3:
111
- function decode(x, k) {
112
- return ((x ^ k) + 52048) & 65535;
113
- }
114
- ```
115
-
116
- - Improved registers:
117
- - - Now flattened into a single array with base/stack pointer mechanics
118
- - - Improved compiler to support virtual registers, allowing passes to introduce scope-specific registers, with a final pass responsible for allocation and freeing
119
-
120
- - Added a template system to insert synthetic JavaScript as native bytecode, similar to [JS-Confuser's template system](https://github.com/MichaelXF/js-confuser/blob/b6ef304f77b7fc27cb9cc8d7d841e50a4200d7bc/docs/Template.md)
121
-
122
-
123
-
124
- ## `0.0.9` Micro Opcodes
125
-
126
- - Added new option `microOpcodes` which breaks opcodes into multiple sub-opcodes.
127
-
128
- ```js
129
- // Input Code
130
- console.log("Hello world!");
131
-
132
- // Before
133
- // [2, 1, 0, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
134
- // [0, 2, 1, 0], LOAD_CONST reg[2] = "log" 1:0-1:27
135
- // [8, 3, 1, 2], GET_PROP reg[3] = reg[1][reg[2]] 1:0-1:27
136
- // [0, 4, 2, 0], LOAD_CONST reg[4] = "Hello world!" 1:12-1:26
137
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
138
-
139
- // What the opcode "LOAD_CONST" looks like:
140
- case OP.LOAD_CONST:
141
- var dst = this._operand();
142
- frame.regs[dst] = this._constant();
143
- break;
144
-
145
- // After
146
- // [60, 1], MICRO_LOAD_GLOBAL_0 1 1:0-1:7
147
- // [61, 0, 0], MICRO_LOAD_GLOBAL_1 [0, 0]
148
- // [62], MICRO_LOAD_GLOBAL_2
149
- // [63], MICRO_LOAD_GLOBAL_3
150
- // [58, 2], MICRO_LOAD_CONST_0 2 1:0-1:27
151
- // [59, 1, 0], MICRO_LOAD_CONST_1 [1, 0]
152
- // [64, 3], MICRO_GET_PROP_0 3 1:0-1:27
153
- // [65, 1], MICRO_GET_PROP_1 1
154
- // [66, 2], MICRO_GET_PROP_2 2
155
- // [67], MICRO_GET_PROP_3
156
- // [58, 4], MICRO_LOAD_CONST_0 4 1:12-1:26
157
- // [59, 2, 0], MICRO_LOAD_CONST_1 [2, 0]
158
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = reg[3](recv=reg[1], 1 args) 1:0-1:27
159
-
160
- // What the opcodes "MICRO_LOAD_CONST_0" (58) and "MICRO_LOAD_CONST_1" (59) look like:
161
- case 58:
162
- // MICRO_LOAD_CONST_0
163
- this._internals[0] = this._operand();
164
- break;
165
- case 59:
166
- // MICRO_LOAD_CONST_1
167
- frame.regs[this._internals[0]] = this._constant();
168
- break;
169
- ```
170
-
171
- - Fixed `Macro Opcodes` possibly clashing variables when merging opcode handlers.
172
-
173
- - Added support for update expressions on member expressions (`object.prop++`, `object.prop--`)
174
-
175
- - Added support for Template literals (ES6 feature added for convenience)
176
-
177
- - 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
178
-
179
- ## `0.0.6` Register based
180
-
181
- - Switched from stack-based to register-based VM.
182
-
183
- - `Specialized Opcodes` now applies to any fixed-size instruction, instead of just singular operands.
184
- - - Specialized Opcodes never applies to N-sized instructions, such as `MAKE_CLOSURE`, `BUILD_ARRAY`, `CALL`, etc.
185
-
186
- - `Macro Opcodes` can now include jumping/terminating opcodes if it's the last instruction in the sequence.
187
-
188
- - Added new option `aliasedOpcodes` which creates duplicate opcodes, including variants with shuffled operand order.
189
-
190
- ```js
191
- // Input Code
192
- console.log("Hello, world!");
193
-
194
- // Before
195
- // [2, 1, 0], LOAD_GLOBAL reg[1] = console 1:0-1:7
196
- // [0, 2, 1], LOAD_CONST reg[2] = "log" 1:0-1:28
197
- // [8, 3, 1, 2], GET_PROP [3, 1, 2] 1:0-1:28
198
- // [0, 4, 2], LOAD_CONST reg[4] = "Hello, world!" 1:12-1:27
199
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
200
- // [0, 1, 3], LOAD_CONST reg[1] = undefined
201
- // [45, 1], RETURN reg[1]
202
-
203
- // What the opcode "LOAD_GLOBAL" looks like:
204
- case OP.LOAD_GLOBAL:
205
- var dst = this._operand();
206
- frame.regs[dst] = this.globals[this.constants[this._operand()]];
207
- break;
208
-
209
- // After
210
- // [52040, 0, 1], ALIAS_LOAD_GLOBAL_1_0 [0, 1] 1:0-1:7
211
- // [24862, 1, 2], ALIAS_LOAD_CONST_1_0 [1, 2] 1:0-1:28
212
- // [25202, 1, 2, 3], ALIAS_GET_PROP_1_2_0 [1, 2, 3] 1:0-1:28
213
- // [24862, 2, 4], ALIAS_LOAD_CONST_1_0 [2, 4] 1:12-1:27
214
- // [43, 5, 1, 3, 1, 4], CALL_METHOD reg[5] = method(recv=reg[1], fn=reg[3], 1 args)1:0-1:28
215
- // [24862, 3, 1], ALIAS_LOAD_CONST_1_0 [3, 1]
216
- // [51807, 1], ALIAS_RETURN_0 1
217
-
218
- // What the opcode "ALIAS_LOAD_GLOBAL_1_0" (52040) looks like:
219
- case 52040:
220
- // ALIAS_LOAD_GLOBAL_1_0 (order: [1,0])
221
- let _unsortedOperands = [this._operand(), this._operand()];
222
- let _operands = [_unsortedOperands[1], _unsortedOperands[0]];
223
- var dst = _operands[0];
224
- frame.regs[dst] = this.globals[this.constants[_operands[1]]];
225
- break;
226
- ```
227
-
228
- - Added new option `concealConstants` which XOR decrypts numbers and strings at runtime.
229
-
230
- - Top level variables are now renamed and not exposed globally. To export a global function, you can use `window.MyGlobalFunction = function(){...}`
231
-
232
- - Accessing an undeclared global variable will throw a ReferenceError
233
-
234
- ## `0.0.5` Generated Opcodes
235
-
236
- - Added new option `specializedOpcodes` which creates specialized opcodes for commonly used opcode+operand pairs.
237
-
238
- ```js
239
- // Input Code
240
- console.log("Hello world!");
241
-
242
- // Before
243
- // [3, 0], LOAD_GLOBAL "console" 1:0-1:7
244
- // [0, 1], LOAD_CONST "log" 1:0-1:27
245
- // [5], GET_PROP 1:0-1:27
246
- // [0, 2], LOAD_CONST "Hello world!" 1:12-1:26
247
- // [12, 1], CALL_METHOD (1 args) 1:0-1:27
248
- // [14], POP 1:0-1:28
249
-
250
- // What the opcode "LOAD_GLOBAL" looks like:
251
- case OP.LOAD_GLOBAL:
252
- this._push(this.globals[this.constants[this._operand()]]);
253
- break;
254
-
255
- // After
256
- // [64], LOAD_GLOBAL_0 1:0-1:7
257
- // [65], LOAD_CONST_1 1:0-1:27
258
- // [5], GET_PROP 1:0-1:27
259
- // [66], LOAD_CONST_2 1:12-1:26
260
- // [67], CALL_METHOD_1 1:0-1:27
261
- // [14], POP 1:0-1:28
262
-
263
- // What the opcode "LOAD_GLOBAL_0" (64) looks like:
264
- case 64:
265
- // LOAD_GLOBAL_0 (specialized)
266
- this._push(this.globals[this.constants[0]]);
267
- break;
268
- ```
269
-
270
- - Added new option `macroOpcodes` which combines multiple opcodes commonly used from your bytecode
271
-
272
- ```js
273
- // Input Code
274
- console.log("Hello world!");
275
- console.log("Hello world!");
276
-
277
- // Before
278
- // [3, 0], LOAD_GLOBAL "console" 1:0-1:7
279
- // [0, 1], LOAD_CONST "log" 1:0-1:27
280
- // [5], GET_PROP 1:0-1:27
281
- // [0, 2], LOAD_CONST "Hello world!" 1:12-1:26
282
- // [12, 1], CALL_METHOD (1 args) 1:0-1:27
283
- // [14], POP 1:0-1:28
284
- // [3, 0], LOAD_GLOBAL "console" 2:0-2:7
285
- // [0, 1], LOAD_CONST "log" 2:0-2:27
286
- // [5], GET_PROP 2:0-2:27
287
- // [0, 2], LOAD_CONST "Hello world!" 2:12-2:26
288
- // [12, 1], CALL_METHOD (1 args) 2:0-2:27
289
- // [14], POP 2:0-2:28
290
-
291
- // After
292
- // [64, 0, 1, 2], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST [0, 1, 2]
293
- // [12, 1], CALL_METHOD (1 args) 1:0-1:27
294
- // [14], POP 1:0-1:28
295
- // [64, 0, 1, 2], LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST [0, 1, 2]
296
- // [12, 1], CALL_METHOD (1 args) 2:0-2:27
297
- // [14], POP 2:0-2:28
298
-
299
- // What the opcode "LOAD_GLOBAL,LOAD_CONST,GET_PROP,LOAD_CONST" (64) looks like:
300
- case 64:
301
- {
302
- // LOAD_GLOBAL
303
- this._push(this.globals[this.constants[this._operand()]]);
304
- // LOAD_CONST
305
- this._push(this.constants[this._operand()]);
306
- // GET_PROP
307
- // Stack: [..., obj, key] -> [..., obj, obj[key]]
308
- // obj is PEEKED (not popped) - CALL_METHOD needs it as receiver
309
- var key = this._pop();
310
- var obj = this.peek();
311
- this._push(obj[key]);
312
- // LOAD_CONST
313
- this._push(this.constants[this._operand()]);
314
- break;
315
- }
316
- ```
317
-
318
- - Flattened the bytecode. Now, instructions can read as many operands as needed, and it's unclear to distinguish between opcodes and operands:
319
-
320
- ```js
321
- // Before (Operands clearly visible)
322
- var BYTECODE = [[3, 0], [0, 1], [5, undefined], [0, 2], [12, 1], [14, undefined], [3, 0], [0, 1], [5, undefined], [0, 2], [12, 1], [14, undefined], [13, undefined]];
323
-
324
- // After (Flattened with multi-operand instruction support)
325
- var BYTECODE = [3, 0, 0, 1, 5, 0, 2, 12, 1, 14, 3, 0, 0, 1, 5, 0, 2, 12, 1, 14, 13];
326
- ```
327
-
328
- - Changed the bytecode to use ushorts (16-bit ints) allowing a max value of 65,535 for opcodes and operands.
329
-
330
-
331
- ## `0.0.3` First update
332
-
333
- - Created [Website Playground](https://development--confuser.netlify.app/vm)
334
-
335
- - Added partial support for `try..catch` - The `finally` operator is not supported yet
336
- - More ES5 coverage: getter/setters, debugger statement
337
- - Improved compilation process:
338
- - Parsing:
339
- JS -> AST
340
- Done by [@babel/parser](https://www.npmjs.com/package/@babel/parser)
341
- - Compilation:
342
- AST -> IR bytecode.
343
- This bytecode contains pseudo instructions and symbolic values for things like jump labels and constants
344
- - Transform passes (Assembler):
345
- Transform passes obfuscate and finally prepare the pseudo bytecode to be runnable. Here, all jump labels get converted into absolute PCs
346
- - Serializer:
347
- The bytecode is printed into the array form or encoded string if you have `encodeBytecode` enabled
348
- - Generating:
349
- This includes two sub-stages:
350
- - 1) Creating (another parsing->transforming->generating) the VM Runtime with the given options (randomized op codes, shuffled handlers)
351
- - 2) Placing the final bytecode into this VM Runtime
352
-
353
- - Typescript is now transpiled for NPM
354
-
355
-
356
-
357
- ## `0.0.2` First release
358
-
@@ -1,34 +0,0 @@
1
- const { readFileSync } = require("fs");
2
- const { join } = require("path");
3
- const babel = require("@babel/core");
4
- const { stripTypeScriptTypes } = require("node:module");
5
-
6
- module.exports = function inlineRuntimePlugin({ types: t }) {
7
- const rawContent = readFileSync(join(__dirname, "./src/runtime.ts"), "utf-8");
8
-
9
- const runtimeContent = stripTypeScriptTypes(rawContent);
10
-
11
- return {
12
- name: "inline-runtime",
13
- visitor: {
14
- VariableDeclarator(path) {
15
- if (
16
- path.node.id?.name === "readVMRuntimeFile" &&
17
- (path.node.init?.type === "ArrowFunctionExpression" ||
18
- path.node.init?.type === "FunctionExpression")
19
- ) {
20
- path.node.init = t.arrowFunctionExpression(
21
- [],
22
- t.stringLiteral(runtimeContent),
23
- );
24
- }
25
- },
26
- ImportDeclaration(path) {
27
- const src = path.node.source.value;
28
- if (src === "fs" || src === "path") {
29
- path.remove();
30
- }
31
- },
32
- },
33
- };
34
- };
package/babel.config.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "presets": [
3
- [
4
- "@babel/preset-env",
5
- {
6
- "targets": {
7
- "node": "18"
8
- },
9
- "modules": false
10
- }
11
- ],
12
- ["@babel/preset-typescript"]
13
- ],
14
- "plugins": [
15
- "./babel-plugin-inline-runtime.cjs",
16
- [
17
- "replace-import-extension",
18
- {
19
- "extMapping": { ".ts": ".js" }
20
- }
21
- ]
22
- ]
23
- }
package/bench.ts DELETED
@@ -1,146 +0,0 @@
1
- import vm from "vm";
2
- import fs from "fs";
3
- import path from "path";
4
-
5
- const args = process.argv.slice(2);
6
- if (args.length === 0) {
7
- console.log("Usage: node bench.js <file> [baseFile]");
8
- console.log(" node bench.js output.js - benchmark a single file");
9
- console.log(
10
- " node bench.js output.js base.js - compare output.js against base.js",
11
- );
12
- process.exit(1);
13
- }
14
-
15
- const ITERATIONS = 5_000;
16
-
17
- function getFileSize(filePath) {
18
- return fs.statSync(filePath).size;
19
- }
20
-
21
- function formatBytes(bytes) {
22
- if (bytes < 1024) return bytes + " B";
23
- return (bytes / 1024).toFixed(2) + " KB";
24
- }
25
-
26
- function runBenchmark(filePath) {
27
- const resolved = path.resolve(filePath);
28
- if (!fs.existsSync(resolved)) {
29
- console.error(`File not found: ${resolved}`);
30
- process.exit(1);
31
- }
32
-
33
- const code = fs.readFileSync(resolved, "utf-8");
34
- const script = new vm.Script(code, { filename: resolved });
35
-
36
- // Verify the file runs successfully first
37
- try {
38
- const testCtx = vm.createContext({
39
- window: {},
40
- console,
41
- process,
42
- performance,
43
- });
44
- script.runInContext(testCtx);
45
- } catch (e) {
46
- console.error(`\nFile failed to execute: ${resolved}`);
47
- console.error(e.message);
48
- process.exit(1);
49
- }
50
-
51
- const times = [];
52
-
53
- // Warmup
54
- for (let i = 0; i < 50; i++) {
55
- const ctx = vm.createContext({ window: {}, console, process, performance });
56
- script.runInContext(ctx);
57
- }
58
-
59
- for (let i = 0; i < ITERATIONS; i++) {
60
- const ctx = vm.createContext({ window: {}, console, process, performance });
61
- const start = performance.now();
62
- script.runInContext(ctx);
63
- const end = performance.now();
64
- times.push(end - start);
65
-
66
- if ((i + 1) % 1000 === 0) {
67
- process.stdout.write(`\r Running... ${i + 1}/${ITERATIONS}`);
68
- }
69
- }
70
- process.stdout.write("\r" + " ".repeat(40) + "\r");
71
-
72
- times.sort((a, b) => a - b);
73
-
74
- const median = times[Math.floor(times.length / 2)];
75
- const q1 = times[Math.floor(times.length * 0.25)];
76
- const q3 = times[Math.floor(times.length * 0.75)];
77
- const min = times[0];
78
- const max = times[times.length - 1];
79
- const mean = times.reduce((a, b) => a + b, 0) / times.length;
80
- const fileSize = getFileSize(resolved);
81
-
82
- return { median, q1, q3, min, max, mean, fileSize, filePath: resolved };
83
- }
84
-
85
- function formatMs(ms) {
86
- return ms.toFixed(3) + " ms";
87
- }
88
-
89
- function pctChange(base, compare) {
90
- const pct = ((compare - base) / base) * 100;
91
- const sign = pct >= 0 ? "+" : "";
92
- return sign + pct.toFixed(1) + "%";
93
- }
94
-
95
- function printResult(label, result) {
96
- console.log(` ${label}`);
97
- console.log(` ${"=".repeat(label.length)}`);
98
- console.log(` File: ${result.filePath}`);
99
- console.log(` File Size: ${formatBytes(result.fileSize)}`);
100
- console.log(` Iterations: ${ITERATIONS.toLocaleString()}`);
101
- console.log();
102
- console.log(` Median: ${formatMs(result.median)}`);
103
- console.log(` Mean: ${formatMs(result.mean)}`);
104
- console.log(` Q1: ${formatMs(result.q1)}`);
105
- console.log(` Q3: ${formatMs(result.q3)}`);
106
- console.log(` Min: ${formatMs(result.min)}`);
107
- console.log(` Max: ${formatMs(result.max)}`);
108
- console.log(` IQR: ${formatMs(result.q3 - result.q1)}`);
109
- }
110
-
111
- function printComparison(base, compare) {
112
- console.log(" Comparison (vs Base)");
113
- console.log(" " + "=".repeat(21));
114
- console.log(` Median: ${pctChange(base.median, compare.median)}`);
115
- console.log(` Mean: ${pctChange(base.mean, compare.mean)}`);
116
- console.log(` Q1: ${pctChange(base.q1, compare.q1)}`);
117
- console.log(` Q3: ${pctChange(base.q3, compare.q3)}`);
118
- console.log(` File Size: ${pctChange(base.fileSize, compare.fileSize)}`);
119
- }
120
-
121
- console.log();
122
- console.log(" JS-Confuser VM Benchmark");
123
- console.log(" " + "-".repeat(26));
124
- console.log();
125
-
126
- if (args.length === 1) {
127
- const result = runBenchmark(args[0]);
128
- printResult(path.basename(args[0]), result);
129
- console.log();
130
- } else {
131
- const baseFile = args[1];
132
- const compareFile = args[0];
133
-
134
- console.log(` Benchmarking base: ${path.basename(baseFile)}`);
135
- const baseResult = runBenchmark(baseFile);
136
- console.log(` Benchmarking compare: ${path.basename(compareFile)}`);
137
- const compareResult = runBenchmark(compareFile);
138
-
139
- console.log();
140
- printResult("Base: " + path.basename(baseFile), baseResult);
141
- console.log();
142
- printResult("Compare: " + path.basename(compareFile), compareResult);
143
- console.log();
144
- printComparison(baseResult, compareResult);
145
- console.log();
146
- }
package/disassemble.ts DELETED
@@ -1,12 +0,0 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import JsConfuserVM from "./src/index.ts";
3
-
4
- const fileContents = await readFile(process.argv[2], "utf-8");
5
-
6
- const outputFile =
7
- process.argv[3] || process.argv[2].replace(/\.js$/, ".disassembled.js");
8
-
9
- const output = await JsConfuserVM.disassemble(fileContents);
10
- console.log(output);
11
-
12
- await writeFile(outputFile, output, "utf-8");
package/index.ts DELETED
@@ -1,43 +0,0 @@
1
- import JsConfuserVM from "./src/index.ts";
2
- import { readFileSync, writeFileSync } from "fs";
3
-
4
- async function main() {
5
- // Compile and write the output to a file
6
- const sourceCode = readFileSync("input.js", "utf-8");
7
-
8
- const { code: orginalOutput } = await JsConfuserVM.obfuscate(sourceCode, {});
9
-
10
- const result = await JsConfuserVM.obfuscate(sourceCode, {
11
- target: "browser", // or "node"
12
- // randomizeOpcodes: true, // randomize the opcode numbers?
13
- // shuffleOpcodes: true, // shuffle order of opcode handlers in the runtime?
14
- // encodeBytecode: true, // encode the bytecode array?
15
- // concealConstants: true, // conceal strings and integers in the constant pool?
16
- // dispatcher: true, // create middleman blocks to process jumps?
17
- // selfModifying: true, // do self-modifying bytecode for function bodies?
18
- // macroOpcodes: true, // create combined opcodes for repeated instruction sequences?
19
- // specializedOpcodes: true, // create specialized opcodes for commonly used opcode+operand pairs?
20
- // aliasedOpcodes: true, // create duplicate opcodes for commonly used opcodes?
21
- // timingChecks: true, // add timing checks to detect debuggers?
22
- // minify: true, // pass final output through Google Closure Compiler? (Renames VM class properties)
23
- controlFlowFlattening: true,
24
- // stringConcealing: true,
25
- verbose: true,
26
- // classObfuscation: true,
27
- });
28
-
29
- writeFileSync("output.original.js", orginalOutput, "utf-8");
30
- writeFileSync("output.js", result.code, "utf-8");
31
-
32
- console.log("Saved");
33
-
34
- // Eval the code like our test suite does
35
- var window = { TEST_OUTPUT: null };
36
- eval(result.code);
37
- console.log(window.TEST_OUTPUT);
38
-
39
- delete result.code;
40
- console.log(JSON.stringify(result));
41
- }
42
-
43
- main();
@@ -1,10 +0,0 @@
1
- import { stripTypeScriptTypes } from 'node:module';
2
-
3
- export default {
4
- process(code, filePath) {
5
- if (filePath.endsWith('.ts')) {
6
- return { code: stripTypeScriptTypes(code) };
7
- }
8
- return { code };
9
- },
10
- };