js-confuser 1.6.0 → 1.7.1

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 (67) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +215 -172
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +10 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +95 -59
  11. package/dist/transforms/extraction/objectExtraction.js +6 -1
  12. package/dist/transforms/flatten.js +224 -147
  13. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  14. package/dist/transforms/identifier/renameVariables.js +94 -41
  15. package/dist/transforms/lock/lock.js +0 -37
  16. package/dist/transforms/minify.js +2 -2
  17. package/dist/transforms/rgf.js +145 -244
  18. package/dist/transforms/stack.js +42 -1
  19. package/dist/transforms/transform.js +1 -1
  20. package/dist/util/gen.js +2 -1
  21. package/dist/util/identifiers.js +38 -4
  22. package/dist/util/insert.js +24 -3
  23. package/docs/ControlFlowFlattening.md +595 -0
  24. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  25. package/docs/ES5.md +197 -0
  26. package/{Integrity.md → docs/Integrity.md} +2 -2
  27. package/docs/RGF.md +419 -0
  28. package/package.json +2 -2
  29. package/src/constants.ts +3 -0
  30. package/src/obfuscator.ts +0 -4
  31. package/src/options.ts +9 -86
  32. package/src/presets.ts +6 -7
  33. package/src/templates/crash.ts +10 -10
  34. package/src/templates/functionLength.ts +14 -0
  35. package/src/transforms/dispatcher.ts +15 -1
  36. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +135 -130
  37. package/src/transforms/extraction/objectExtraction.ts +4 -0
  38. package/src/transforms/flatten.ts +357 -290
  39. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  40. package/src/transforms/identifier/renameVariables.ts +120 -56
  41. package/src/transforms/lock/lock.ts +1 -42
  42. package/src/transforms/minify.ts +11 -2
  43. package/src/transforms/rgf.ts +221 -402
  44. package/src/transforms/stack.ts +62 -0
  45. package/src/transforms/transform.ts +6 -2
  46. package/src/util/gen.ts +7 -2
  47. package/src/util/identifiers.ts +48 -4
  48. package/src/util/insert.ts +26 -2
  49. package/test/code/ES6.src.js +24 -0
  50. package/test/transforms/dispatcher.test.ts +27 -0
  51. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +21 -8
  52. package/test/transforms/extraction/objectExtraction.test.ts +35 -15
  53. package/test/transforms/flatten.test.ts +352 -88
  54. package/test/transforms/identifier/globalConcealing.test.ts +23 -2
  55. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  56. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  57. package/test/transforms/lock/integrity.test.ts +24 -0
  58. package/test/transforms/lock/lock.test.ts +1 -48
  59. package/test/transforms/minify.test.ts +19 -0
  60. package/test/transforms/rgf.test.ts +262 -353
  61. package/test/transforms/stack.test.ts +52 -0
  62. package/test/util/identifiers.test.ts +134 -1
  63. package/test/util/insert.test.ts +57 -3
  64. package/src/transforms/eval.ts +0 -89
  65. package/src/transforms/identifier/nameRecycling.ts +0 -280
  66. package/test/transforms/eval.test.ts +0 -131
  67. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
@@ -1,419 +1,328 @@
1
1
  import { writeFileSync } from "fs";
2
2
  import JsConfuser from "../../src/index";
3
3
 
