js-confuser 1.2.2 → 1.4.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 (78) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +4 -1
  3. package/dist/parser.js +1 -2
  4. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +482 -91
  5. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -0
  6. package/dist/transforms/controlFlowFlattening/{switchCaseObfucation.js → switchCaseObfuscation.js} +2 -2
  7. package/dist/transforms/deadCode.js +1 -1
  8. package/dist/transforms/dispatcher.js +7 -6
  9. package/dist/transforms/eval.js +1 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +4 -2
  11. package/dist/transforms/hideInitializingCode.js +4 -1
  12. package/dist/transforms/identifier/globalConcealing.js +18 -8
  13. package/dist/transforms/identifier/variableAnalysis.js +1 -1
  14. package/dist/transforms/label.js +11 -2
  15. package/dist/transforms/lock/antiDebug.js +32 -13
  16. package/dist/transforms/lock/lock.js +3 -3
  17. package/dist/transforms/minify.js +2 -2
  18. package/dist/transforms/opaquePredicates.js +4 -2
  19. package/dist/transforms/preparation/preparation.js +8 -0
  20. package/dist/transforms/renameLabels.js +17 -3
  21. package/dist/transforms/rgf.js +8 -3
  22. package/dist/transforms/stack.js +1 -1
  23. package/dist/transforms/string/encoding.js +74 -0
  24. package/dist/transforms/string/stringCompression.js +6 -2
  25. package/dist/transforms/string/stringConcealing.js +1 -1
  26. package/dist/transforms/string/stringSplitting.js +6 -0
  27. package/dist/traverse.js +0 -34
  28. package/dist/util/gen.js +3 -1
  29. package/dist/util/identifiers.js +8 -18
  30. package/dist/util/insert.js +4 -38
  31. package/package.json +2 -2
  32. package/src/options.ts +3 -3
  33. package/src/parser.ts +1 -2
  34. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +735 -134
  35. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +6 -0
  36. package/src/transforms/controlFlowFlattening/{switchCaseObfucation.ts → switchCaseObfuscation.ts} +6 -2
  37. package/src/transforms/deadCode.ts +8 -0
  38. package/src/transforms/dispatcher.ts +16 -6
  39. package/src/transforms/eval.ts +2 -1
  40. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +40 -5
  41. package/src/transforms/hideInitializingCode.ts +432 -425
  42. package/src/transforms/identifier/globalConcealing.ts +102 -38
  43. package/src/transforms/identifier/variableAnalysis.ts +1 -1
  44. package/src/transforms/label.ts +20 -2
  45. package/src/transforms/lock/antiDebug.ts +69 -33
  46. package/src/transforms/lock/lock.ts +4 -5
  47. package/src/transforms/minify.ts +2 -1
  48. package/src/transforms/opaquePredicates.ts +25 -3
  49. package/src/transforms/preparation/preparation.ts +8 -1
  50. package/src/transforms/renameLabels.ts +26 -3
  51. package/src/transforms/rgf.ts +6 -1
  52. package/src/transforms/stack.ts +2 -1
  53. package/src/transforms/string/encoding.ts +107 -1
  54. package/src/transforms/string/stringCompression.ts +28 -3
  55. package/src/transforms/string/stringConcealing.ts +2 -0
  56. package/src/transforms/string/stringSplitting.ts +11 -0
  57. package/src/transforms/transform.ts +1 -2
  58. package/src/traverse.ts +0 -30
  59. package/src/util/gen.ts +5 -3
  60. package/src/util/identifiers.ts +18 -19
  61. package/src/util/insert.ts +10 -76
  62. package/src/util/scope.ts +9 -9
  63. package/test/{transforms/compare.test.ts → compare.test.ts} +2 -2
  64. package/test/index.test.ts +109 -1
  65. package/test/templates/template.test.ts +14 -0
  66. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +392 -10
  67. package/test/transforms/dispatcher.test.ts +30 -0
  68. package/test/transforms/eval.test.ts +28 -0
  69. package/test/transforms/flatten.test.ts +28 -0
  70. package/test/transforms/hideInitializingCode.test.ts +336 -336
  71. package/test/transforms/identifier/renameVariables.test.ts +31 -0
  72. package/test/transforms/lock/antiDebug.test.ts +43 -0
  73. package/test/transforms/renameLabels.test.ts +33 -0
  74. package/test/transforms/rgf.test.ts +29 -0
  75. package/test/transforms/string/stringSplitting.test.ts +33 -0
  76. package/test/util/identifiers.test.ts +105 -17
  77. package/dist/util/expr.js +0 -60
  78. package/src/util/expr.ts +0 -56
