js-confuser 1.5.6 → 1.5.8

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 (29) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/options.js +4 -4
  3. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +16 -2
  4. package/dist/transforms/identifier/nameRecycling.js +8 -2
  5. package/dist/transforms/identifier/renameVariables.js +9 -0
  6. package/dist/transforms/lock/antiDebug.js +1 -1
  7. package/dist/transforms/lock/integrity.js +6 -2
  8. package/dist/transforms/lock/lock.js +40 -32
  9. package/dist/transforms/preparation/preparation.js +0 -7
  10. package/dist/transforms/rgf.js +32 -3
  11. package/dist/transforms/string/stringConcealing.js +77 -40
  12. package/dist/transforms/transform.js +1 -1
  13. package/package.json +2 -2
  14. package/src/options.ts +10 -4
  15. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +16 -1
  16. package/src/transforms/identifier/nameRecycling.ts +14 -3
  17. package/src/transforms/identifier/renameVariables.ts +19 -0
  18. package/src/transforms/lock/antiDebug.ts +1 -1
  19. package/src/transforms/lock/integrity.ts +13 -1
  20. package/src/transforms/lock/lock.ts +81 -44
  21. package/src/transforms/preparation/preparation.ts +2 -21
  22. package/src/transforms/rgf.ts +39 -3
  23. package/src/transforms/string/stringConcealing.ts +120 -56
  24. package/src/transforms/transform.ts +1 -1
  25. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +36 -0
  26. package/test/transforms/identifier/nameRecycling.test.ts +39 -0
  27. package/test/transforms/identifier/renameVariables.test.ts +38 -0
  28. package/test/transforms/lock/countermeasures.test.ts +18 -0
  29. package/test/transforms/rgf.test.ts +37 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-confuser",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "description": "JavaScript Obfuscation Tool.",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "author": "MichaelXF",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "acorn": "^8.7.0",
25
+ "acorn": "^8.8.2",
26
26
  "escodegen": "^2.0.0"
27
27
  },
