js-confuser 1.5.9 → 1.7.0

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 (143) hide show
  1. package/.github/workflows/node.js.yml +2 -2
  2. package/CHANGELOG.md +55 -0
  3. package/README.md +346 -165
  4. package/dist/constants.js +6 -2
  5. package/dist/index.js +9 -21
  6. package/dist/obfuscator.js +19 -31
  7. package/dist/options.js +5 -5
  8. package/dist/order.js +1 -3
  9. package/dist/presets.js +6 -7
  10. package/dist/probability.js +2 -4
  11. package/dist/templates/bufferToString.js +13 -0
  12. package/dist/templates/crash.js +3 -3
  13. package/dist/templates/es5.js +18 -0
  14. package/dist/templates/functionLength.js +16 -0
  15. package/dist/transforms/calculator.js +77 -21
  16. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +980 -367
  17. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -1
  18. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +25 -26
  19. package/dist/transforms/deadCode.js +33 -25
  20. package/dist/transforms/dispatcher.js +8 -4
  21. package/dist/transforms/es5/antiDestructuring.js +2 -0
  22. package/dist/transforms/es5/es5.js +31 -34
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +92 -58
  24. package/dist/transforms/finalizer.js +82 -0
  25. package/dist/transforms/flatten.js +229 -148
  26. package/dist/transforms/identifier/globalAnalysis.js +88 -0
  27. package/dist/transforms/identifier/globalConcealing.js +10 -83
  28. package/dist/transforms/identifier/movedDeclarations.js +35 -88
  29. package/dist/transforms/identifier/renameVariables.js +124 -59
  30. package/dist/transforms/identifier/variableAnalysis.js +58 -62
  31. package/dist/transforms/lock/lock.js +0 -37
  32. package/dist/transforms/minify.js +60 -57
  33. package/dist/transforms/opaquePredicates.js +1 -1
  34. package/dist/transforms/preparation/preparation.js +2 -2
  35. package/dist/transforms/preparation.js +231 -0
  36. package/dist/transforms/renameLabels.js +1 -1
  37. package/dist/transforms/rgf.js +139 -247
  38. package/dist/transforms/stack.js +128 -26
  39. package/dist/transforms/string/encoding.js +150 -179
  40. package/dist/transforms/string/stringCompression.js +14 -15
  41. package/dist/transforms/string/stringConcealing.js +25 -8
  42. package/dist/transforms/string/stringEncoding.js +13 -24
  43. package/dist/transforms/transform.js +12 -19
  44. package/dist/traverse.js +24 -10
  45. package/dist/util/gen.js +17 -1
  46. package/dist/util/identifiers.js +37 -3
  47. package/dist/util/insert.js +35 -4
  48. package/dist/util/random.js +15 -0
  49. package/docs/ControlFlowFlattening.md +595 -0
  50. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  51. package/{Integrity.md → docs/Integrity.md} +2 -2
  52. package/docs/RGF.md +419 -0
  53. package/package.json +5 -5
  54. package/src/constants.ts +3 -0
  55. package/src/index.ts +2 -2
  56. package/src/obfuscator.ts +19 -31
  57. package/src/options.ts +14 -103
  58. package/src/order.ts +1 -5
  59. package/src/presets.ts +6 -7
  60. package/src/probability.ts +2 -3
  61. package/src/templates/bufferToString.ts +68 -0
  62. package/src/templates/crash.ts +15 -19
  63. package/src/templates/es5.ts +131 -0
  64. package/src/templates/functionLength.ts +14 -0
  65. package/src/transforms/calculator.ts +122 -59
  66. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +1583 -571
  67. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +4 -1
  68. package/src/transforms/deadCode.ts +383 -26
  69. package/src/transforms/dispatcher.ts +9 -4
  70. package/src/transforms/es5/antiDestructuring.ts +2 -0
  71. package/src/transforms/es5/es5.ts +32 -77
  72. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +133 -129
  73. package/src/transforms/{hexadecimalNumbers.ts → finalizer.ts} +29 -13
  74. package/src/transforms/flatten.ts +357 -300
  75. package/src/transforms/identifier/globalAnalysis.ts +85 -0
  76. package/src/transforms/identifier/globalConcealing.ts +14 -103
  77. package/src/transforms/identifier/movedDeclarations.ts +49 -102
  78. package/src/transforms/identifier/renameVariables.ts +149 -78
  79. package/src/transforms/identifier/variableAnalysis.ts +66 -73
  80. package/src/transforms/lock/lock.ts +1 -42
  81. package/src/transforms/minify.ts +91 -75
  82. package/src/transforms/opaquePredicates.ts +2 -2
  83. package/src/transforms/preparation.ts +238 -0
  84. package/src/transforms/renameLabels.ts +2 -2
  85. package/src/transforms/rgf.ts +213 -405
  86. package/src/transforms/stack.ts +156 -36
  87. package/src/transforms/string/encoding.ts +115 -212
  88. package/src/transforms/string/stringCompression.ts +27 -18
  89. package/src/transforms/string/stringConcealing.ts +39 -9
  90. package/src/transforms/string/stringEncoding.ts +18 -18
  91. package/src/transforms/transform.ts +21 -23
  92. package/src/traverse.ts +23 -4
  93. package/src/types.ts +2 -1
  94. package/src/util/gen.ts +28 -3
  95. package/src/util/identifiers.ts +43 -2
  96. package/src/util/insert.ts +38 -3
  97. package/src/util/random.ts +13 -0
  98. package/test/code/Cash.test.ts +1 -1
  99. package/test/code/Dynamic.test.ts +12 -10
  100. package/test/code/ES6.src.js +146 -0
  101. package/test/code/ES6.test.ts +28 -2
  102. package/test/index.test.ts +2 -1
  103. package/test/probability.test.ts +44 -0
  104. package/test/templates/template.test.ts +1 -1
  105. package/test/transforms/antiTooling.test.ts +22 -0
  106. package/test/transforms/calculator.test.ts +40 -0
  107. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +702 -160
  108. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +173 -0
  109. package/test/transforms/deadCode.test.ts +66 -15
  110. package/test/transforms/dispatcher.test.ts +20 -1
  111. package/test/transforms/es5/antiDestructuring.test.ts +16 -0
  112. package/test/transforms/flatten.test.ts +399 -86
  113. package/test/transforms/identifier/movedDeclarations.test.ts +63 -8
  114. package/test/transforms/identifier/renameVariables.test.ts +119 -0
  115. package/test/transforms/lock/antiDebug.test.ts +2 -2
  116. package/test/transforms/lock/lock.test.ts +1 -48
  117. package/test/transforms/minify.test.ts +104 -0
  118. package/test/transforms/preparation.test.ts +157 -0
  119. package/test/transforms/rgf.test.ts +261 -381
  120. package/test/transforms/stack.test.ts +143 -21
  121. package/test/transforms/string/stringCompression.test.ts +39 -0
  122. package/test/transforms/string/stringConcealing.test.ts +82 -0
  123. package/test/transforms/string/stringEncoding.test.ts +53 -2
  124. package/test/transforms/transform.test.ts +66 -0
  125. package/test/traverse.test.ts +139 -0
  126. package/test/util/identifiers.test.ts +113 -1
  127. package/test/util/insert.test.ts +57 -3
  128. package/src/transforms/controlFlowFlattening/choiceFlowObfuscation.ts +0 -87
  129. package/src/transforms/controlFlowFlattening/controlFlowObfuscation.ts +0 -203
  130. package/src/transforms/controlFlowFlattening/switchCaseObfuscation.ts +0 -130
  131. package/src/transforms/eval.ts +0 -89
  132. package/src/transforms/hideInitializingCode.ts +0 -432
  133. package/src/transforms/identifier/nameRecycling.ts +0 -280
  134. package/src/transforms/label.ts +0 -64
  135. package/src/transforms/preparation/nameConflicts.ts +0 -102
  136. package/src/transforms/preparation/preparation.ts +0 -176
  137. package/test/transforms/controlFlowFlattening/controlFlowObfuscation.test.ts +0 -101
  138. package/test/transforms/controlFlowFlattening/switchCaseObfuscation.test.ts +0 -120
  139. package/test/transforms/eval.test.ts +0 -131
  140. package/test/transforms/hideInitializingCode.test.ts +0 -336
  141. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
  142. package/test/transforms/preparation/nameConflicts.test.ts +0 -52
  143. package/test/transforms/preparation/preparation.test.ts +0 -62