@@ -29,10 +29,13 @@ var _controlFlowObfuscation = _interopRequireDefault(require("./controlFlowObfus
29
29
 
30
30
  var _expressionObfuscation = _interopRequireDefault(require("./expressionObfuscation"));
31
31
 
32
- var _switchCaseObfucation = _interopRequireDefault(require("./switchCaseObfucation"));
32
+ var _switchCaseObfuscation = _interopRequireDefault(require("./switchCaseObfuscation"));
33
33
 
34
34
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
35
 
36
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37
+
38
+ var flattenStructures = new Set(["IfStatement", "ForStatement", "WhileStatement", "DoWhileStatement"]);
36
39
  /**
37
40
  * Breaks functions into DAGs (Directed Acyclic Graphs)
38
41
  *
@@ -46,20 +49,33 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
46
49
  * - 2. At the end of each case, the state variable is updated to the next block of code.
47
50
  * - 3. The while loop continues until the the state variable is the end state.
48
51
  */
52
+
49
53
  class ControlFlowFlattening extends _transform.default {
50
54
  constructor(o) {
51
55
  super(o, _order.ObfuscateOrder.ControlFlowFlattening);
52
- this.before.push(new _expressionObfuscation.default(o));
53
- this.before.push(new _controlFlowObfuscation.default(o));
54
- this.after.push(new _switchCaseObfucation.default(o)); // this.after.push(new ChoiceFlowObfuscation(o));
56
+
57
+ _defineProperty(this, "isDebug", false);
58
+
59
+ if (!this.isDebug) {
60
+ this.before.push(new _expressionObfuscation.default(o));
61
+ this.after.push(new _controlFlowObfuscation.default(o));
62
+ this.after.push(new _switchCaseObfuscation.default(o));
63
+ } else {
64
+ console.warn("Debug mode enabled");
65
+ } // this.after.push(new ChoiceFlowObfuscation(o));
66
+
55
67
  }
56
68
 
57
69
  match(object, parents) {
58
- return (0, _traverse.isBlock)(object);
70
+ return (0, _traverse.isBlock)(object) && (!parents[1] || !flattenStructures.has(parents[1].type)) && (!parents[2] || !flattenStructures.has(parents[2].type));
59
71
  }
60
72
 
61
73
  transform(object, parents) {
62
74
  return () => {
75
+ if (object.body.length < 3) {
76
+ return;
77
+ }
78
+
63
79
  if ((0, _identifiers.containsLexicallyBoundVariables)(object, parents)) {
64
80
  return;
65
81
  }
@@ -78,14 +94,34 @@ class ControlFlowFlattening extends _transform.default {
78
94
 
79
95
  var functionDeclarations = new Set();
80
96
  var fnNames = new Set();
97
+ var illegalFnNames = new Set();
98
+ /**
99
+ * The variable names
100
+ *
101
+ * index -> var name
102
+ */
103
+
104
+ var stateVars = Array(this.isDebug ? 1 : (0, _random.getRandomInteger)(2, 5)).fill(0).map(() => this.getPlaceholder());
81
105
  body.forEach((stmt, i) => {
82
106
  if (stmt.type == "FunctionDeclaration") {
83
107
  functionDeclarations.add(stmt);
84
- fnNames.add(stmt.id && stmt.id.name);
108
+ var name = stmt.id && stmt.id.name;
109
+ fnNames.add(name);
110
+
111
+ if (stmt.body.type !== "BlockStatement") {
112
+ illegalFnNames.add(name);
113
+ } else {
114
+ (0, _traverse.walk)(stmt, [body, object, ...parents], (o, p) => {
115
+ if (o.type == "ThisExpression" || o.type == "SuperExpression" || o.type == "Identifier" && (o.name == "arguments" || o.name == "eval")) {
116
+ illegalFnNames.add(name);
117
+ return "EXIT";
118
+ }
119
+ });
120
+ }
85
121
  }
86
122
  });
87
123
  (0, _traverse.walk)(object, parents, (o, p) => {
88
- if (o.type == "Identifier") {
124
+ if (o.type == "Identifier" && fnNames.has(o.name)) {
89
125
  var info = (0, _identifiers.getIdentifierInfo)(o, p);
90
126
 
91
127
  if (!info.spec.isReferenced) {
@@ -103,6 +139,36 @@ class ControlFlowFlattening extends _transform.default {
103
139
  fnNames.delete(o.name);
104
140
  }
105
141
  }
142
+
143
+ if (!info.spec.isDefined) {
144
+ var b = (0, _traverse.getBlock)(o, p);
145
+
146
+ if (b !== object || !p[0] || p[0].type !== "CallExpression") {
147
+ illegalFnNames.add(o.name);
148
+ } else {
149
+ var isExtractable = false;
150
+
151
+ if (p[1]) {
152
+ if (p[1].type == "ExpressionStatement" && p[1].expression == p[0] && p[2] == object.body) {
153
+ isExtractable = true;
154
+ p[1].$callExpression = "ExpressionStatement";
155
+ p[1].$fnName = o.name;
156
+ } else if (p[1].type == "VariableDeclarator" && p[1].init == p[0] && p[2].length === 1 && p[4] == object.body) {
157
+ isExtractable = true;
158
+ p[3].$callExpression = "VariableDeclarator";
159
+ p[3].$fnName = o.name;
160
+ } else if (p[1].type == "AssignmentExpression" && p[1].operator == "=" && p[1].right === p[0] && p[2] && p[2].type == "ExpressionStatement" && p[3] == object.body) {
161
+ isExtractable = true;
162
+ p[2].$callExpression = "AssignmentExpression";
163
+ p[2].$fnName = o.name;
164
+ }
165
+ }
166
+
167
+ if (!isExtractable) {
168
+ illegalFnNames.add(o.name);
169
+ }
170
+ }
171
+ }
106
172
  }
107
173
  }); // redefined function,
108
174
 
@@ -110,7 +176,9 @@ class ControlFlowFlattening extends _transform.default {
110
176
  return;
111
177
  }
112
178
 
113
- var chunks = [[]];
179
+ illegalFnNames.forEach(illegal => {
180
+ fnNames.delete(illegal);
181
+ });
114
182
  var fraction = 0.9;
115
183
 
116
184
  if (body.length > 20) {
@@ -123,63 +191,339 @@ class ControlFlowFlattening extends _transform.default {
123
191
  fraction = 0.5;
124
192
  }
125
193
 
126
- body.forEach((x, i) => {
127
- if (functionDeclarations.has(x)) {
128
- return;
129
- }
194
+ var resultVar = this.getPlaceholder();
195
+ var argVar = this.getPlaceholder();
196
+ var needsResultAndArgVar = false;
197
+ var fnToLabel = Object.create(null);
198
+ fnNames.forEach(fnName => {
199
+ fnToLabel[fnName] = this.getPlaceholder();
200
+ });
130
201
 
131
- var currentChunk = chunks[chunks.length - 1];
202
+ const flattenBody = (body, startingLabel = this.getPlaceholder()) => {
203
+ var chunks = [];
204
+ var currentBody = [];
205
+ var currentLabel = startingLabel;
132
206
 
133
- if (!currentChunk.length || Math.random() < fraction) {
134
- currentChunk.push(x);
135
- } else {
136
- // Start new chunk
137
- chunks.push([x]);
138
- }
139
- });
207
+ const finishCurrentChunk = (pointingLabel, newLabel, addGotoStatement = true) => {
208
+ if (!newLabel) {
209
+ newLabel = this.getPlaceholder();
210
+ }
140
211
 
141
- if (!chunks[chunks.length - 1].length) {
142
- chunks.pop();
143
- }
212
+ if (!pointingLabel) {
213
+ pointingLabel = newLabel;
214
+ }
144
215
 
145
- if (chunks.length < 2) {
146
- return;
147
- } // Add empty chunks
216
+ if (addGotoStatement) {
217
+ currentBody.push({
218
+ type: "GotoStatement",
219
+ label: pointingLabel
220
+ });
221
+ }
222
+
223
+ chunks.push({
224
+ label: currentLabel,
225
+ body: [...currentBody]
226
+ });
227
+ currentLabel = newLabel;
228
+ currentBody = [];
229
+ };
230
+
231
+ body.forEach((stmt, i) => {
232
+ if (functionDeclarations.has(stmt)) {
233
+ return;
234
+ }
235
+
236
+ if (stmt.$exit) {
237
+ currentBody.push(stmt);
238
+ currentBody.push((0, _gen.BreakStatement)(switchLabel));
239
+ finishCurrentChunk(null, null, false);
240
+ return;
241
+ }
242
+
243
+ if (stmt.$callExpression && fnToLabel[stmt.$fnName]) {
244
+ var afterPath = this.getPlaceholder();
245
+ var args = [];
246
+
247
+ switch (stmt.$callExpression) {
248
+ // var a = fn();
249
+ case "VariableDeclarator":
250
+ args = stmt.declarations[0].init.arguments;
251
+ stmt.declarations[0].init = (0, _gen.Identifier)(resultVar);
252
+ break;
253
+ // fn();
254
+
255
+ case "ExpressionStatement":
256
+ args = stmt.expression.arguments;
257
+ stmt.expression = (0, _gen.Identifier)("undefined");
258
+ break;
259
+ // a = fn();
260
+
261
+ case "AssignmentExpression":
262
+ args = stmt.expression.right.arguments;
263
+ stmt.expression.right = (0, _gen.Identifier)(resultVar);
264
+ break;
265
+ }
266
+
267
+ needsResultAndArgVar = true;
268
+ currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(argVar), (0, _gen.ArrayExpression)([{
269
+ type: "StateIdentifier",
270
+ label: afterPath
271
+ }, (0, _gen.ArrayExpression)(args)]))));
272
+ finishCurrentChunk(fnToLabel[stmt.$fnName], afterPath);
273
+ }
148
274
 
275
+ if (stmt.type == "GotoStatement" && i !== body.length - 1) {
276
+ finishCurrentChunk(stmt.label);
277
+ return;
278
+ }
279
+
280
+ if (stmt.type == "LabeledStatement") {
281
+ var lbl = stmt.label.name;
282
+ var control = stmt.body;
283
+ var isSwitchStatement = control.type === "SwitchStatement";
284
+
285
+ if (isSwitchStatement || (control.type == "ForStatement" || control.type == "WhileStatement" || control.type == "DoWhileStatement") && control.body.type == "BlockStatement") {
286
+ if (isSwitchStatement) {
287
+ if (control.cases.length == 0 || // at least 1 case
288
+ control.cases.find(x => !x.test || // cant be default case
289
+ !x.consequent.length || // must have body
290
+ x.consequent.findIndex(node => node.type == "BreakStatement") !== x.consequent.length - 1 || // break statement must be at the end
291
+ x.consequent[x.consequent.length - 1].type !== // must end with break statement
292
+ "BreakStatement" || !x.consequent[x.consequent.length - 1].label || // must be labeled and correct
293
+ x.consequent[x.consequent.length - 1].label.name != lbl)) {
294
+ currentBody.push(stmt);
295
+ return;
296
+ }
297
+ }
298
+
299
+ var isLoop = !isSwitchStatement;
300
+ var supportContinueStatement = isLoop;
301
+ var testPath = this.getPlaceholder();
302
+ var updatePath = this.getPlaceholder();
303
+ var bodyPath = this.getPlaceholder();
304
+ var afterPath = this.getPlaceholder();
305
+ var possible = true;
306
+ var toReplace = [];
307
+ (0, _traverse.walk)(control.body, [], (o, p) => {
308
+ if (o.type == "BreakStatement" || supportContinueStatement && o.type == "ContinueStatement") {
309
+ if (!o.label || o.label.name !== lbl) {
310
+ possible = false;
311
+ return "EXIT";
312
+ }
313
+
314
+ if (o.label.name === lbl) {
315
+ return () => {
316
+ toReplace.push([o, {
317
+ type: "GotoStatement",
318
+ label: o.type == "BreakStatement" ? afterPath : updatePath
319
+ }]);
320
+ };
321
+ }
322
+ }
323
+ });
324
+
325
+ if (!possible) {
326
+ currentBody.push(stmt);
327
+ return;
328
+ }
329
+
330
+ toReplace.forEach(v => this.replace(v[0], v[1]));
331
+
332
+ if (isSwitchStatement) {
333
+ var switchVarName = this.getPlaceholder();
334
+ currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchVarName, control.discriminant)));
335
+ var afterPath = this.getPlaceholder();
336
+ finishCurrentChunk();
337
+ control.cases.forEach((switchCase, i) => {
338
+ var entryPath = this.getPlaceholder();
339
+ currentBody.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("===", (0, _gen.Identifier)(switchVarName), switchCase.test), [{
340
+ type: "GotoStatement",
341
+ label: entryPath
342
+ }]));
343
+ chunks.push(...flattenBody([...switchCase.consequent.slice(0, switchCase.consequent.length - 1), {
344
+ type: "GotoStatement",
345
+ label: afterPath
346
+ }], entryPath));
347
+
348
+ if (i === control.cases.length - 1) {} else {
349
+ finishCurrentChunk();
350
+ }
351
+ });
352
+ finishCurrentChunk(afterPath, afterPath);
353
+ return;
354
+ } else if (isLoop) {
355
+ var isPostTest = control.type == "DoWhileStatement"; // add initializing section to current chunk
356
+
357
+ if (control.init) {
358
+ if (control.init.type == "VariableDeclaration") {
359
+ currentBody.push(control.init);
360
+ } else {
361
+ currentBody.push((0, _gen.ExpressionStatement)(control.init));
362
+ }
363
+ } // create new label called `testPath` and have current chunk point to it (goto testPath)
364
+
365
+
366
+ finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
367
+ currentBody.push((0, _gen.IfStatement)(control.test || (0, _gen.Literal)(true), [{
368
+ type: "GotoStatement",
369
+ label: bodyPath
370
+ }])); // create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
371
+
372
+ finishCurrentChunk(afterPath, bodyPath);
373
+ var innerBothPath = this.getPlaceholder();
374
+ chunks.push(...flattenBody([...control.body.body, {
375
+ type: "GotoStatement",
376
+ label: updatePath
377
+ }], innerBothPath));
378
+ finishCurrentChunk(innerBothPath, updatePath);
379
+
380
+ if (control.update) {
381
+ currentBody.push((0, _gen.ExpressionStatement)(control.update));
382
+ }
383
+
384
+ finishCurrentChunk(testPath, afterPath);
385
+ return;
386
+ }
387
+ }
388
+ }
149
389
 
150
- Array((0, _random.getRandomInteger)(1, 10)).fill(0).forEach(() => {
151
- chunks.splice((0, _random.getRandomInteger)(0, chunks.length), 0, []);
390
+ if (stmt.type == "IfStatement" && stmt.consequent.type == "BlockStatement" && (!stmt.alternate || stmt.alternate.type == "BlockStatement")) {
391
+ finishCurrentChunk();
392
+ var hasAlternate = !!stmt.alternate;
393
+ (0, _assert.ok)(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
394
+ var yesPath = this.getPlaceholder();
395
+ var noPath = this.getPlaceholder();
396
+ var afterPath = this.getPlaceholder();
397
+ currentBody.push((0, _gen.IfStatement)(stmt.test, [{
398
+ type: "GotoStatement",
399
+ label: yesPath
400
+ }]));
401
+ chunks.push(...flattenBody([...stmt.consequent.body, {
402
+ type: "GotoStatement",
403
+ label: afterPath
404
+ }], yesPath));
405
+
406
+ if (hasAlternate) {
407
+ chunks.push(...flattenBody([...stmt.alternate.body, {
408
+ type: "GotoStatement",
409
+ label: afterPath
410
+ }], noPath));
411
+ finishCurrentChunk(noPath, afterPath);
412
+ } else {
413
+ finishCurrentChunk(afterPath, afterPath);
414
+ }
415
+
416
+ return;
417
+ }
418
+
419
+ if (!currentBody.length || Math.random() < fraction) {
420
+ currentBody.push(stmt);
421
+ } else {
422
+ // Start new chunk
423
+ finishCurrentChunk();
424
+ currentBody.push(stmt);
425
+ }
426
+ });
427
+ finishCurrentChunk();
428
+ chunks[chunks.length - 1].body.pop();
429
+ return chunks;
430
+ };
431
+
432
+ var chunks = [];
433
+ /**
434
+ * label: switch(a+b+c){...break label...}
435
+ */
436
+
437
+ var switchLabel = this.getPlaceholder();
438
+ functionDeclarations.forEach(node => {
439
+ if (node.id && fnNames.has(node.id.name)) {
440
+ var exitStateName = this.getPlaceholder();
441
+ var argumentsName = this.getPlaceholder();
442
+ needsResultAndArgVar = true;
443
+ node.body.body.push((0, _gen.ReturnStatement)());
444
+ (0, _traverse.walk)(node.body, [], (o, p) => {
445
+ if (o.type == "ReturnStatement") {
446
+ if (!(0, _insert.getFunction)(o, p)) {
447
+ return () => {
448
+ var exitExpr = (0, _gen.SequenceExpression)([(0, _gen.AssignmentExpression)("=", (0, _gen.ArrayPattern)(stateVars.map(_gen.Identifier)), (0, _gen.Identifier)(exitStateName)), (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(resultVar), o.argument || (0, _gen.Identifier)("undefined"))]);
449
+ this.replace(o, (0, _gen.ReturnStatement)(exitExpr));
450
+ };
451
+ }
452
+ }
453
+ });
454
+ var declarations = [(0, _gen.VariableDeclarator)((0, _gen.ArrayPattern)([(0, _gen.Identifier)(exitStateName), (0, _gen.Identifier)(argumentsName)]), (0, _gen.Identifier)(argVar))];
455
+
456
+ if (node.params.length) {
457
+ declarations.push((0, _gen.VariableDeclarator)((0, _gen.ArrayPattern)(node.params), (0, _gen.Identifier)(argumentsName)));
458
+ }
459
+
460
+ var innerName = this.getPlaceholder();
461
+ chunks.push(...flattenBody([(0, _gen.FunctionDeclaration)(innerName, [], [(0, _gen.VariableDeclaration)(declarations), ...node.body.body]), this.objectAssign((0, _gen.ExpressionStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(innerName), [])), {
462
+ $exit: true
463
+ })], fnToLabel[node.id.name]));
464
+ }
465
+ });
466
+ var startLabel = this.getPlaceholder();
467
+ chunks.push(...flattenBody(body, startLabel));
468
+ chunks[chunks.length - 1].body.push({
469
+ type: "GotoStatement",
470
+ label: "END_LABEL"
471
+ });
472
+ chunks.push({
473
+ label: "END_LABEL",
474
+ body: []
152
475
  });
153
476
  var caseSelection = new Set();
154
- var uniqueStatesNeeded = chunks.length + 1;
477
+ var uniqueStatesNeeded = chunks.length;
478
+ var endLabel = chunks[Object.keys(chunks).length - 1].label;
155
479
 
156
- for (var i = 0; i < uniqueStatesNeeded; i++) {
157
- var newState;
480
+ do {
481
+ var newState = (0, _random.getRandomInteger)(1, chunks.length * 15);
158
482
 
159
- do {
160
- newState = (0, _random.getRandomInteger)(1, chunks.length * 15);
161
- } while (caseSelection.has(newState));
483
+ if (this.isDebug) {
484
+ newState = caseSelection.size;
485
+ }
162
486
 
163
487
  caseSelection.add(newState);
164
- }
488
+ } while (caseSelection.size !== uniqueStatesNeeded);
165
489
 
166
490
  (0, _assert.ok)(caseSelection.size == uniqueStatesNeeded);
491
+ /**
492
+ * The accumulated state values
493
+ *
494
+ * index -> total state value
495
+ */
496
+
167
497
  var caseStates = Array.from(caseSelection);
168
- var startState = caseStates[0];
169
- var endState = caseStates[caseStates.length - 1];
170
- var stateVars = Array((0, _random.getRandomInteger)(2, 7)).fill(0).map(() => this.getPlaceholder());
171
- var stateValues = Array(stateVars.length).fill(0).map(() => (0, _random.getRandomInteger)(-250, 250));
498
+ /**
499
+ * The individual state values for each label
500
+ *
501
+ * labels right now are just chunk indexes (numbers)
502
+ *
503
+ * but will expand to if statements and functions when `goto statement` obfuscation is added
504
+ */
505
+
506
+ var labelToStates = Object.create(null);
507
+ Object.values(chunks).forEach((chunk, i) => {
508
+ var state = caseStates[i];
509
+ var stateValues = Array(stateVars.length).fill(0).map(() => (0, _random.getRandomInteger)(-250, 250));
172
510
 
173
- const getCurrentState = () => {
174
- return stateValues.reduce((a, b) => b + a, 0);
175
- };
511
+ const getCurrentState = () => {
512
+ return stateValues.reduce((a, b) => b + a, 0);
513
+ };
514
+
515
+ var correctIndex = (0, _random.getRandomInteger)(0, stateValues.length);
516
+ stateValues[correctIndex] = state - (getCurrentState() - stateValues[correctIndex]);
517
+ labelToStates[chunk.label] = stateValues;
518
+ }); // console.log(labelToStates);
176
519
 
177
- var correctIndex = (0, _random.getRandomInteger)(0, stateVars.length);
178
- stateValues[correctIndex] = startState - (getCurrentState() - stateValues[correctIndex]);
179
- var initStateValues = [...stateValues];
520
+ var initStateValues = [...labelToStates[startLabel]];
521
+ var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
180
522
 
181
- const numberLiteral = (num, depth) => {
182
- if (depth > 12 || Math.random() > 0.9 / (depth * 4)) {
523
+ const numberLiteral = (num, depth, stateValues) => {
524
+ (0, _assert.ok)(Array.isArray(stateValues));
525
+
526
+ if (depth > 10 || Math.random() > 0.8 / (depth * 4)) {
183
527
  return (0, _gen.Literal)(num);
184
528
  }
185
529
 
@@ -189,95 +533,142 @@ class ControlFlowFlattening extends _transform.default {
189
533
  var x = (0, _random.getRandomInteger)(-250, 250);
190
534
  var operator = (0, _random.choice)(["<", ">"]);
191
535
  var answer = operator == "<" ? x < stateValues[opposing] : x > stateValues[opposing];
192
- var correct = numberLiteral(num, depth + 1);
193
- var incorrect = numberLiteral((0, _random.getRandomInteger)(-250, 250), depth + 1);
194
- return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, numberLiteral(x, depth + 1), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
536
+ var correct = numberLiteral(num, depth + 1, stateValues);
537
+ var incorrect = numberLiteral((0, _random.getRandomInteger)(-250, 250), depth + 1, stateValues);
538
+ return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, numberLiteral(x, depth + 1, stateValues), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
195
539
  }
196
540
 
197
- return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), numberLiteral(num - stateValues[opposing], depth + 1));
541
+ return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), numberLiteral(num - stateValues[opposing], depth + 1, stateValues));
198
542
  };