28
28
  "devDependencies": {
package/src/options.ts CHANGED
@@ -770,7 +770,10 @@ export function validateOptions(options: ObfuscateOptions) {
770
770
 
771
771
  if (options.lock) {
772
772
  // Validate browser-lock option
773
- if (typeof options.lock.browserLock !== "undefined") {
773
+ if (
774
+ options.lock.browserLock &&
775
+ typeof options.lock.browserLock !== "undefined"
776
+ ) {
774
777
  ok(
775
778
  Array.isArray(options.lock.browserLock),
776
779
  "browserLock must be an array"
@@ -783,7 +786,7 @@ export function validateOptions(options: ObfuscateOptions) {
783
786
  );
784
787
  }
785
788
  // Validate os-lock option
786
- if (typeof options.lock.osLock !== "undefined") {
789
+ if (options.lock.osLock && typeof options.lock.osLock !== "undefined") {
787
790
  ok(Array.isArray(options.lock.osLock), "osLock must be an array");
788
791
  ok(
789
792
  !options.lock.osLock.find((osName) => !validOses.has(osName)),
@@ -791,12 +794,15 @@ export function validateOptions(options: ObfuscateOptions) {
791
794
  );
792
795
  }
793
796
  // Validate domain-lock option
794
- if (typeof options.lock.domainLock !== "undefined") {
797
+ if (
798
+ options.lock.domainLock &&
799
+ typeof options.lock.domainLock !== "undefined"
800
+ ) {
795
801
  ok(Array.isArray(options.lock.domainLock), "domainLock must be an array");
796
802
  }
797
803
 
798
804
  // Validate context option
799
- if (typeof options.lock.context !== "undefined") {
805
+ if (options.lock.context && typeof options.lock.context !== "undefined") {
800
806
  ok(Array.isArray(options.lock.context), "context must be an array");
801
807
  }
802
808
 
@@ -47,6 +47,7 @@ import ChoiceFlowObfuscation from "./choiceFlowObfuscation";
47
47
  import ControlFlowObfuscation from "./controlFlowObfuscation";
48
48
  import ExpressionObfuscation from "./expressionObfuscation";
49
49
  import SwitchCaseObfuscation from "./switchCaseObfuscation";
50
+ import { isModuleSource } from "../string/stringConcealing";
50
51
 
51
52
  var flattenStructures = new Set([
52
53
  "IfStatement",
@@ -226,6 +227,13 @@ export default class ControlFlowFlattening extends Transform {
226
227
  fnNames.delete(illegal);
227
228
  });
228
229
 
230
+ var importDeclarations = [];
231
+ for (var stmt of body) {
232
+ if (stmt.type === "ImportDeclaration") {
233
+ importDeclarations.push(stmt);
234
+ }
235
+ }
236
+
229
237
  var fraction = 0.9;
230
238
  if (body.length > 20) {
231
239
  fraction /= Math.max(1.2, body.length - 18);
@@ -285,6 +293,7 @@ export default class ControlFlowFlattening extends Transform {
285
293
  if (
286
294
  o.type == "Literal" &&
287
295
  typeof o.value == "string" &&
296
+ !isModuleSource(o, p) &&
288
297
  !o.regex &&
289
298
  Math.random() / (Object.keys(stringBank).length / 2 + 1) > 0.5
290
299
  ) {
@@ -318,7 +327,10 @@ export default class ControlFlowFlattening extends Transform {
318
327
  };
319
328
 
320
329
  body.forEach((stmt, i) => {
321
- if (functionDeclarations.has(stmt)) {
330
+ if (
331
+ functionDeclarations.has(stmt) ||
332
+ stmt.type === "ImportDeclaration"
333
+ ) {
322
334
  return;
323
335
  }
324
336
 
@@ -1045,6 +1057,9 @@ export default class ControlFlowFlattening extends Transform {
1045
1057
  var discriminant = Template(`${stateVars.join("+")}`).single().expression;
1046
1058
 
1047
1059
  body.length = 0;
1060
+ for (var importDeclaration of importDeclarations) {
1061
+ body.push(importDeclaration);
1062
+ }
1048
1063
 
1049
1064
  if (functionDeclarations.size) {
1050
1065
  functionDeclarations.forEach((x) => {
@@ -97,8 +97,6 @@ export default class NameRecycling extends Transform {
97
97
  return;
98
98
  }
99
99
 
100
- lastReferenceMap.set(o.name, i);
101
-
102
100
  var comparingContext = info.spec.isDefined
103
101
  ? getDefiningContext(o, p)
104
102
  : getReferencingContexts(o, p).find((x) => isVarContext(x));
@@ -114,7 +112,18 @@ export default class NameRecycling extends Transform {
114
112
  }
115
113
 
116
114
  if (info.spec.isDefined) {
117
- if (defined.has(o.name) || getBlock(o, p) !== object) {
115
+ // Function Declarations can be used before they're defined, if so, don't change this
116
+ if (
117
+ info.isFunctionDeclaration &&
118
+ lastReferenceMap.has(o.name)
119
+ ) {
120
+ illegal.add(o.name);
121
+ }
122
+ if (
123
+ defined.has(o.name) ||
124
+ getBlock(o, p) !== object ||
125
+ info.isImportSpecifier
126
+ ) {
118
127
  illegal.add(o.name);
119
128
  }
120
129
  defined.add(o.name);
@@ -123,6 +132,8 @@ export default class NameRecycling extends Transform {
123
132
  referencedHere.add(o.name);
124
133
  }
125
134
  }
135
+
136
+ lastReferenceMap.set(o.name, i);
126
137
  };
127
138
  }
128
139
  });
@@ -10,6 +10,7 @@ import {
10
10
  isContext,
11
11
  isLexContext,
12
12
  getDefiningContext,
13
+ clone,
13
14
  } from "../../util/insert";
14
15
  import { isValidIdentifier } from "../../util/compare";
15
16
  import Transform from "../transform";
@@ -185,6 +186,24 @@ export default class RenameVariables extends Transform {
185
186
  return;
186
187
  }
187
188
 
189
+ // Strange behavior where the `local` and `imported` objects are the same
190
+ if (info.isImportSpecifier) {
191
+ var importSpecifierIndex = p.findIndex(
192
+ (x) => x.type === "ImportSpecifier"
193
+ );
194
+ if (
195
+ importSpecifierIndex != -1 &&
196
+ p[importSpecifierIndex].imported ===
197
+ (p[importSpecifierIndex - 1] || o) &&
198
+ p[importSpecifierIndex].imported &&
199
+ p[importSpecifierIndex].imported.type === "Identifier"
200
+ ) {
201
+ p[importSpecifierIndex].imported = clone(
202
+ p[importSpecifierIndex - 1] || o
203
+ );
204
+ }
205
+ }
206
+
188
207
  // console.log(o.name, "->", newName);
189
208
  o.name = newName;
190
209
  o.$renamed = true;
@@ -62,7 +62,7 @@ export default class AntiDebug extends Transform {
62
62
  IfStatement(
63
63
  Identifier(isDevName),
64
64
  this.options.lock.countermeasures
65
- ? this.lock.getCounterMeasuresCode()
65
+ ? this.lock.getCounterMeasuresCode(tree.body, [tree])
66
66
  : [
67
67
  WhileStatement(Identifier(isDevName), [
68
68
  ExpressionStatement(
@@ -195,7 +195,10 @@ export default class Integrity extends Transform {
195
195
  }
196
196
 
197
197
  return () => {
198
- object.__hiddenCountermeasures = this.lock.getCounterMeasuresCode();
198
+ object.__hiddenCountermeasures = this.lock.getCounterMeasuresCode(
199
+ object,
200
+ parents
201
+ );
199
202
 
200
203
  object.$eval = () => {
201
204
  var functionName = this.generateIdentifier();
@@ -258,6 +261,15 @@ export default class Integrity extends Transform {
258
261
  ifStatement,
259
262
  ]);
260
263
 
264
+ // Make sure the countermeasures activation variable is present
265
+ if (this.lock.counterMeasuresActivated) {
266
+ object.body.body.unshift(
267
+ VariableDeclaration(
268
+ VariableDeclarator(this.lock.counterMeasuresActivated)
269
+ )
270
+ );
271
+ }
272
+
261
273
  if (object.type == "ArrowFunctionExpression") {
262
274
  object.type = "FunctionExpression";
263
275
  object.expression = false;
@@ -11,17 +11,12 @@ import {
11
11
  Literal,
12
12
  UnaryExpression,
13
13
  NewExpression,
14
- FunctionDeclaration,
15
- ReturnStatement,
16
14
  VariableDeclaration,
17
- ObjectExpression,
18
- Property,
19
- ArrayExpression,
20
- FunctionExpression,
21
15
  ThisExpression,
22
16
  VariableDeclarator,
23
17
  Location,
24
18
  LogicalExpression,
19
+ SequenceExpression,
25
20
  } from "../../util/gen";
26
21
  import traverse, { getBlock, isBlock } from "../../traverse";
27
22
  import { choice, getRandomInteger } from "../../util/random";
@@ -47,6 +42,12 @@ export default class Lock extends Transform {
47
42
  counterMeasuresNode: Location;
48
43
  iosDetectFn: string;
49
44
 
45
+ /**
46
+ * This is a boolean variable injected into the source code determining wether the countermeasures function has been called.
47
+ * This is used to prevent infinite loops from happening
48
+ */
49
+ counterMeasuresActivated: string;
50
+
50
51
  made: number;
51
52
 
52
53
  constructor(o) {
@@ -73,36 +74,32 @@ export default class Lock extends Transform {
73
74
  typeof this.options.lock.countermeasures === "string" &&
74
75
  isValidIdentifier(this.options.lock.countermeasures)
75
76
  ) {
76
- var defined = new Set<string>();
77
77
  traverse(tree, (object, parents) => {
78
- if (object.type == "Identifier") {
78
+ if (
79
+ object.type == "Identifier" &&
80
+ object.name === this.options.lock.countermeasures
81
+ ) {
79
82
  var info = getIdentifierInfo(object, parents);
80
83
  if (info.spec.isDefined) {
81
- defined.add(object.name);
82
- if (object.name === this.options.lock.countermeasures) {
83
- if (this.counterMeasuresNode) {
84
+ if (this.counterMeasuresNode) {
85
+ throw new Error(
86
+ "Countermeasures function was already defined, it must have a unique name from the rest of your code"
87
+ );
88
+ } else {
89
+ var definingContext = getVarContext(parents[0], parents.slice(1));
90
+ if (definingContext != tree) {
84
91
  throw new Error(
85
- "Countermeasures function was already defined, it must have a unique name from the rest of your code"
86
- );
87
- } else {
88
- var definingContext = getVarContext(
89
- parents[0],
90
- parents.slice(1)
92
+ "Countermeasures function must be defined at the global level"
91
93
  );
92
- if (definingContext != tree) {
93
- throw new Error(
94
- "Countermeasures function must be defined at the global level"
95
- );
96
- }
97
- var chain: Location = [object, parents];
98
- if (info.isFunctionDeclaration) {
99
- chain = [parents[0], parents.slice(1)];
100
- } else if (info.isVariableDeclaration) {
101
- chain = [parents[1], parents.slice(2)];
102
- }
103
-
104
- this.counterMeasuresNode = chain;
105
94
  }
95
+ var chain: Location = [object, parents];
96
+ if (info.isFunctionDeclaration) {
97
+ chain = [parents[0], parents.slice(1)];
98
+ } else if (info.isVariableDeclaration) {
99
+ chain = [parents[1], parents.slice(2)];
100
+ }
101
+
102
+ this.counterMeasuresNode = chain;
106
103
  }
107
104
  }
108
105
  }
@@ -120,7 +117,7 @@ export default class Lock extends Transform {
120
117
  super.apply(tree);
121
118
  }
122
119
 
123
- getCounterMeasuresCode(): Node[] {
120
+ getCounterMeasuresCode(object: Node, parents: Node[]): Node[] {
124
121
  var opt = this.options.lock.countermeasures;
125
122
 
126
123
  if (opt === false) {
@@ -129,10 +126,30 @@ export default class Lock extends Transform {
129
126
 
130
127
  // Call function
131
128
  if (typeof opt === "string") {
129
+ if (!this.counterMeasuresActivated) {
130
+ this.counterMeasuresActivated = this.getPlaceholder();
131
+
132
+ prepend(
133
+ parents[parents.length - 1] || object,
134
+ VariableDeclaration(VariableDeclarator(this.counterMeasuresActivated))
135
+ );
136
+ }
137
+
132
138
  // Since Lock occurs before variable renaming, we are using the pre-obfuscated function name
133
139
  return [
134
140
  ExpressionStatement(
135
- CallExpression(Template(opt).single().expression, [])
141
+ LogicalExpression(
142
+ "||",
143
+ Identifier(this.counterMeasuresActivated),
144
+ SequenceExpression([
145
+ AssignmentExpression(
146
+ "=",
147
+ Identifier(this.counterMeasuresActivated),
148
+ Literal(true)
149
+ ),
150
+ CallExpression(Template(opt).single().expression, []),
151
+ ])
152
+ )
136
153
  ),
137
154
  ];
138
155
  }
@@ -288,7 +305,7 @@ export default class Lock extends Transform {
288
305
  nodes.push(
289
306
  IfStatement(
290
307
  callExpression,
291
- this.getCounterMeasuresCode() || [],
308
+ this.getCounterMeasuresCode(object, parents) || [],
292
309
  null
293
310
  )
294
311
  );
@@ -324,7 +341,11 @@ export default class Lock extends Transform {
324
341
  }
325
342
 
326
343
  nodes.push(
327
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
344
+ IfStatement(
345
+ test,
346
+ this.getCounterMeasuresCode(object, parents) || [],
347
+ null
348
+ )
328
349
  );
329
350
  }
330
351
 
@@ -338,7 +359,11 @@ export default class Lock extends Transform {
338
359
  );
339
360
 
340
361
  nodes.push(
341
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
362
+ IfStatement(
363
+ test,
364
+ this.getCounterMeasuresCode(object, parents) || [],
365
+ null
366
+ )
342
367
  );
343
368
 
344
369
  break;
@@ -351,7 +376,11 @@ export default class Lock extends Transform {
351
376
  );
352
377
 
353
378
  nodes.push(
354
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
379
+ IfStatement(
380
+ test,
381
+ this.getCounterMeasuresCode(object, parents) || [],
382
+ null
383
+ )
355
384
  );
356
385
 
357
386
  break;
@@ -359,6 +388,8 @@ export default class Lock extends Transform {
359
388
  case "context":
360
389
  var prop = choice(this.options.lock.context);
361
390
 
391
+ var code = this.getCounterMeasuresCode(object, parents) || [];
392
+
362
393
  // Todo: Alternative to `this`
363
394
  if (!this.globalVar) {
364
395
  offset = 1;
@@ -384,9 +415,7 @@ export default class Lock extends Transform {
384
415
  "!",
385
416
  MemberExpression(Identifier(this.globalVar), Literal(prop), true)
386
417
  );
387
- nodes.push(
388
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
389
- );
418
+ nodes.push(IfStatement(test, code, null));
390
419
 
391
420
  break;
392
421
 
@@ -397,6 +426,8 @@ export default class Lock extends Transform {
397
426
 
398
427
  ok(this.options.lock.osLock);
399
428
 
429
+ var code = this.getCounterMeasuresCode(object, parents) || [];
430
+
400
431
  this.options.lock.osLock.forEach((osName) => {
401
432
  var agentMatcher = {
402
433
  windows: "Win",
@@ -449,9 +480,7 @@ export default class Lock extends Transform {
449
480
  });
450
481
 
451
482
  test = UnaryExpression("!", { ...test });
452
- nodes.push(
453
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
454
- );
483
+ nodes.push(IfStatement(test, code, null));
455
484
  break;
456
485
 
457
486
  case "browserLock":
@@ -488,7 +517,11 @@ export default class Lock extends Transform {
488
517
 
489
518
  test = UnaryExpression("!", { ...test });
490
519
  nodes.push(
491
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
520
+ IfStatement(
521
+ test,
522
+ this.getCounterMeasuresCode(object, parents) || [],
523
+ null
524
+ )
492
525
  );
493
526
  break;
494
527
 
@@ -540,7 +573,11 @@ export default class Lock extends Transform {
540
573
  );
541
574
  }
542
575
  nodes.push(
543
- IfStatement(test, this.getCounterMeasuresCode() || [], null)
576
+ IfStatement(
577
+ test,
578
+ this.getCounterMeasuresCode(object, parents) || [],
579
+ null
580
+ )
544
581
  );
545
582
  }
546
583
 
@@ -3,24 +3,11 @@
3
3
  */
4
4
  import Transform from "../transform";
5
5
 
6
- import {
7
- BlockStatement,
8
- Identifier,
9
- LabeledStatement,
10
- Literal,
11
- Location,
12
- Node,
13
- ReturnStatement,
14
- } from "../../util/gen";
6
+ import { BlockStatement, Literal, ReturnStatement } from "../../util/gen";
15
7
  import { ObfuscateOrder } from "../../order";
16
- import { getIndexDirect, clone, getFunction } from "../../util/insert";
17
- import { ok } from "assert";
8
+ import { clone, getFunction } from "../../util/insert";
18
9
  import { getIdentifierInfo } from "../../util/identifiers";
19
- import { walk } from "../../traverse";
20
10
  import Label from "../label";
21
- import NameConflicts from "./nameConflicts";
22
- import AntiDestructuring from "../es5/antiDestructuring";
23
- import { OPERATOR_PRECEDENCE } from "../../precedence";
24
11
  import { isLoop } from "../../util/compare";
25
12
 
26
13
  /**
@@ -181,12 +168,6 @@ export default class Preparation extends Transform {
181
168
  this.before.push(new Label(o));
182
169
  this.before.push(new ExplicitIdentifiers(o));
183
170
  this.before.push(new ExplicitDeclarations(o));
184
-
185
- if (this.options.es5) {
186
- this.before.push(new AntiDestructuring(o));
187
- }
188
-
189
- // this.before.push(new NameConflicts(o));
190
171
  }
191
172
 
192
173
  match() {
@@ -30,6 +30,7 @@ import {
30
30
  isVarContext,
31
31
  isFunction,
32
32
  prepend,
33
+ getDefiningContext,
33
34
  } from "../util/insert";
34
35
  import { getRandomString } from "../util/random";
35
36
  import Transform from "./transform";
@@ -106,12 +107,44 @@ export default class RGF extends Transform {
106
107
  return;
107
108
  }
108
109
 
110
+ // Avoid applying to the countermeasures function
111
+ if (typeof this.options.lock?.countermeasures === "string") {
112
+ // function countermeasures(){...}
113
+ if (
114
+ object.type === "FunctionDeclaration" &&
115
+ object.id.type === "Identifier" &&
116
+ object.id.name === this.options.lock.countermeasures
117
+ ) {
118
+ return;
119
+ }
120
+
121
+ // var countermeasures = function(){...}
122
+ if (
123
+ parents[0].type === "VariableDeclarator" &&
124
+ parents[0].init === object &&
125
+ parents[0].id.type === "Identifier" &&
126
+ parents[0].id.name === this.options.lock.countermeasures
127
+ ) {
128
+ return;
129
+ }
130
+ }
131
+
109
132
  var defined = new Set<string>(),
110
133
  referenced = new Set<string>();
111
134
 
112
135
  var isBound = false;
113
136
 
114
- walk(object.body, [object, ...parents], (o, p) => {
137
+ /**
138
+ * The fnTraverses serves two important purposes
139
+ *
140
+ * - Identify all the variables referenced and defined here
141
+ * - Identify is the 'this' keyword is used anywhere
142
+ *
143
+ * @param o
144
+ * @param p
145
+ * @returns
146
+ */
147
+ const fnTraverser = (o, p) => {
115
148
  if (
116
149
  o.type == "Identifier" &&
117
150
  !reservedIdentifiers.has(o.name) &&
@@ -121,7 +154,7 @@ export default class RGF extends Transform {
121
154
  if (!info.spec.isReferenced) {
122
155
  return;
123
156
  }
124
- if (info.spec.isDefined) {
157
+ if (info.spec.isDefined && getDefiningContext(o, p) === object) {
125
158
  defined.add(o.name);
126
159
  } else {
127
160
  referenced.add(o.name);
@@ -131,7 +164,10 @@ export default class RGF extends Transform {
131
164
  if (o.type == "ThisExpression" || o.type == "Super") {
132
165
  isBound = true;
133
166
  }
134
- });
167
+ };
168
+
169
+ walk(object.params, [object, ...parents], fnTraverser);
170
+ walk(object.body, [object, ...parents], fnTraverser);
135
171
 
136
172
  if (!isBound) {
137
173
  defined.forEach((identifier) => {