js-confuser 1.5.5 → 1.5.7

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.
@@ -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
 
@@ -61,9 +61,10 @@ export default class Minify extends Transform {
61
61
 
62
62
  body.forEach((stmt, i) => {
63
63
  if (
64
- stmt.type == "ReturnStatement" ||
65
- stmt.type == "BreakStatement" ||
66
- stmt.type == "ContinueStatement"
64
+ stmt.type === "ReturnStatement" ||
65
+ stmt.type === "BreakStatement" ||
66
+ stmt.type === "ContinueStatement" ||
67
+ stmt.type === "ThrowStatement"
67
68
  ) {
68
69
  if (earlyReturn > i + 1) {
69
70
  earlyReturn = i + 1;
@@ -156,7 +157,7 @@ export default class Minify extends Transform {
156
157
  ) {
157
158
  lastDec = x;
158
159
  } else {
159
- lastDec.declarations.push(...x.declarations);
160
+ lastDec.declarations.push(...clone(x.declarations));
160
161
  remove.unshift(i);
161
162
  }
162
163
  } else {
@@ -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() {
@@ -7,7 +7,10 @@ import Template from "../templates/template";
7
7
  import traverse, { walk } from "../traverse";
8
8
  import {
9
9
  ArrayExpression,
10
+ AssignmentExpression,
10
11
  CallExpression,
12
+ ConditionalExpression,
13
+ ExpressionStatement,
11
14
  FunctionExpression,
12
15
  Identifier,
13
16
  Literal,
@@ -27,7 +30,9 @@ import {
27
30
  isVarContext,
28
31
  isFunction,
29
32
  prepend,
33
+ getDefiningContext,
30
34
  } from "../util/insert";
35
+ import { getRandomString } from "../util/random";
31
36
  import Transform from "./transform";
32
37
 
33
38
  /**
@@ -70,6 +75,8 @@ export default class RGF extends Transform {
70
75
  }[] = [];
71
76
  var queue: Location[] = [];
72
77
  var names = new Map<string, number>();
78
+ var referenceSignatures: { [name: string]: string } = {};
79
+
73
80
  var definingNodes = new Map<string, Node>();
74
81
 
75
82
  walk(contextObject, contextParents, (object, parents) => {
@@ -81,12 +88,63 @@ export default class RGF extends Transform {
81
88
  !object.generator &&
82
89
  getVarContext(parents[0], parents.slice(1)) === contextObject
83
90
  ) {
91
+ // Discard getter/setter methods
92
+ if (parents[0].type === "Property" && parents[0].value === object) {
93
+ if (
94
+ parents[0].method ||
95
+ parents[0].kind === "get" ||
96
+ parents[0].kind === "set"
97
+ ) {
98
+ return;
99
+ }
100
+ }
101
+
102
+ // Discard class methods
103
+ if (
104
+ parents[0].type === "MethodDefinition" &&
105
+ parents[0].value === object
106
+ ) {
107
+ return;
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
+
84
132
  var defined = new Set<string>(),
85
133
  referenced = new Set<string>();
86
134
 
87
135
  var isBound = false;
88
136
 
89
- 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) => {
90
148
  if (
91
149
  o.type == "Identifier" &&
92
150
  !reservedIdentifiers.has(o.name) &&
@@ -96,7 +154,7 @@ export default class RGF extends Transform {
96
154
  if (!info.spec.isReferenced) {
97
155
  return;
98
156
  }
99
- if (info.spec.isDefined) {
157
+ if (info.spec.isDefined && getDefiningContext(o, p) === object) {
100
158
  defined.add(o.name);
101
159
  } else {
102
160
  referenced.add(o.name);
@@ -106,7 +164,10 @@ export default class RGF extends Transform {
106
164
  if (o.type == "ThisExpression" || o.type == "Super") {
107
165
  isBound = true;
108
166
  }
109
- });
167
+ };
168
+
169
+ walk(object.params, [object, ...parents], fnTraverser);
170
+ walk(object.body, [object, ...parents], fnTraverser);
110
171
 
111
172
  if (!isBound) {
112
173
  defined.forEach((identifier) => {
@@ -177,6 +238,8 @@ export default class RGF extends Transform {
177
238
  var index = names.size;
178
239
 
179
240
  names.set(object.id.name, index);
241
+ referenceSignatures[index] = getRandomString(10);
242
+
180
243
  definingNodes.set(object.id.name, object.id);
181
244
  }
182
245
  }
@@ -186,7 +249,8 @@ export default class RGF extends Transform {
186
249
  return;
187
250
  }
188
251
 
189
- var referenceArray = this.generateIdentifier();
252
+ // An array containing all the function declarations
253
+ var referenceArray = "_" + getRandomString(10);
190
254
 
191
255
  walk(contextObject, contextParents, (o, p) => {
192
256
  if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
@@ -204,27 +268,50 @@ export default class RGF extends Transform {
204
268
  if (pointingTo == shouldBe) {
205
269
  this.log(o.name, "->", `${referenceArray}[${index}]`);
206
270
 
207
- this.replace(
208
- o,
209
- FunctionExpression(
210
- [],
211
- [
212
- ReturnStatement(
213
- CallExpression(
214
- MemberExpression(
215
- Identifier(referenceArray),
216
- Literal(index),
217
- true
218
- ),
219
- [
271
+ var memberExpression = MemberExpression(
272
+ Identifier(referenceArray),
273
+ Literal(index),
274
+ true
275
+ );
276
+
277
+ // Allow re-assignment to the RGF function
278
+ if (
279
+ p[0] &&
280
+ p[0].type === "AssignmentExpression" &&
281
+ p[0].left === o
282
+ ) {
283
+ // fn = ...
284
+
285
+ this.replace(o, memberExpression);
286
+ } else {
287
+ // fn()
288
+ // fn
289
+
290
+ // In most cases the identifier is being used like this (call expression, or referenced to be called later)
291
+ // Replace it with a simple wrapper function that will pass on the reference array
292
+
293
+ var conditionalExpression = ConditionalExpression(
294
+ Template(
295
+ `typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
296
+ referenceSignatures[index] || "_"
297
+ }"]`
298
+ ).single().expression,
299
+ FunctionExpression(
300
+ [],
301
+ [
302
+ ReturnStatement(
303
+ CallExpression(memberExpression, [
220
304
  Identifier(referenceArray),
221
305
  SpreadElement(Identifier("arguments")),
222
- ]
223
- )
224
- ),
225
- ]
226
- )
227
- );
306
+ ])
307
+ ),
308
+ ]
309
+ ),
310
+ memberExpression
311
+ );
312
+
313
+ this.replace(o, conditionalExpression);
314
+ }
228
315
  }
229
316
  }
230
317
  }
@@ -243,6 +330,7 @@ export default class RGF extends Transform {
243
330
  var name = object?.id?.name;
244
331
  var hasName = !!name;
245
332
  var params = object.params.map((x) => x.name) || [];
333
+ var signature = referenceSignatures[names.get(name)];
246
334
 
247
335
  var embeddedName = name || this.getPlaceholder();
248
336
 
@@ -280,11 +368,11 @@ export default class RGF extends Transform {
280
368
  CallExpression(
281
369
  MemberExpression(
282
370
  Identifier(embeddedName),
283
- Identifier("call"),
284
- false
371
+ Literal("call"),
372
+ true
285
373
  ),
286
374
  [
287
- ThisExpression(),
375
+ Identifier("undefined"),
288
376
  SpreadElement(
289
377
  Template(
290
378
  `Array.prototype.slice.call(arguments, 1)`
@@ -322,8 +410,38 @@ export default class RGF extends Transform {
322
410
  Literal(toString),
323
411
  ]);
324
412
 
325
- if (hasName) {
326
- arrayExpression.elements[names.get(name)] = newFunction;
413
+ function applySignature(fn) {
414
+ if (!signature) {
415
+ return fn;
416
+ }
417
+
418
+ // This code marks the function object with a unique property
419
+ return CallExpression(
420
+ FunctionExpression(
421
+ [],
422
+ [
423
+ VariableDeclaration(VariableDeclarator("fn", fn)),
424
+ ExpressionStatement(
425
+ AssignmentExpression(
426
+ "=",
427
+ MemberExpression(
428
+ Identifier("fn"),
429
+ Literal(signature),
430
+ true
431
+ ),
432
+ Literal(true)
433
+ )
434
+ ),
435
+ ReturnStatement(Identifier("fn")),
436
+ ]
437
+ ),
438
+ []
439
+ );
440
+ }
441
+
442
+ if (object.type === "FunctionDeclaration") {
443
+ arrayExpression.elements[names.get(name)] =
444
+ applySignature(newFunction);
327
445
 
328
446
  if (Array.isArray(parents[0])) {
329
447
  parents[0].splice(parents[0].indexOf(object), 1);
@@ -336,7 +454,24 @@ export default class RGF extends Transform {
336
454
  );
337
455
  }
338
456
  } else {
339
- this.replace(object, newFunction);
457
+ // The wrapper function passes the reference array around
458
+ var wrapperFunction = FunctionExpression(
459
+ [],
460
+ [
461
+ ReturnStatement(
462
+ CallExpression(
463
+ MemberExpression(newFunction, Literal("call"), true),
464
+ [
465
+ Identifier("undefined"),
466
+ Identifier(referenceArray),
467
+ SpreadElement(Identifier("arguments")),
468
+ ]
469
+ )
470
+ ),
471
+ ]
472
+ );
473
+
474
+ this.replace(object, applySignature(wrapperFunction));
340
475
  }
341
476
  });
342
477
  };
@@ -25,34 +25,11 @@ export default class StringSplitting extends Transform {
25
25
  this.vars = [];
26
26
  }
27
27
 
28
- apply(tree) {
29
- super.apply(tree);
30
-
31
- if (this.vars.length) {
32
- shuffle(this.adders);
33
- shuffle(this.vars);
34
-
35
- var body: Node[] = tree.body;
36
-
37
- this.adders.forEach((nodes) => {
38
- nodes.forEach((x) => body.unshift(x));
39
- });
40
-
41
- var variableDeclaration = {
42
- type: "VariableDeclaration",
43
- declarations: [],
44
- kind: "var",
45
- };
46
- this.vars.forEach((node) => variableDeclaration.declarations.push(node));
47
-
48
- body.unshift(variableDeclaration);
49
- }
50
- }
51
-
52
28
  match(object: Node, parents: Node[]) {
53
29
  return (
54
30
  object.type == "Literal" &&
55
31
  typeof object.value === "string" &&
32
+ object.value.length >= 8 &&
56
33
  !isModuleSource(object, parents) &&
57
34
  !isDirective(object, parents)
58
35
  );
@@ -87,7 +64,6 @@ export default class StringSplitting extends Transform {
87
64
  var last = chunks.pop();
88
65
  chunks.forEach((chunk, i) => {
89
66
  if (i == 0) {
90
- ok(i == 0);
91
67
  parent = binaryExpression = BinaryExpression(
92
68
  "+",
93
69
  Literal(chunk),
@@ -80,3 +80,21 @@ test("Variant #4: Should work when countermeasures is variable declaration", asy
80
80
  }
81
81
  );
82
82
  });
83
+
84
+ // https://github.com/MichaelXF/js-confuser/issues/66
85
+ test("Variant #5: Should work with RGF enabled", async () => {
86
+ await JsConfuser.obfuscate(
87
+ `
88
+ function myCountermeasuresFunction(){
89
+
90
+ }
91
+ `,
92
+ {
93
+ target: "node",
94
+ lock: {
95
+ countermeasures: "myCountermeasuresFunction",
96
+ },
97
+ rgf: true,
98
+ }
99
+ );
100
+ });