4
- describe("RGF", () => {
5
- it("should contain `new Function` in the output and work", async () => {
6
- var output = await JsConfuser.obfuscate(
7
- `
8
- function add(a,b){
9
- return a + b;
10
- }
11
-
12
- input(add(10, 5))
13
- `,
14
- {
15
- target: "node",
16
- rgf: true,
17
- }
18
- );
19
-
20
- expect(output).toContain("new Function");
21
- var value = "never_called";
22
- function input(valueIn) {
23
- value = valueIn;
4
+ test("Variant #1: Convert Function Declaration into 'new Function' code", async () => {
5
+ var output = await JsConfuser.obfuscate(
6
+ `
7
+ function addTwoNumbers(a, b){
8
+ return a + b;
24
9
  }
10
+
11
+ TEST_OUTPUT = addTwoNumbers(10, 5);
12
+ `,
13
+ {
14
+ target: "node",
15
+ rgf: true,
16
+ }
17
+ );
25
18
 
26
- eval(output);
27
- expect(value).toStrictEqual(15);
28
- });
19
+ expect(output).toContain("new Function");
29
20
 
30
- it("should work with multiple functions", async () => {
31
- var output = await JsConfuser.obfuscate(
32
- `
33
- function add(a,b){
34
- return a + b;
35
- }
36
-
37
- function parse(str){
38
- return parseInt(str);
39
- }
40
-
41
- input(add(parse("20"), 5))
42
- `,
43
- {
44
- target: "node",
45
- rgf: true,
46
- }
47
- );
21
+ var TEST_OUTPUT;
22
+ eval(output);
48
23
 
49
- expect(output).toContain("new Function");
50
- var value = "never_called";
51
- function input(valueIn) {
52
- value = valueIn;
24
+ expect(TEST_OUTPUT).toStrictEqual(15);
25
+ });
26
+
27
+ test("Variant #2: Convert Function Expression into 'new Function' code", async () => {
28
+ var output = await JsConfuser.obfuscate(
29
+ `
30
+ var addTwoNumbers = function(a, b){
31
+ return a + b;
53
32
  }
33
+
34
+ TEST_OUTPUT = addTwoNumbers(10, 5);
35
+ `,
36
+ {
37
+ target: "node",
38
+ rgf: true,
39
+ }
40
+ );
54
41
 
55
- eval(output);
56
- expect(value).toStrictEqual(25);
57
- });
42
+ expect(output).toContain("new Function");
58
43
 
59
- it("should not change functions that have references to parent scopes", async () => {
60
- var output = await JsConfuser.obfuscate(
61
- `
62
- var parentScope = 0;
63
- function add(a,b){
44
+ var TEST_OUTPUT;
45
+ eval(output);
64
46
 
65
- // reference to parent scope
66
- return a + parentScope;
67
- }
68
-
69
- input(add(10, 5))
70
- `,
71
- {
72
- target: "node",
73
- rgf: true,
74
- }
75
- );
47
+ expect(TEST_OUTPUT).toStrictEqual(15);
48
+ });
49
+
50
+ test("Variant #3: Convert functions that use global variables", async () => {
51
+ var output = await JsConfuser.obfuscate(
52
+ `
53
+ function floorNumber(num){
54
+ return Math.floor(num);
55
+ }
56
+
57
+ TEST_OUTPUT = floorNumber(1.9);
58
+ `,
59
+ {
60
+ target: "node",
61
+ rgf: true,
62
+ }
63
+ );
76
64
 
77
- expect(output).not.toContain("new Function");
78
- });
65
+ expect(output).toContain("new Function");
79
66
 
80
- it("should not change functions that have mutate their parent scopes", async () => {
81
- var output = await JsConfuser.obfuscate(
82
- `
83
- var parentScope = 0;
84
- function add(a,b){
67
+ var TEST_OUTPUT;
68
+ eval(output);
85
69
 
86
- // modifies parent scope
87
- parentScope = 1;
88
- return a + b;
89
- }
90
-
91
- input(add(10, 5))
92
- `,
93
- {
94
- target: "node",
95
- rgf: true,
96
- }
97
- );
98
-
99
- expect(output).not.toContain("new Function");
100
- });
101
-
102
- it("should work with High Preset", async () => {
103
- var output = await JsConfuser.obfuscate(
104
- `
105
- function log2(){
106
- var inputFn = input;
107
- var inputString = "Hello World";
108
- inputFn(inputString)
70
+ expect(TEST_OUTPUT).toStrictEqual(1);
71
+ });
72
+
73
+ test("Variant #4: Don't convert functions that rely on outside-scoped variables", async () => {
74
+ var output = await JsConfuser.obfuscate(
75
+ `
76
+ var _Math = Math;
77
+
78
+ function floorNumber(num){
79
+ return _Math.floor(num);
109
80
  }
110
- log2()
81
+
82
+ TEST_OUTPUT = floorNumber(1.9);
111
83
  `,
112
- {
113
- target: "node",
114
- preset: "high",
115
- globalConcealing: false,
116
- }
117
- );
118
-
119
- var value = "never_called";
120
- function input(valueIn) {
121
- value = valueIn;
84
+ {
85
+ target: "node",
86
+ rgf: true,
122
87
  }
88
+ );
123
89
 
124
- eval(output);
125
-
126
- expect(value).toStrictEqual("Hello World");
127
- });
128
-
129
- // https://github.com/MichaelXF/js-confuser/issues/64
130
- it("should work on Arrow Functions", async () => {
131
- var output = await JsConfuser.obfuscate(
132
- `
133
- var double = (num)=>num*2;
134
- TEST_VALUE = double(10);
135
- `,
136
- {
137
- target: "node",
138
- rgf: true,
139
- }
140
- );
141
-
142
- expect(output).toContain("new Function");
143
-
144
- var TEST_VALUE;
145
-
146
- eval(output);
147
- expect(TEST_VALUE).toStrictEqual(20);
148
- });
149
-
150
- it("should work on Function Expressions", async () => {
151
- var output = await JsConfuser.obfuscate(
152
- `
153
- var double = function(num){
154
- return num * 2
155
- };
156
- TEST_VALUE = double(10);
157
- `,
158
- {
159
- target: "node",
160
- rgf: true,
161
- }
162
- );
90
+ expect(output).not.toContain("new Function");
163
91
 
164
- expect(output).toContain("new Function");
92
+ var TEST_OUTPUT;
93
+ eval(output);
165
94
 
166
- var TEST_VALUE;
95
+ expect(TEST_OUTPUT).toStrictEqual(1);
96
+ });
167
97
 
168
- eval(output);
169
- expect(TEST_VALUE).toStrictEqual(20);
170
- });
98
+ test("Variant #5: Don't convert functions that rely on outside-scoped variables (trap)", async () => {
99
+ var output = await JsConfuser.obfuscate(
100
+ `
101
+ var _Math = Math;
171
102
 
172
- it("should work on re-assigned functions", async () => {
173
- var output = await JsConfuser.obfuscate(
174
- `
175
- var fn1 = ()=>{
176
- return "FN1";
177
- }
178
- var fn2 = ()=>{
179
- fn1 = ()=>{
180
- return "FN1 - Modified"
181
- }
182
- }
183
- fn2();
184
- TEST_VALUE = fn1();
185
- `,
186
- {
187
- target: "node",
188
- rgf: true,
189
- }
190
- );
103
+ function floorNumber(num){
104
+ (()=>{
105
+ var _Math;
106
+ })();
107
+ return _Math.floor(num);
108
+ }
109
+
110
+ TEST_OUTPUT = floorNumber(1.9);
111
+ `,
112
+ {
113
+ target: "node",
114
+ rgf: true,
115
+ }
116
+ );
191
117
 
192
- expect(output).toContain("new Function");
118
+ expect(output).not.toContain("new Function");
193
119
 
194
- var TEST_VALUE;
120
+ var TEST_OUTPUT;
121
+ eval(output);
195
122
 
196
- eval(output);
197
- expect(TEST_VALUE).toStrictEqual("FN1 - Modified");
198
- });
123
+ expect(TEST_OUTPUT).toStrictEqual(1);
124
+ });
199
125
 
200
- it("should work on re-assigned functions to non-function values", async () => {
201
- var output = await JsConfuser.obfuscate(
202
- `
203
- var fn1 = ()=>{
204
- return "FN1";
205
- }
206
- var fn2 = ()=>{
207
- fn1 = undefined;
208
- }
209
- fn2();
210
- TEST_VALUE = typeof fn1;
211
- `,
212
- {
213
- target: "node",
214
- rgf: true,
215
- }
216
- );
126
+ test("Variant #6: Work on High Preset", async () => {
127
+ var output = await JsConfuser.obfuscate(
128
+ `
129
+ function addTwoNumbers(a, b){
130
+ return a + b;
131
+ }
132
+
133
+ TEST_OUTPUT = addTwoNumbers(10, 5);
134
+ `,
135
+ {
136
+ target: "node",
137
+ preset: "high",
138
+ rgf: true,
139
+ }
140
+ );
217
141
 
218
- expect(output).toContain("new Function");
142
+ var TEST_OUTPUT;
143
+ eval(output);
219
144
 
220
- var TEST_VALUE;
145
+ expect(TEST_OUTPUT).toStrictEqual(15);
146
+ });
221
147
 
222
- eval(output);
223
- expect(TEST_VALUE).toStrictEqual("undefined");
224
- });
148
+ test("Variant #7: Don't convert arrow, async, or generator functions", async () => {
149
+ var output = await JsConfuser.obfuscate(
150
+ `
151
+ var arrowFunction = ()=>{};
152
+ async function asyncFunction(){
225
153
 
226
- it("should not apply to functions that reference their parent scope in the parameters", async () => {
227
- var output = await JsConfuser.obfuscate(
228
- `
229
- var outsideVar = 0;
230
- function myFunction(insideVar = outsideVar){
231
- }
232
- `,
233
- {
234
- target: "node",
235
- rgf: true,
236
- }
237
- );
154
+ };
155
+ function* generatorFunction(){
156
+ yield "Correct Value";
157
+ };
238
158
 
239
- expect(output).not.toContain("new Function");
240
- });
159
+ TEST_OUTPUT = generatorFunction().next().value;
160
+ `,
161
+ {
162
+ target: "node",
163
+ rgf: true,
164
+ }
165
+ );
241
166
 
242
- it("should not apply to functions that reference their parent scope in previously defined names", async () => {
243
- var output = await JsConfuser.obfuscate(
244
- `
245
- var outsideVar = 0;
246
- function myFunction(){
247
- (function (){
248
- var outsideVar;
249
- })();
167
+ expect(output).not.toContain("new Function");
250
168
 
251
- console.log(outsideVar);
252
- }
253
- `,
254
- {
255
- target: "node",
256
- rgf: true,
257
- }
258
- );
169
+ var TEST_OUTPUT;
170
+ eval(output);
259
171
 
260
- expect(output).not.toContain("new Function");
261
- });
172
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
173
+ });
262
174
 
263
- it("should work with Control Flow Flattening and Duplicate Literals Removal enabled", async () => {
264
- var output = await JsConfuser.obfuscate(
265
- `
266
- var x = [1,1,1,1,1,1,1,1,1,1];
175
+ test("Variant #8: Modified Function", async () => {
176
+ var output = await JsConfuser.obfuscate(
177
+ `
178
+ function addTwoNumbers(x,y){
179
+ return x + y;
180
+ }
267
181
 
268
- function myFunction(){
269
- return 1;
270
- };
182
+ addTwoNumbers = function(){
183
+ return "Incorrect Value";
184
+ }
271
185
 
272
- input( myFunction() ); // 1
273
- `,
274
- {
275
- target: "node",
276
- controlFlowFlattening: true,
277
- duplicateLiteralsRemoval: true,
278
- rgf: true,
279
- }
280
- );
186
+ addTwoNumbers = ()=>{
187
+ return "Correct Value";
188
+ }
281
189
 
282
- var value = "never_called";
283
- function input(valueIn) {
284
- value = valueIn;
190
+ TEST_OUTPUT = addTwoNumbers(10, 5);
191
+ `,
192
+ {
193
+ target: "node",
194
+ rgf: true,
285
195
  }
196
+ );
286
197
 
287
- eval(output);
198
+ expect(output).toContain("new Function");
288
199
 
289
- expect(value).toStrictEqual(1);
290
- });
200
+ var TEST_OUTPUT;
201
+ eval(output);
291
202
 
292
- it("should work with String Encoding enabled", async () => {
293
- var output = await JsConfuser.obfuscate(
294
- `
295
- function myFunction(){
296
- var val1 = "\\x43\\x6F\\x72\\x72\\x65\\x63\\x74\\x20\\x56\\x61\\x6C\\x75\\x65"; // "Correct Value"
297
- var val2 = "Correct Value";
298
- return val1 === val2;
299
- }
203
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
204
+ });
205
+
206
+ test("Variant #8: Modified Function (non function value)", async () => {
207
+ var output = await JsConfuser.obfuscate(
208
+ `
209
+ function addTwoNumbers(x,y){
210
+ return x+y;
211
+ }
300
212
 
301
- TEST_OUTPUT = myFunction(); // true
213
+ addTwoNumbers = "Correct Value";
214
+
215
+ TEST_OUTPUT = addTwoNumbers;
302
216
  `,
303
- {
304
- target: "node",
305
- rgf: true,
306
- stringEncoding: true,
307
- }
308
- );
217
+ {
218
+ target: "node",
219
+ rgf: true,
220
+ }
221
+ );
309
222
 
310
- // Ensure RGF applied
311
- expect(output).toContain("new Function");
223
+ expect(output).toContain("new Function");
312
224
 
313
- var TEST_OUTPUT;
314
- eval(output);
225
+ var TEST_OUTPUT;
226
+ eval(output);
315
227
 
316
- expect(TEST_OUTPUT).toStrictEqual(true);
317
- });
228
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
318
229
  });
319
230
 
320
- describe("RGF with the 'all' mode", () => {
321
- it("should contain `new Function` in the output and work", async () => {
322
- var output = await JsConfuser.obfuscate(
323
- `
324
- function add(a,b){
325
- return a + b;
326
- }
327
-
328
- input(add(10, 5))
329
- `,
330
- {
331
- target: "node",
332
- rgf: "all",
231
+ test("Variant #9: Work with Flatten on any function", async () => {
232
+ var output = await JsConfuser.obfuscate(
233
+ `
234
+ var outsideCounter = 0;
235
+ var outsideFlag = false;
236
+
237
+ function incrementOutsideCounter(){
238
+ outsideCounter++;
239
+ }
240
+
241
+ function incrementTimes(times){
242
+ for( var i = 0; i < times; i++ ) {
243
+ incrementOutsideCounter();
333
244
  }
334
- );
245
+ if( outsideFlag ) {
246
+ TEST_OUTPUT = times === 1 && outsideCounter === 10 ? "Correct Value" : "Incorrect Value";
247
+ }
248
+ outsideFlag = true;
249
+ }
335
250
 
336
- expect(output).toContain("new Function");
337
- var value = "never_called";
338
- function input(valueIn) {
339
- value = valueIn;
251
+ incrementOutsideCounter();
252
+ incrementTimes(8);
253
+ incrementTimes(1);
254
+ `,
255
+ {
256
+ target: "node",
257
+ rgf: true,
258
+ flatten: true,
340
259
  }
260
+ );
341
261
 
342
- eval(output);
343
- expect(value).toStrictEqual(15);
344
- });
262
+ expect(output).toContain("new Function");
345
263
 
346
- it("should work with multiple functions", async () => {
347
- var output = await JsConfuser.obfuscate(
348
- `
349
- function add(a,b){
350
- return a + b;
351
- }
352
-
353
- function parse(str){
354
- return parseInt(str);
355
- }
356
-
357
- input(add(parse("20"), 5))
358
- `,
359
- {
360
- target: "node",
361
- rgf: "all",
362
- }
363
- );
264
+ var TEST_OUTPUT;
265
+ eval(output);
364
266
 
365
- expect(output).toContain("new Function");
366
- var value = "never_called";
367
- function input(valueIn) {
368
- value = valueIn;
267
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
268
+ });
269
+
270
+ test("Variant #10: Configurable by custom function option", async () => {
271
+ var functionNames: string[] = [];
272
+
273
+ var output = await JsConfuser(
274
+ `
275
+ "use strict";
276
+
277
+ // By checking strict-mode, we can check if the function was RGF or not
278
+ function rgfThisFunction(){
279
+ var isStrictMode = () => {
280
+ try {
281
+ undefined = true;
282
+ } catch (E) {
283
+ return true;
284
+ }
285
+ return false;
369
286
  }
370
287
 
371
- eval(output);
372
- expect(value).toStrictEqual(25);
373
- });
374
-
375
- it("should work with multiple, deeply-nested, functions", async () => {
376
- var output = await JsConfuser.obfuscate(
377
- `
378
-
379
- function getNumbers(){
380
- return [5, 10];
381
- }
382
-
383
- function multiply(x,y){
384
- return x*y;
385
- }
386
-
387
- function testFunction(){
388
- function add(x,y){
389
- return x+y;
288
+ return isStrictMode();
390
289
  }
391
290
 
392
- function testInnerFunction(){
393
- var numbers = getNumbers();
291
+ function doNotRgfThisFunction(){
292
+ var isStrictMode = () => {
293
+ try {
294
+ undefined = true;
295
+ } catch (E) {
296
+ return true;
297
+ }
298
+ return false;
299
+ }
394
300
 
395
- // 5*10 + 10 = 60
396
- return add(multiply(numbers[0], numbers[1]), numbers[1])
301
+ return isStrictMode();
397
302
  }
398
303
 
399
- input( testInnerFunction() );
400
- }
304
+ TEST_OUTPUT_1 = rgfThisFunction();
305
+ TEST_OUTPUT_2 = doNotRgfThisFunction();
306
+ `,
307
+ {
308
+ target: "node",
309
+ rgf: (name) => {
310
+ functionNames.push(name);
311
+ return name !== "doNotRgfThisFunction";
312
+ },
313
+ }
314
+ );
401
315
 
402
- testFunction();
403
- `,
404
- {
405
- target: "node",
406
- rgf: "all",
407
- }
408
- );
316
+ expect(functionNames).toStrictEqual([
317
+ "rgfThisFunction",
318
+ "doNotRgfThisFunction",
319
+ ]);
320
+ expect(output).toContain("new Function");
409
321
 
410
- expect(output).toContain("new Function");
411
- var value = "never_called";
412
- function input(valueIn) {
413
- value = valueIn;
414
- }
322
+ var TEST_OUTPUT_1;
323
+ var TEST_OUTPUT_2;
415
324
 
416
- eval(output);
417
- expect(value).toStrictEqual(60);
418
- });
325
+ eval(output);
326
+ expect(TEST_OUTPUT_1).toStrictEqual(false);
327
+ expect(TEST_OUTPUT_2).toStrictEqual(true);
419
328
  });