199
543
 
200
- const createTransitionStatement = (index, add) => {
201
- var newValue = stateValues[index] + add;
544
+ const createTransitionExpression = (index, add, mutatingStateValues) => {
545
+ var newValue = mutatingStateValues[index] + add;
202
546
  var expr = null;
203
547
 
204
- if (Math.random() > 0.5) {
205
- expr = (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("+=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(add, 0)));
548
+ if (this.isDebug) {
549
+ expr = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(stateVars[index]), (0, _gen.Literal)(newValue));
550
+ } else if (Math.random() > 0.5) {
551
+ expr = (0, _gen.AssignmentExpression)("+=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(add, 0, mutatingStateValues));
206
552
  } else {
207
- var double = stateValues[index] * 2;
553
+ var double = mutatingStateValues[index] * 2;
208
554
  var diff = double - newValue;
209
- var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(2, 0));
210
- stateValues[index] = double;
211
- expr = (0, _gen.ExpressionStatement)((0, _gen.SequenceExpression)([first, (0, _gen.AssignmentExpression)("-=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(diff, 0))]));
555
+ var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(2, 0, mutatingStateValues));
556
+ mutatingStateValues[index] = double;
557
+ expr = (0, _gen.SequenceExpression)([first, (0, _gen.AssignmentExpression)("-=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(diff, 0, mutatingStateValues))]);
212
558
  }
213
559
 
214
- stateValues[index] = newValue;
560
+ mutatingStateValues[index] = newValue;
215
561
  return expr;
216
562
  };
217
563
 
218
564
  var order = Object.create(null);
219
- var cases = chunks.map((body, i) => {
565
+ var cases = [];
566
+ chunks.forEach((chunk, i) => {
567
+ // skip last case, its empty and never ran
568
+ if (chunk.label === endLabel) {
569
+ return;
570
+ }
571
+
572
+ (0, _assert.ok)(labelToStates[chunk.label]);
573
+ var state = caseStates[i];
220
574
  var made = 1;
221
- body.forEach(stmt => {
575
+ var breaksInsertion = [];
576
+ var staticStateValues = [...labelToStates[chunk.label]];
577
+ chunk.body.forEach((stmt, stmtIndex) => {
578
+ var addBreak = false;
222
579
  (0, _traverse.walk)(stmt, [], (o, p) => {
223
- if (o.type == "Literal" && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100000 && Math.random() < 4 / made && !p.find(x => (0, _insert.isVarContext)(x))) {
580
+ if (!this.isDebug && o.type == "Literal" && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100000 && Math.random() < 4 / made && !p.find(x => (0, _insert.isVarContext)(x))) {
224
581
  made++;
225
582
  return () => {
226
- this.replaceIdentifierOrLiteral(o, numberLiteral(o.value, 0), p);
583
+ this.replaceIdentifierOrLiteral(o, numberLiteral(o.value, 0, staticStateValues), p);
584
+ };
585
+ }
586
+
587
+ if (o.type == "StateIdentifier") {
588
+ return () => {
589
+ (0, _assert.ok)(labelToStates[o.label]);
590
+ this.replace(o, (0, _gen.ArrayExpression)(labelToStates[o.label].map(_gen.Literal)));
591
+ };
592
+ }
593
+
594
+ if (o.type == "GotoStatement") {
595
+ return () => {
596
+ var blockIndex = p.findIndex(node => (0, _traverse.isBlock)(node));
597
+
598
+ if (blockIndex === -1) {
599
+ addBreak = true;
600
+ } else {
601
+ var child = p[blockIndex - 2] || o;
602
+ var childIndex = p[blockIndex].body.indexOf(child);
603
+ p[blockIndex].body.splice(childIndex + 1, 0, (0, _gen.BreakStatement)(switchLabel));
604
+ }
605
+
606
+ var mutatingStateValues = [...labelToStates[chunk.label]];
607
+ var nextStateValues = labelToStates[o.label];
608
+ (0, _assert.ok)(nextStateValues, o.label);
609
+ this.replace(o, (0, _gen.ExpressionStatement)((0, _gen.SequenceExpression)(mutatingStateValues.map((_v, stateValueIndex) => {
610
+ var diff = nextStateValues[stateValueIndex] - mutatingStateValues[stateValueIndex];
611
+ return createTransitionExpression(stateValueIndex, diff, mutatingStateValues);
612
+ }))));
227
613
  };
228
614
  }
229
615
  });
616
+
617
+ if (addBreak) {
618
+ breaksInsertion.push(stmtIndex);
619
+ }
230
620
  });
231
- var state = caseStates[i];
232
- var nextState = caseStates[i + 1];
233
- var diff = nextState - state;
234
- var transitionStatements = [];
235
- (0, _assert.ok)(!isNaN(diff));
236
- var modifying = (0, _random.getRandomInteger)(0, stateVars.length);
237
- var shift = 0; // var c1 = Identifier("undefined");
238
- // this.addComment(c1, stateValues.join(", "));
239
- // transitionStatements.push(c1);
240
-
241
- transitionStatements.push(...Array.from(new Set(Array((0, _random.getRandomInteger)(0, stateVars.length - 2)).fill(0).map(() => (0, _random.getRandomInteger)(0, stateVars.length)).filter(x => x != modifying))).map(x => {
242
- var randomNumber = (0, _random.getRandomInteger)(-250, 250);
243
- shift += randomNumber;
244
- return createTransitionStatement(x, randomNumber);
245
- }));
246
- transitionStatements.push(createTransitionStatement(modifying, diff - shift)); // var c = Identifier("undefined");
621
+ breaksInsertion.sort();
622
+ breaksInsertion.reverse();
623
+ breaksInsertion.forEach(index => {
624
+ chunk.body.splice(index + 1, 0, (0, _gen.BreakStatement)(switchLabel));
625
+ }); // var c = Identifier("undefined");
247
626
  // this.addComment(c, stateValues.join(", "));
248
627
  // transitionStatements.push(c);
249
628
 
250
629
  var caseObject = {
251
- body: body,
630
+ body: chunk.body,
252
631
  state: state,
253
- nextState: nextState,
254
- order: i,
255
- transitionStatements: transitionStatements
632
+ order: i
256
633
  };
257
634
  order[i] = caseObject;
258
- return caseObject;
635
+ cases.push(caseObject);
259
636
  });
260
- (0, _random.shuffle)(cases);
637
+
638
+ if (!this.isDebug) {
639
+ (0, _random.shuffle)(cases);
640
+ }
641
+
261
642
  var discriminant = (0, _template.default)("".concat(stateVars.join("+"))).single().expression;
262
643
  body.length = 0;
263
644
 
264
645
  if (functionDeclarations.size) {
265
646
  functionDeclarations.forEach(x => {
266
- body.unshift((0, _insert.clone)(x));
647
+ if (!x.id || illegalFnNames.has(x.id.name)) {
648
+ body.unshift((0, _insert.clone)(x));
649
+ }
267
650
  });
268
651
  }
269
652
 
270
653
  var switchStatement = (0, _gen.SwitchStatement)(discriminant, cases.map((x, i) => {
271
654
  var statements = [];
272
655
  statements.push(...x.body);
273
- statements.push(...x.transitionStatements);
274
- statements.push((0, _gen.BreakStatement)());
275
656
  var test = (0, _gen.Literal)(x.state);
276
657
  return (0, _gen.SwitchCase)(test, statements);
277
658
  }));
278
- body.push((0, _gen.VariableDeclaration)(stateVars.map((stateVar, i) => {
659
+ var declarations = [];
660
+
661
+ if (needsResultAndArgVar) {
662
+ declarations.push((0, _gen.VariableDeclarator)(resultVar));
663
+ declarations.push((0, _gen.VariableDeclarator)(argVar));
664
+ }
665
+
666
+ declarations.push(...stateVars.map((stateVar, i) => {
279
667
  return (0, _gen.VariableDeclarator)(stateVar, (0, _gen.Literal)(initStateValues[i]));
280
- })), (0, _gen.WhileStatement)((0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(endState)), [switchStatement]));
668
+ }));
669
+ body.push((0, _gen.VariableDeclaration)(declarations), (0, _gen.WhileStatement)((0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(endState)), [(0, _gen.LabeledStatement)(switchLabel, switchStatement)])); // mark this object for switch case obfuscation
670
+
671
+ switchStatement.$controlFlowFlattening = true;
281
672
  };
282
673
  }
283
674