@@ -1,6 +1,6 @@
1
1
  import JsConfuser from "../../../src/index";
2
2
 
3
- test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async () => {
3
+ test("Variant #1: Obfuscate code and still execute in correct order", async () => {
4
4
  var code = `
5
5
  var array = [];
6
6
 
@@ -15,7 +15,7 @@ test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async (
15
15
  array.push(9);
16
16
  array.push(10);
17
17
 
18
- input(array);
18
+ TEST_OUTPUT = array;
19
19
  `;
20
20
 
21
21
  var output = await JsConfuser(code, {
@@ -23,14 +23,17 @@ test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async (
23
23
  controlFlowFlattening: true,
24
24
  });
25
25
 
26
- function input(array) {
27
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
28
- }
26
+ // Ensure Control Flow Flattening applied
27
+ expect(output).toContain("while");
29
28
 
29
+ // Ensure the output is the exact same
30
+ var TEST_OUTPUT;
30
31
  eval(output);
32
+
33
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
31
34
  });
32
35
 
33
- test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
36
+ test("Variant #2: Obfuscate for loops", async () => {
34
37
  var code = `
35
38
  var array = [];
36
39
 
@@ -38,7 +41,7 @@ test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
38
41
  array.push(i);
39
42
  }
40
43
 
41
- input(array);
44
+ TEST_OUTPUT = array;
42
45
  `;
43
46
 
44
47
  var output = await JsConfuser(code, {
@@ -46,16 +49,20 @@ test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
46
49
  controlFlowFlattening: true,
47
50
  });
48
51
 
49
- expect(output).toContain("switch");
52
+ // Ensure Control Flow Flattening applied
53
+ expect(output).toContain("while");
50
54
 
51
- function input(array) {
52
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
53
- }
55
+ // Ensure the for statement got flattened
56
+ expect(output).not.toContain("for");
57
+
58
+ // Ensure the output is the exact same
59
+ var TEST_OUTPUT;
54
60
 
55
61
  eval(output);
62
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
56
63
  });
57
64
 
58
- test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
65
+ test("Variant #3: Obfuscate while loops", async () => {
59
66
  var code = `
60
67
  var array = [];
61
68
  var i = 1;
@@ -65,7 +72,7 @@ test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
65
72
  i++
66
73
  }
67
74
 
68
- input(array);
75
+ TEST_OUTPUT = array;
69
76
  `;
70
77
 
71
78
  var output = await JsConfuser(code, {
@@ -73,13 +80,14 @@ test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
73
80
  controlFlowFlattening: true,
74
81
  });
75
82
 
76
- expect(output).toContain("switch");
77
-
78
- function input(array) {
79
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
80
- }
83
+ // Ensure Control Flow Flattening applied
84
+ expect(output).toContain("while");
81
85
 
86
+ // Ensure the output is the exact same
87
+ var TEST_OUTPUT;
82
88
  eval(output);
89
+
90
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
83
91
  });
84
92
 
85
93
  test("Variant #4: Work with break statements", async () => {
@@ -94,7 +102,7 @@ test("Variant #4: Work with break statements", async () => {
94
102
  TEST_ARRAY.push(i);
95
103
  }
96
104
 
97
- input(TEST_ARRAY);
105
+ TEST_OUTPUT = TEST_ARRAY;
98
106
  `;
99
107
 
100
108
  var output = await JsConfuser(code, {
@@ -102,19 +110,20 @@ test("Variant #4: Work with break statements", async () => {
102
110
  controlFlowFlattening: true,
103
111
  });
104
112
 
113
+ // Ensure Control Flow Flattening applied
105
114
  expect(output).toContain("switch");
106
115
  expect(output).toContain("while");
107
116
 
108
- function input(array) {
109
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
110
- }
117
+ // Ensure the output is the exact same
118
+ var TEST_OUTPUT;
111
119
 
112
120
  eval(output);
121
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
113
122
  });
114
123
 
115
- test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, ControlFlowFlattening)", async () => {
124
+ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables)", async () => {
116
125
  var code = `
117
- let array = [];
126
+ let array = [];
118
127
 
119
128
  array.push(1);
120
129
  array.push(2);
@@ -127,7 +136,7 @@ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, Co
127
136
  array.push(9);
128
137
  array.push(10);
129
138
 
130
- input(array);
139
+ TEST_OUTPUT = array;
131
140
  `;
132
141
 
133
142
  var output = await JsConfuser(code, {
@@ -135,17 +144,24 @@ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, Co
135
144
  controlFlowFlattening: true,
136
145
  });
137
146
 
138
- expect(output).not.toContain("switch");
147
+ // Ensure Control Flow Flattening did NOT apply here
148
+ expect(output).not.toContain("while");
149
+
150
+ // Ensure the output is the exact same
151
+ var TEST_OUTPUT;
152
+ eval(output);
153
+
154
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
139
155
  });
140
156
 
141
- test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables, ControlFlowObfuscation)", async () => {
157
+ test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables)", async () => {
142
158
  var code = `
143
- var array=[];
144
- for ( let i =1; i <= 10; i++ ) {
145
- array.push(i);
146
- }
159
+ var array=[];
160
+ for ( let i =1; i <= 10; i++ ) {
161
+ array.push(i);
162
+ }
147
163
 
148
- input(array);
164
+ TEST_OUTPUT = array;
149
165
  `;
150
166
 
151
167
  var output = await JsConfuser(code, {
@@ -153,10 +169,17 @@ test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables, Co
153
169
  controlFlowFlattening: true,
154
170
  });
155
171
 
156
- expect(output).not.toContain("switch");
172
+ // Ensure Control Flow Flattening did NOT apply here
173
+ expect(output).not.toContain("while");
174
+
175
+ // Ensure the output is the exact same
176
+ var TEST_OUTPUT;
177
+ eval(output);
178
+
179
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
157
180
  });
158
181
 
159
- test("Variant #7: Accept percentages", async () => {
182
+ test("Variant #7: Allow option to be set a percentage threshold", async () => {
160
183
  var code = `
161
184
  var array = [];
162
185
  var i = 1;
@@ -166,7 +189,7 @@ test("Variant #7: Accept percentages", async () => {
166
189
  i++
167
190
  }
168
191
 
169
- input(array);
192
+ TEST_OUTPUT = array;
170
193
  `;
171
194
 
172
195
  var output = await JsConfuser(code, {
@@ -174,27 +197,48 @@ test("Variant #7: Accept percentages", async () => {
174
197
  controlFlowFlattening: 0.5,
175
198
  });
176
199
 
177
- // expect(output).toContain("switch");
178
- // expect(output).toContain("while");
179
-
180
- function input(array) {
181
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
182
- }
200
+ // Ensure the output is the exact same
201
+ var TEST_OUTPUT;
183
202
 
184
203
  eval(output);
204
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
185
205
  });
186
206
 
187
- it("Variant #8: Work when obfuscated multiple times", async () => {
207
+ test("Variant #8: Work when obfuscated multiple times", async () => {
188
208
  var code = `
189
209
  var array = [];
190
- var i = 1;
191
210
 
192
- while ( i <= 10 ) {
193
- array.push(i);
194
- i++
195
- }
211
+ switch(true){
212
+ case true: // Always true
213
+ if(true){ // Always true
214
+ var i;
196
215
 
197
- input(array);
216
+ for ( i = 1; i <= 10; i++ ) {
217
+ if(typeof i === "number") { // Always true
218
+ array.push(i);
219
+
220
+ var filler1;
221
+ var filler2;
222
+ var filler3;
223
+ }
224
+
225
+ var filler1;
226
+ var filler2;
227
+ var filler3;
228
+ }
229
+
230
+ var filler1;
231
+ var filler2;
232
+ var filler3;
233
+ }
234
+
235
+ var filler1;
236
+ var filler2;
237
+ var filler3;
238
+ break;
239
+ }
240
+
241
+ TEST_OUTPUT = array;
198
242
  `; // [1,2,3,4,5,6,7,8,9,10]
199
243
 
200
244
  var output = await JsConfuser(code, {
@@ -202,19 +246,19 @@ it("Variant #8: Work when obfuscated multiple times", async () => {
202
246
  controlFlowFlattening: true,
203
247
  });
204
248
 
249
+ // Ensure Control Flow Flattening applied
250
+ expect(output).toContain("while");
251
+
205
252
  var doublyObfuscated = await JsConfuser(output, {
206
253
  target: "node",
207
254
  controlFlowFlattening: true,
208
255
  });
209
256
 
210
- // expect(output).toContain("switch");
211
- // expect(output).toContain("while");
212
-
213
- function input(array) {
214
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
215
- }
257
+ // Ensure the output is the exact same
258
+ var TEST_OUTPUT;
216
259
 
217
260
  eval(doublyObfuscated);
261
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
218
262
  });
219
263
 
220
264
  test("Variant #9: Don't entangle floats or NaN", async () => {
@@ -539,43 +583,36 @@ test("Variant #17: Flatten with infinite for loop and break", async () => {
539
583
  expect(TEST_ARRAY).toStrictEqual([1, 2, 3, 4, 5]);
540
584
  });
541
585
 
542
- test("Variant #18: Flatten certain functions", async () => {
586
+ test("Variant #20: Work with redefined functions", async () => {
543
587
  var output = await JsConfuser(
544
588
  `
545
-
546
- var x = -1;
589
+ var counter = 0;
547
590
  function increment(){
548
- x++;
549
- return x;
550
- }
591
+ counter++;
551
592
 
552
- increment();
593
+ if(counter == 3) {
594
+ increment = () => { counter = "Correct Value" }
595
+ }
596
+ }
553
597
 
554
- function breakSequencingAttempt1(){
598
+ increment(); // 1
599
+ increment(); // 2
600
+ increment(); // 3
555
601
 
556
- }
602
+ var originalIncrement = increment;
603
+ increment = ()=>{};
557
604
 
558
- var element1 = increment();
559
- var element2 = increment();
560
- var element3;
561
-
562
- element3 = increment();
605
+ increment(); // 3
606
+ increment(); // 3
607
+ increment(); // 3
563
608
 
564
- function breakSequencingAttempt2(){
609
+ increment = undefined;
565
610
 
566
- }
611
+ increment = typeof increment === "undefined" ? originalIncrement : 0;
567
612
 
568
- TEST_ARRAY = [
569
- element1,
570
- element2,
571
- element3,
572
- ]
613
+ increment(); // "Correct Value"
573
614
 
574
- var fillerExpr1;
575
- var fillerExpr2;
576
- var fillerExpr3;
577
- var fillerExpr4;
578
- var fillerExpr5;
615
+ TEST_OUTPUT = counter;
579
616
  `,
580
617
  {
581
618
  target: "node",
@@ -583,38 +620,59 @@ test("Variant #18: Flatten certain functions", async () => {
583
620
  }
584
621
  );
585
622
 
586
- expect(output).not.toContain("function increment");
587
- expect(output).not.toContain("increment");
623
+ // Ensure Control Flow Flattening applied
624
+ expect(output).toContain("while");
588
625
 
589
- var TEST_ARRAY;
626
+ var TEST_OUTPUT;
590
627
 
591
628
  eval(output);
592
- expect(TEST_ARRAY).toStrictEqual([1, 2, 3]);
629
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
593
630
  });
594
631
 
595
- test("Variant #19: Don't flatten non-extractable functions", async () => {
632
+ // https://github.com/MichaelXF/js-confuser/issues/70
633
+ test("Variant #21: Don't move Import Declarations", async () => {
596
634
  var output = await JsConfuser(
597
635
  `
598
-
599
- var x = -1;
600
- function increment(){
601
- x++;
602
- return x;
636
+ import {createHash} from "crypto";
637
+ var inputString = "Hash this string";
638
+ var hashed = createHash("sha256").update(inputString).digest("hex");
639
+ TEST_OUTPUT = hashed;
640
+ `,
641
+ {
642
+ target: "node",
643
+ controlFlowFlattening: true,
603
644
  }
645
+ );
604
646
 
605
- increment();
647
+ // Ensure Control Flow FLattening was applied
648
+ expect(output).toContain("switch");
606
649
 
607
- TEST_ARRAY = [
608
- increment(),
609
- increment(),
610
- increment()
611
- ]
650
+ // Ensure the import declaration wasn't moved
651
+ expect(output.startsWith("import")).toStrictEqual(true);
612
652
 
613
- var fillerExpr1;
614
- var fillerExpr2;
615
- var fillerExpr3;
616
- var fillerExpr4;
617
- var fillerExpr5;
653
+ // Convert to runnable code
654
+ output = output.replace(
655
+ `import{createHash}from'crypto';`,
656
+ "const {createHash}=require('crypto');"
657
+ );
658
+
659
+ var TEST_OUTPUT = "";
660
+
661
+ eval(output);
662
+
663
+ expect(TEST_OUTPUT).toStrictEqual(
664
+ "1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
665
+ );
666
+ });
667
+
668
+ // https://github.com/MichaelXF/js-confuser/issues/81
669
+ test("Variant #22: Don't break typeof expression", async () => {
670
+ var output = await JsConfuser(
671
+ `
672
+ TEST_OUTPUT = false;
673
+ if(typeof nonExistentVariable === "undefined") {
674
+ TEST_OUTPUT = true;
675
+ }
618
676
  `,
619
677
  {
620
678
  target: "node",
@@ -622,65 +680,377 @@ test("Variant #19: Don't flatten non-extractable functions", async () => {
622
680
  }
623
681
  );
624
682
 
625
- expect(output).toContain("function increment");
626
-
627
- var TEST_ARRAY;
683
+ var TEST_OUTPUT;
628
684
 
629
685
  eval(output);
630
- expect(TEST_ARRAY).toStrictEqual([1, 2, 3]);
686
+
687
+ expect(TEST_OUTPUT).toStrictEqual(true);
631
688
  });
632
689
 
633
- test("Variant #20: Don't apply when functions are redefined", async () => {
690
+ test("Variant #23: Don't break Super calls", async () => {
634
691
  var output = await JsConfuser(
635
692
  `
636
-
637
- var x = -1;
638
- function increment(){
639
- x++;
640
- return x;
693
+ class MyClass1 {
694
+ constructor(val){
695
+ this.val = val;
641
696
  }
642
-
643
- // redefined function declaration
644
- increment = function(){
645
- return 0;
697
+ }
698
+ class MyClass2 extends MyClass1 {
699
+ constructor(){
700
+ super(10);
701
+
702
+ // Ensure ControlFlowFlattening applies here
703
+ var filler1;
704
+ var filler2;
705
+ var filler3;
646
706
  }
707
+ }
647
708
 
648
- increment();
709
+ var myObject = new MyClass2();
710
+ TEST_OUTPUT = myObject.val; // 10
711
+ `,
712
+ { target: "node", controlFlowFlattening: true }
713
+ );
649
714
 
650
- TEST_ARRAY = [
651
- increment(),
652
- increment(),
653
- increment()
654
- ]
715
+ var TEST_OUTPUT;
716
+ eval(output);
655
717
 
656
- var fillerExpr1;
657
- var fillerExpr2;
658
- var fillerExpr3;
659
- var fillerExpr4;
660
- var fillerExpr5;
661
- `,
662
- {
663
- target: "node",
664
- controlFlowFlattening: true,
718
+ expect(TEST_OUTPUT).toStrictEqual(10);
719
+ });
720
+
721
+ test("Variant #24: Nested function-calls with labeled breaks/continues", async () => {
722
+ var code = `
723
+ function myFunction(){
724
+ function x( STOP_VALUE ){
725
+ function y(){
726
+ function dead(){
727
+ var STOP_VALUE = 3;
728
+ }
729
+ function z(){
730
+ a: for(var i = 0; i < 10; i++) {
731
+ if (i == STOP_VALUE) {
732
+ var counter = 0;
733
+ b: for ( var j = 0; j < 10; j++ ) {
734
+ if(j == 4) {
735
+ counter -= 50;
736
+ continue b;
737
+ }
738
+ counter++;
739
+ if(j == 8) {
740
+ break a;
741
+ }
742
+ }
743
+ }
744
+ }
745
+
746
+ var filler1;
747
+ var filler2;
748
+ var filler3;
749
+ return i + j + counter;
750
+ }
751
+
752
+ var filler1;
753
+ var filler2;
754
+ var filler3;
755
+ return z();
756
+ }
757
+
758
+ var filler1;
759
+ var filler2;
760
+ var filler3;
761
+ return y();
665
762
  }
763
+
764
+ var filler1;
765
+ var filler2;
766
+ var filler3;
767
+ return x( 1 );
768
+ }
769
+
770
+ var x = myFunction();
771
+
772
+ TEST_OUTPUT = x;`;
773
+
774
+ var output = await JsConfuser(code, {
775
+ target: "node",
776
+ controlFlowFlattening: true,
777
+ renameVariables: true,
778
+ identifierGenerator: "mangled",
779
+ stack: true,
780
+ });
781
+
782
+ var TEST_OUTPUT;
783
+ eval(output);
784
+
785
+ expect(TEST_OUTPUT).toStrictEqual(-33);
786
+ });
787
+
788
+ test("Variant #25: Don't break call expressions to bound functions", async () => {
789
+ var code = `
790
+ var array = [];
791
+ array.push(1);
792
+ array.push(2);
793
+ array.push(3);
794
+ array.push(4);
795
+ array.push(5);
796
+
797
+ TEST_OUTPUT = array;
798
+ `;
799
+
800
+ var output = await JsConfuser(code, {
801
+ target: "node",
802
+ controlFlowFlattening: true,
803
+ });
804
+
805
+ // Ensure Control Flow Flattening applied
806
+ expect(output).toContain("while");
807
+
808
+ var TEST_OUTPUT;
809
+ eval(output);
810
+
811
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5]);
812
+ });
813
+
814
+ test("Variant #26: Add opaque predicates and still work", async () => {
815
+ var output = await JsConfuser(
816
+ `
817
+ TEST_OUTPUT = [];
818
+ if(true) TEST_OUTPUT.push(1);
819
+ else TEST_OUTPUT.push("Incorrect If Statement")
820
+
821
+ switch(true){
822
+ case true: TEST_OUTPUT.push(2); break;
823
+ default:
824
+ TEST_OUTPUT.push("Incorrect Switch")
825
+ }
826
+
827
+ TEST_OUTPUT.push( true ? 3 : "Incorrect Conditional Statement" );
828
+ `,
829
+ { target: "node", controlFlowFlattening: true }
666
830
  );
667
831
 
668
- expect(output).not.toContain("switch");
832
+ expect(output).toContain("while");
833
+ var TEST_OUTPUT = [];
669
834
 
670
- var TEST_ARRAY;
835
+ eval(output);
671
836
 
837
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3]);
838
+ });
839
+
840
+ test("Variant #27: Work on async/generator functions", async () => {
841
+ var output = await JsConfuser(
842
+ `
843
+ "use strict";
844
+ async function myAsyncFunction(){
845
+ await (1);
846
+ }
847
+
848
+ function* myGeneratorFunction(){
849
+ yield "Correct Value";
850
+ }
851
+
852
+ var x = myAsyncFunction();
853
+ var generatorObject = myGeneratorFunction();
854
+
855
+ TEST_OUTPUT = generatorObject.next().value;
856
+
857
+ var fillerVar1;
858
+ var fillerVar2;
859
+ var fillerVar3;
860
+ `,
861
+ { target: "node", controlFlowFlattening: true }
862
+ );
863
+
864
+ // Ensure Control Flow Flattening applied
865
+ expect(output).toContain("while");
866
+
867
+ var TEST_OUTPUT;
672
868
  eval(output);
673
- expect(TEST_ARRAY).toStrictEqual([0, 0, 0]);
869
+
870
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
674
871
  });
675
872
 
676
- // https://github.com/MichaelXF/js-confuser/issues/70
677
- test("Variant #21: Don't move Import Declarations", async () => {
873
+ test("Variant #28: Don't break update expressions", async () => {
874
+ var code = `
875
+ var counter = 0;
876
+
877
+ counter++; // 1
878
+ counter++; // 2
879
+ counter++; // 3
880
+ counter++; // 4
881
+ counter++; // 5
882
+ counter++; // 6
883
+ counter++; // 7
884
+ counter++; // 8
885
+ counter++; // 9
886
+ counter++; // 10
887
+
888
+ TEST_OUTPUT = counter;
889
+ `;
890
+
891
+ var output = await JsConfuser(code, {
892
+ target: "node",
893
+ controlFlowFlattening: true,
894
+ });
895
+
896
+ // Ensure Control Flow Flattening applied
897
+ expect(output).toContain("while");
898
+
899
+ var TEST_OUTPUT;
900
+ eval(output);
901
+
902
+ expect(TEST_OUTPUT).toStrictEqual(10);
903
+ });
904
+
905
+ test("Variant #29: Nested labeled break and continue statements with RGF enabled", async () => {
906
+ var code = `
907
+ function labeledBreaksAndContinues() {
908
+ var flag = true;
909
+
910
+ label_1: for (var i = 0; i < 20; i++) {
911
+ b: switch (i) {
912
+ case 15:
913
+ c: do {
914
+ if (i !== 15) {
915
+ break c;
916
+ }
917
+ flag = true;
918
+
919
+ break label_1;
920
+
921
+ var fillerVar1;
922
+ var fillerVar2;
923
+ var fillerVar3;
924
+ } while (i == 15);
925
+
926
+ break;
927
+
928
+ case 10:
929
+ continue label_1;
930
+
931
+ default:
932
+ flag = false;
933
+ break b;
934
+ }
935
+
936
+ var fillerVar1;
937
+ var fillerVar2;
938
+ var fillerVar3;
939
+ }
940
+
941
+ var fillerVar1;
942
+ var fillerVar2;
943
+ var fillerVar3;
944
+
945
+ if (flag) {
946
+ return i;
947
+ }
948
+ }
949
+
950
+ var fillerVar1;
951
+ var fillerVar2;
952
+ var fillerVar3;
953
+
954
+ TEST_OUTPUT = labeledBreaksAndContinues();
955
+ `; // This complex code produces the value of 15
956
+
957
+ var output = await JsConfuser(code, {
958
+ target: "node",
959
+ controlFlowFlattening: true,
960
+ rgf: true,
961
+ renameVariables: true,
962
+ identifierGenerator: "mangled",
963
+ });
964
+
965
+ // Run the code
966
+ var TEST_OUTPUT;
967
+ eval(output);
968
+
969
+ expect(TEST_OUTPUT).toStrictEqual(15);
970
+ });
971
+
972
+ test("Variant #30: Obfuscate switch statements", async () => {
678
973
  var output = await JsConfuser(
679
974
  `
680
- import {createHash} from "crypto";
681
- var inputString = "Hash this string";
682
- var hashed = createHash("sha256").update(inputString).digest("hex");
683
- TEST_OUTPUT = hashed;
975
+ switch("DON'T CHANGE ME"){} // Empty switch for testing
976
+
977
+ switch(true){
978
+ case 0:
979
+ TEST_OUTPUT = "Incorrect Value (1)";
980
+ break
981
+
982
+ case true:
983
+ TEST_OUTPUT = "First Correct Value";
984
+ break;
985
+
986
+ case 1:
987
+ case 2:
988
+ case 3:
989
+ case false:
990
+ TEST_OUTPUT = "Incorrect Value (2)";
991
+ break;
992
+ };
993
+
994
+ switch(TEST_OUTPUT){
995
+ case true:
996
+ TEST_OUTPUT = "Incorrect Value (3)";
997
+ break;
998
+
999
+ case false:
1000
+ TEST_OUTPUT = "Incorrect Value (4)";
1001
+ break;
1002
+
1003
+ default:
1004
+ TEST_OUTPUT = "Second Correct Value";
1005
+ }
1006
+
1007
+ switch(true){
1008
+ case false:
1009
+ throw new Error();
1010
+ break;
1011
+
1012
+ default:
1013
+ TEST_OUTPUT = "Incorrect Value";
1014
+
1015
+ case true:
1016
+ TEST_OUTPUT = "Third Correct Value";
1017
+ // Fall-through test
1018
+ case 10:
1019
+ TEST_OUTPUT = "Fourth Correct Value";
1020
+
1021
+ }
1022
+
1023
+ switch(true){
1024
+ default:
1025
+ throw new Error("NO");
1026
+ break;
1027
+ case true:
1028
+
1029
+ break;
1030
+ }
1031
+
1032
+ var hitDefault = false;
1033
+
1034
+ switch(true){
1035
+ case 1:
1036
+ case 2:
1037
+
1038
+ default:
1039
+ hitDefault = true;
1040
+ break;
1041
+
1042
+ case 3:
1043
+ case 4:
1044
+ break;
1045
+ }
1046
+
1047
+ if(!hitDefault) {
1048
+ throw new Error("Did not hit default case");
1049
+ }
1050
+
1051
+ var filler1;
1052
+ var filler2;
1053
+ var filler3;
684
1054
  `,
685
1055
  {
686
1056
  target: "node",
@@ -688,45 +1058,217 @@ test("Variant #21: Don't move Import Declarations", async () => {
688
1058
  }
689
1059
  );
690
1060
 
691
- // Ensure Control Flow FLattening was applied
692
- expect(output).toContain("switch");
1061
+ // Ensure Control Flow Flattening applied
1062
+ expect(output).toContain("while");
693
1063
 
694
- // Ensure the import declaration wasn't moved
695
- expect(output.startsWith("import")).toStrictEqual(true);
1064
+ // Ensure switch-statements got changed
1065
+ expect(output).not.toContain("switch(true)");
1066
+ expect(output).not.toContain("switch(TEST_OUTPUT)");
696
1067
 
697
- // Convert to runnable code
698
- output = output.replace(
699
- `import{createHash}from'crypto';`,
700
- "const {createHash}=require('crypto');"
1068
+ var TEST_OUTPUT;
1069
+ eval(output);
1070
+
1071
+ expect(TEST_OUTPUT).toStrictEqual("Fourth Correct Value");
1072
+ });
1073
+
1074
+ test("Variant #31: Don't break nested function calls", async () => {
1075
+ var output = await JsConfuser(
1076
+ `
1077
+ var i;
1078
+ var counter = 0;
1079
+ for(i = 0; i < 10;) {
1080
+ function logger(x){
1081
+ counter++;
1082
+ }
1083
+
1084
+ logger("Hello World");
1085
+ i++
1086
+ }
1087
+
1088
+ TEST_OUTPUT = counter;
1089
+ `,
1090
+ { target: "node", controlFlowFlattening: true }
701
1091
  );
702
1092
 
703
- var TEST_OUTPUT = "";
1093
+ expect(output).toContain("while");
704
1094
 
1095
+ var TEST_OUTPUT;
705
1096
  eval(output);
706
1097
 
707
- expect(TEST_OUTPUT).toStrictEqual(
708
- "1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
1098
+ expect(TEST_OUTPUT).toStrictEqual(10);
1099
+ });
1100
+
1101
+ test("Variant #32: Don't break same name function calls", async () => {
1102
+ var output = await JsConfuser(
1103
+ `
1104
+ var counter = 0;
1105
+
1106
+ function a(){
1107
+ // Outer a called
1108
+ counter *= 2;
1109
+ }
1110
+
1111
+ var i;
1112
+
1113
+ for(i = 0; i < 10;) {
1114
+ function a(){
1115
+ // Inner a called
1116
+ counter += 1;
1117
+ }
1118
+
1119
+ a(); // Inner a
1120
+ i++;
1121
+ }
1122
+
1123
+ a(); // Inner a, Outer a got renamed
1124
+
1125
+ TEST_OUTPUT = counter;
1126
+ `,
1127
+ { target: "node", controlFlowFlattening: true }
709
1128
  );
1129
+
1130
+ expect(output).toContain("while");
1131
+
1132
+ var TEST_OUTPUT;
1133
+ eval(output);
1134
+
1135
+ expect(TEST_OUTPUT).toStrictEqual(11);
710
1136
  });
711
1137
 
712
- // https://github.com/MichaelXF/js-confuser/issues/81
713
- test("Variant #22: Don't break typeof expression", async () => {
1138
+ test("Variant #33: Don't break same name function declarations that are not ran", async () => {
714
1139
  var output = await JsConfuser(
715
1140
  `
716
- TEST_OUTPUT = false;
717
- if(typeof nonExistentVariable === "undefined") {
718
- TEST_OUTPUT = true;
1141
+ var counter = 0;
1142
+
1143
+ function a(){
1144
+ // Outer a called
1145
+ counter += 1;
719
1146
  }
720
- `,
721
- {
722
- target: "node",
723
- controlFlowFlattening: true,
1147
+
1148
+ for(var i = 0; i < 10;) {
1149
+ if(false){
1150
+ function a(){
1151
+ // Inner a called
1152
+ counter = 0;
1153
+ }
1154
+ }
1155
+
1156
+
1157
+ a(); // Outer a
1158
+ i++;
724
1159
  }
1160
+
1161
+ a(); // Outer a
1162
+
1163
+ TEST_OUTPUT = counter;
1164
+ `,
1165
+ { target: "node", controlFlowFlattening: true }
725
1166
  );
726
1167
 
1168
+ expect(output).toContain("while");
1169
+
1170
+ var TEST_OUTPUT;
1171
+ eval(output);
1172
+
1173
+ expect(TEST_OUTPUT).toStrictEqual(11);
1174
+ });
1175
+
1176
+ test("Variant #34: Flatten If, For, While, Do-while, and Switch statements multiple times", async () => {
1177
+ var code = `
1178
+ var counter = -1;
1179
+
1180
+ function incrementCounter(){
1181
+ counter++;
1182
+ }
1183
+
1184
+ if(false) {
1185
+
1186
+ } else {
1187
+ counter = 0;
1188
+ }
1189
+
1190
+ if(true){
1191
+ for(var i = 0; i < 10; i++) {
1192
+ switch(i){
1193
+ default:
1194
+ i++;
1195
+ break;
1196
+
1197
+ case 6:
1198
+ if(false){
1199
+ function incrementCounter(){
1200
+ throw new Error("Fake counter function");
1201
+ }
1202
+ }
1203
+
1204
+ while(i != 10) {
1205
+ incrementCounter();
1206
+ if(counter > 10) break;
1207
+ }
1208
+
1209
+ do {
1210
+ counter--;
1211
+ } while (counter > 5)
1212
+ // Fall-through
1213
+ case 8:
1214
+ counter *= 2;
1215
+ break;
1216
+ }
1217
+ }
1218
+ } else {
1219
+ counter = "Incorrect Value";
1220
+ }
1221
+ if(false){
1222
+ counter = "Incorrect Value";
1223
+ }
1224
+
1225
+ TEST_OUTPUT = counter;
1226
+ `;
1227
+
1228
+ var firstObfuscation = await JsConfuser(code, {
1229
+ target: "node",
1230
+ controlFlowFlattening: true,
1231
+ });
1232
+ var secondObfuscation = await JsConfuser(firstObfuscation, {
1233
+ target: "node",
1234
+ controlFlowFlattening: true,
1235
+ });
1236
+
727
1237
  var TEST_OUTPUT;
1238
+ eval(secondObfuscation);
1239
+
1240
+ expect(TEST_OUTPUT).toStrictEqual(10);
1241
+ });
1242
+
1243
+ test("Variant #35: Redefined function declaration + variable declaration", async () => {
1244
+ var code = `
1245
+ function push(str){
1246
+ TEST_OUTPUT.push(str);
1247
+ }
1248
+
1249
+ x();
1250
+
1251
+ var x = ()=>push("Top x");
1252
+
1253
+ x()
1254
+
1255
+ function x(){ push("Bottom x") }
728
1256
 
1257
+ if(true){
1258
+
1259
+ x();
1260
+
1261
+ function x(){ push("Nested x") }
1262
+ }
1263
+ `;
1264
+
1265
+ var output = await JsConfuser(code, {
1266
+ target: "node",
1267
+ controlFlowFlattening: true,
1268
+ });
1269
+
1270
+ var TEST_OUTPUT = [];
729
1271
  eval(output);
730
1272
 
731
- expect(TEST_OUTPUT).toStrictEqual(true);
1273
+ expect(TEST_OUTPUT).toStrictEqual(["Bottom x", "Top x", "Nested x"]);
732
1274
  });