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
@@ -1,329 +0,0 @@
1
- /**
2
- * Simple bytecode disassembler for debugging.
3
- *
4
- * Takes the bytecode debug comment block (as generated by the compiler)
5
- * and produces a flat pseudo-code listing with registers, gotos, and labels.
6
- */
7
-
8
- // Regex to match a single instruction line from the debug comment block.
9
- // Groups: raw array, opcode name, annotation (rest of line after opcode name)
10
- const INSTR_RE = /^\s*\/\/\s*\[([^\]]+)\],\s+(\w+)\s+(.*?)$/;
11
-
12
- // Label line: // label_name:
13
- const LABEL_RE = /^\s*\/\/\s*(\w+):$/;
14
-
15
- interface ParsedInstr {
16
- raw: number[];
17
- opName: string;
18
- annotation: string;
19
- }
20
-
21
- interface Line {
22
- kind: "label" | "instr";
23
- label?: string;
24
- instr?: ParsedInstr;
25
- }
26
-
27
- function parseBlock(commentBlock: string): Line[] {
28
- const lines: Line[] = [];
29
- for (const raw of commentBlock.split("\n")) {
30
- const trimmed = raw.trim();
31
- if (!trimmed || !trimmed.startsWith("//")) continue;
32
-
33
- const instrMatch = trimmed.match(INSTR_RE);
34
- if (instrMatch) {
35
- const nums = instrMatch[1].split(",").map((s) => parseInt(s.trim(), 10));
36
- lines.push({
37
- kind: "instr",
38
- instr: {
39
- raw: nums,
40
- opName: instrMatch[2],
41
- annotation: instrMatch[3].trim(),
42
- },
43
- });
44
- continue;
45
- }
46
-
47
- const labelMatch = trimmed.match(LABEL_RE);
48
- if (labelMatch) {
49
- lines.push({ kind: "label", label: labelMatch[1] });
50
- }
51
- }
52
- return lines;
53
- }
54
-
55
- // Extract a label name from an annotation string like "[4, while_exit_8]" or "while_top_7"
56
- // Also handles "PC=fn_1_1" for closures
57
- function extractLabel(annotation: string): string | null {
58
- // Strip trailing source location info like "3:4-7:5"
59
- const stripped = annotation.replace(/\s+\d+:\d+-\d+:\d+\s*$/, "").trim();
60
-
61
- const bracketMatch = stripped.match(/\[\d+,\s*(\w+)\]/);
62
- if (bracketMatch) return bracketMatch[1];
63
-
64
- const pcMatch = stripped.match(/\bPC=(\w+)/);
65
- if (pcMatch) return pcMatch[1];
66
-
67
- const gotoMatch = stripped.match(/\bgoto\s+(\w+)/);
68
- if (gotoMatch) return gotoMatch[1];
69
-
70
- // Bare label at end: last whitespace-separated token
71
- const lastToken = stripped.split(/\s+/).pop();
72
- if (lastToken && /^[a-zA-Z_]\w*$/.test(lastToken)) return lastToken;
73
-
74
- return null;
75
- }
76
-
77
- const ARITH_SYMBOLS: Record<string, string> = {
78
- ADD: "+",
79
- SUB: "-",
80
- MUL: "*",
81
- DIV: "/",
82
- MOD: "%",
83
- BAND: "&",
84
- BOR: "|",
85
- BXOR: "^",
86
- SHL: "<<",
87
- SHR: ">>",
88
- USHR: ">>>",
89
- };
90
-
91
- const CMP_SYMBOLS: Record<string, string> = {
92
- LT: "<",
93
- GT: ">",
94
- LTE: "<=",
95
- GTE: ">=",
96
- EQ: "===",
97
- NEQ: "!==",
98
- LOOSE_EQ: "==",
99
- LOOSE_NEQ: "!=",
100
- IN: "in",
101
- INSTANCEOF: "instanceof",
102
- };
103
-
104
- const UNARY_SYMBOLS: Record<string, string> = {
105
- UNARY_NEG: "-",
106
- UNARY_POS: "+",
107
- UNARY_NOT: "!",
108
- UNARY_BITNOT: "~",
109
- };
110
-
111
- // Extract value from annotation like 'reg[1] = "Hello"' or 'reg[1] = 42'
112
- function extractConstValue(annotation: string): string | null {
113
- const m = annotation.match(/=\s*(.+?)(?:\s+\d+:\d+-\d+:\d+)?$/);
114
- return m ? m[1].trim() : null;
115
- }
116
-
117
- function disassembleInstr(instr: ParsedInstr): string {
118
- const { raw, opName, annotation } = instr;
119
- const r = (i: number) => `r${raw[i]}`;
120
-
121
- switch (opName) {
122
- case "LOAD_CONST": {
123
- const val = extractConstValue(annotation);
124
- return `${r(1)} = ${val ?? `const[${raw[2]}]`}`;
125
- }
126
- case "LOAD_INT":
127
- return `${r(1)} = ${raw[2]}`;
128
- case "LOAD_GLOBAL": {
129
- const val = extractConstValue(annotation);
130
- return `${r(1)} = ${val ?? `global[${raw[2]}]`}`;
131
- }
132
- case "LOAD_UPVALUE":
133
- return `${r(1)} = upvalue[${raw[2]}]`;
134
- case "LOAD_THIS":
135
- return `${r(1)} = this`;
136
- case "MOVE":
137
- return `${r(1)} = ${r(2)}`;
138
-
139
- case "STORE_GLOBAL": {
140
- // annotation: globals[name] = reg[src]
141
- const val = extractConstValue(annotation);
142
- return `global[${val ?? raw[1]}] = ${r(2)}`;
143
- }
144
- case "STORE_UPVALUE":
145
- return `upvalue[${raw[1]}] = ${r(2)}`;
146
-
147
- case "GET_PROP":
148
- return `${r(1)} = ${r(2)}[${r(3)}]`;
149
- case "SET_PROP":
150
- return `${r(1)}[${r(2)}] = ${r(3)}`;
151
- case "DELETE_PROP":
152
- return `${r(1)} = delete ${r(2)}[${r(3)}]`;
153
-
154
- // Arithmetic / bitwise
155
- case "ADD":
156
- case "SUB":
157
- case "MUL":
158
- case "DIV":
159
- case "MOD":
160
- case "BAND":
161
- case "BOR":
162
- case "BXOR":
163
- case "SHL":
164
- case "SHR":
165
- case "USHR":
166
- return `${r(1)} = ${r(2)} ${ARITH_SYMBOLS[opName]} ${r(3)}`;
167
-
168
- // Comparison
169
- case "LT":
170
- case "GT":
171
- case "LTE":
172
- case "GTE":
173
- case "EQ":
174
- case "NEQ":
175
- case "LOOSE_EQ":
176
- case "LOOSE_NEQ":
177
- case "IN":
178
- case "INSTANCEOF":
179
- return `${r(1)} = ${r(2)} ${CMP_SYMBOLS[opName]} ${r(3)}`;
180
-
181
- // Unary
182
- case "UNARY_NEG":
183
- case "UNARY_POS":
184
- case "UNARY_NOT":
185
- case "UNARY_BITNOT":
186
- return `${r(1)} = ${UNARY_SYMBOLS[opName]}${r(2)}`;
187
- case "TYPEOF":
188
- return `${r(1)} = typeof ${r(2)}`;
189
- case "VOID":
190
- return `${r(1)} = void ${r(2)}`;
191
- case "TYPEOF_SAFE": {
192
- const val = extractConstValue(annotation);
193
- return `${r(1)} = typeof ${val ?? `safe[${raw[2]}]`}`;
194
- }
195
-
196
- // Control flow
197
- case "JUMP": {
198
- const label = extractLabel(annotation);
199
- return `goto: ${label ?? `pc:${raw[1]}`}`;
200
- }
201
- case "JUMP_IF_FALSE": {
202
- const label = extractLabel(annotation);
203
- return `if (!${r(1)}) goto: ${label ?? `pc:${raw[2]}`}`;
204
- }
205
- case "JUMP_IF_TRUE": {
206
- const label = extractLabel(annotation);
207
- return `if (${r(1)}) goto: ${label ?? `pc:${raw[2]}`}`;
208
- }
209
- case "JUMP_REG":
210
- return `goto: *${r(1)}`;
211
-
212
- // Calls
213
- case "CALL": {
214
- const dst = r(1);
215
- const callee = r(2);
216
- const argc = raw[3];
217
- const args = raw.slice(4, 4 + argc).map((_, i) => `r${raw[4 + i]}`);
218
- return `${dst} = ${callee}(${args.join(", ")})`;
219
- }
220
- case "CALL_METHOD": {
221
- const dst = r(1);
222
- const recv = r(2);
223
- const callee = r(3);
224
- const argc = raw[4];
225
- const args = raw.slice(5, 5 + argc).map((_, i) => `r${raw[5 + i]}`);
226
- return `${dst} = ${callee}.call(${recv}, ${args.join(", ")})`;
227
- }
228
- case "NEW": {
229
- const dst = r(1);
230
- const callee = r(2);
231
- const argc = raw[3];
232
- const args = raw.slice(4, 4 + argc).map((_, i) => `r${raw[4 + i]}`);
233
- return `${dst} = new ${callee}(${args.join(", ")})`;
234
- }
235
-
236
- case "RETURN":
237
- return `return ${r(1)}`;
238
- case "THROW":
239
- return `throw ${r(1)}`;
240
-
241
- // Closures
242
- case "MAKE_CLOSURE": {
243
- const dst = r(1);
244
- const startPc = raw[2];
245
- const paramCount = raw[3];
246
- const regCount = raw[4];
247
- const uvCount = raw[5];
248
- const label = extractLabel(annotation);
249
- const uvParts: string[] = [];
250
- for (let i = 0; i < uvCount; i++) {
251
- const isLocal = raw[6 + i * 2];
252
- const idx = raw[6 + i * 2 + 1];
253
- uvParts.push(isLocal ? `local[${idx}]` : `uv[${idx}]`);
254
- }
255
- const uvStr = uvCount > 0 ? `, upvalues=[${uvParts.join(", ")}]` : "";
256
- return `${dst} = MakeClosure(${label ?? `pc:${startPc}`}, params=${paramCount}, regs=${regCount}${uvStr})`;
257
- }
258
-
259
- // Collections
260
- case "BUILD_ARRAY": {
261
- const dst = r(1);
262
- const count = raw[2];
263
- const elems = raw.slice(3, 3 + count).map((_, i) => `r${raw[3 + i]}`);
264
- return `${dst} = [${elems.join(", ")}]`;
265
- }
266
- case "BUILD_OBJECT": {
267
- const dst = r(1);
268
- const pairCount = raw[2];
269
- const pairs: string[] = [];
270
- for (let i = 0; i < pairCount; i++) {
271
- pairs.push(`[r${raw[3 + i * 2]}]: r${raw[4 + i * 2]}`);
272
- }
273
- return `${dst} = {${pairs.join(", ")}}`;
274
- }
275
-
276
- // Property definitions
277
- case "DEFINE_GETTER":
278
- return `Object.defineGetter(${r(1)}, ${r(2)}, ${r(3)})`;
279
- case "DEFINE_SETTER":
280
- return `Object.defineSetter(${r(1)}, ${r(2)}, ${r(3)})`;
281
-
282
- // For-in
283
- case "FOR_IN_SETUP":
284
- return `${r(1)} = ForInSetup(${r(2)})`;
285
- case "FOR_IN_NEXT": {
286
- const label = extractLabel(annotation);
287
- return `${r(1)} = ForInNext(${r(2)}) else goto ${label ?? `pc:${raw[3]}`}`;
288
- }
289
-
290
- // Exception handling
291
- case "TRY_SETUP": {
292
- return `try { catch -> pc:${raw[1]}, exReg=${r(2)}`;
293
- }
294
- case "TRY_END":
295
- return `} // end try`;
296
-
297
- // Self-modifying
298
- case "PATCH":
299
- return `patch(dest=pc:${raw[1]}, src=pc:${raw[2]}..${raw[3]})`;
300
-
301
- case "DEBUGGER":
302
- return `debugger`;
303
-
304
- default:
305
- return `??? ${opName} [${raw.join(", ")}]`;
306
- }
307
- }
308
-
309
- export function disassembleCommentBlock(commentBlock: string): string {
310
- const lines = parseBlock(commentBlock);
311
-
312
- let bodies: string[][] = [];
313
- let currentBody: string[] = [];
314
-
315
- for (const line of lines) {
316
- if (line.kind === "label") {
317
- let newBody = [];
318
- newBody.push(`// ${line.label}:`);
319
- bodies.push(newBody);
320
- currentBody = newBody;
321
- } else if (line.instr) {
322
- currentBody.push(` ${disassembleInstr(line.instr)}`);
323
- }
324
- }
325
-
326
- const out: string[] = bodies.flatMap((body) => body);
327
-
328
- return out.join("\n");
329
- }
package/src/index.ts DELETED
@@ -1,24 +0,0 @@
1
- import { compileAndSerialize } from "./compiler.ts";
2
- import type { Options } from "./options.ts";
3
- import { DEFAULT_OPTIONS } from "./options.ts";
4
- import { disassembleCommentBlock } from "./disassembler.ts";
5
- import type { ObfuscationResult } from "./types.ts";
6
-
7
- async function obfuscate(
8
- source: string,
9
- options: Options = DEFAULT_OPTIONS,
10
- ): Promise<ObfuscationResult> {
11
- const result = await compileAndSerialize(source, options);
12
-
13
- return result;
14
- }
15
-
16
- async function disassemble(bytecodeComments: string) {
17
- return disassembleCommentBlock(bytecodeComments);
18
- }
19
-
20
- export const JsConfuserVM = {
21
- obfuscate,
22
- disassemble,
23
- };
24
- export default JsConfuserVM;
package/src/minify.ts DELETED
@@ -1,21 +0,0 @@
1
- import ClosureCompiler from "google-closure-compiler";
2
-
3
- export function minify(sourceCode: string): Promise<string> {
4
- return new Promise((resolve, reject) => {
5
- const compiler = new ClosureCompiler({
6
- compilation_level: "ADVANCED",
7
- warning_level: "QUIET",
8
- });
9
-
10
- const compilerProcess = compiler.run((exitCode: number, stdOut: string, stdErr: string) => {
11
- if (exitCode !== 0) {
12
- reject(new Error(stdErr || `Closure Compiler exited with code ${exitCode}`));
13
- } else {
14
- resolve(stdOut);
15
- }
16
- });
17
-
18
- compilerProcess.stdin.write(sourceCode);
19
- compilerProcess.stdin.end();
20
- });
21
- }
package/src/options.ts DELETED
@@ -1,24 +0,0 @@
1
- export interface Options {
2
- target?: "node" | "browser";
3
-
4
- randomizeOpcodes?: boolean; // randomize the opcode numbers?
5
- shuffleOpcodes?: boolean; // shuffle order of opcode handlers in the runtime?
6
- encodeBytecode?: boolean; // encode bytecode? when off, comments for instructions are added
7
- selfModifying?: boolean; // do self-modifying bytecode for function bodies?
8
- dispatcher?: boolean; // create middleman blocks to process jumps?
9
- controlFlowFlattening?: boolean; // flatten the control flow of your program into a convoluted state machine?
10
- macroOpcodes?: boolean; // create combined opcodes for repeated instruction sequences?
11
- specializedOpcodes?: boolean; // create specialized opcodes for commonly used opcode+operand pairs?
12
- aliasedOpcodes?: boolean; // create duplicate opcodes for commonly used opcodes?
13
- handlerTable?: boolean; // convert switch dispatch to a handler table on the VM prototype?
14
- timingChecks?: boolean; // add timing checks to detect debuggers?
15
- concealConstants?: boolean; // conceal strings and integers in the constant pool?
16
- classObfuscation?: boolean; // obfuscate the VM runtime classes?
17
- minify?: boolean; // pass final output through Google Closure Compiler? (Renames VM class properties)
18
-
19
- stringConcealing?: boolean;
20
-
21
- verbose?: boolean;
22
- }
23
-
24
- export const DEFAULT_OPTIONS = {};