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
@@ -3,37 +3,23 @@ import { reservedIdentifiers } from "../constants";
3
3
  import Obfuscator from "../obfuscator";
4
4
  import { ObfuscateOrder } from "../order";
5
5
  import { ComputeProbabilityMap } from "../probability";
6
- import Template from "../templates/template";
7
- import traverse, { walk } from "../traverse";
6
+ import { walk } from "../traverse";
8
7
  import {
9
8
  ArrayExpression,
10
- AssignmentExpression,
9
+ BlockStatement,
11
10
  CallExpression,
12
- ConditionalExpression,
13
- ExpressionStatement,
14
- FunctionExpression,
15
11
  Identifier,
16
12
  Literal,
17
- Location,
18
13
  MemberExpression,
19
14
  NewExpression,
20
15
  Node,
21
16
  ReturnStatement,
22
- SpreadElement,
23
17
  ThisExpression,
24
18
  VariableDeclaration,
25
19
  VariableDeclarator,
26
20
  } from "../util/gen";
27
- import { getDefiningIdentifier, getIdentifierInfo } from "../util/identifiers";
28
- import {
29
- getVarContext,
30
- isVarContext,
31
- isFunction,
32
- prepend,
33
- getDefiningContext,
34
- clone,
35
- } from "../util/insert";
36
- import { getRandomString } from "../util/random";
21
+ import { getIdentifierInfo } from "../util/identifiers";
22
+ import { prepend, getDefiningContext } from "../util/insert";
37
23
  import Transform from "./transform";
38
24
 
39
25
  /**
@@ -44,437 +30,259 @@ import Transform from "./transform";
44
30
  * Rigorous checks are in place to only include pure functions.
45
31
  *
46
32
  * `flatten` can attempt to make function reference-less. Recommended to have flatten enabled with RGF.
47
- *
48
- * | Mode | Description |
49
- * | --- | --- |
50
- * | `"all"` | Applies to all scopes |
51
- * | `true` | Applies to the top level only |
52
- * | `false` | Feature disabled |
53
33
  */
54
34
  export default class RGF extends Transform {
35
+ // Array of all the `new Function` calls
36
+ arrayExpressionElements: Node[];
37
+ // The name of the array holding all the `new Function` expressions
38
+ arrayExpressionName: string;
39
+
55
40
  constructor(o) {
56
41
  super(o, ObfuscateOrder.RGF);
42
+
43
+ this.arrayExpressionName = this.getPlaceholder() + "_rgf";
44
+ this.arrayExpressionElements = [];
45
+ }
46
+
47
+ apply(tree: Node): void {
48
+ super.apply(tree);
49
+
50
+ // Only add the array if there were converted functions
51
+ if (this.arrayExpressionElements.length > 0) {
52
+ prepend(
53
+ tree,
54
+ VariableDeclaration(
55
+ VariableDeclarator(
56
+ Identifier(this.arrayExpressionName),
57
+ ArrayExpression(this.arrayExpressionElements)
58
+ )
59
+ )
60
+ );
61
+ }
57
62
  }
58
63
 
59
64
  match(object, parents) {
60
- return isVarContext(object) && object.type !== "ArrowFunctionExpression";
65
+ return (
66
+ (object.type === "FunctionDeclaration" ||
67
+ object.type === "FunctionExpression") && // Does not apply to Arrow functions
68
+ !object.async && // Does not apply to async/generator functions
69
+ !object.generator
70
+ );
61
71
  }
62
72
 
63
- transform(contextObject, contextParents) {
64
- return () => {
65
- var isGlobal = contextObject.type == "Program";
73
+ transform(object: Node, parents: Node[]) {
74
+ // Discard getter/setter methods
75
+ if (parents[0].type === "Property" && parents[0].value === object) {
76
+ if (
77
+ parents[0].method ||
78
+ parents[0].kind === "get" ||
79
+ parents[0].kind === "set"
80
+ ) {
81
+ return;
82
+ }
83
+ }
84
+
85
+ // Discard class methods
86
+ if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
87
+ return;
88
+ }
89
+
90
+ // Avoid applying to the countermeasures function
91
+ if (typeof this.options.lock?.countermeasures === "string") {
92
+ // function countermeasures(){...}
93
+ if (
94
+ object.type === "FunctionDeclaration" &&
95
+ object.id.type === "Identifier" &&
96
+ object.id.name === this.options.lock.countermeasures
97
+ ) {
98
+ return;
99
+ }
66
100
 
67
- var value = ComputeProbabilityMap(this.options.rgf, (x) => x, isGlobal);
68
- if (value !== "all" && !isGlobal) {
101
+ // var countermeasures = function(){...}
102
+ if (
103
+ parents[0].type === "VariableDeclarator" &&
104
+ parents[0].init === object &&
105
+ parents[0].id.type === "Identifier" &&
106
+ parents[0].id.name === this.options.lock.countermeasures
107
+ ) {
69
108
  return;
70
109
  }
110
+ }
111
+
112
+ // Check user option
113
+ if (!ComputeProbabilityMap(this.options.rgf, (x) => x, object?.id?.name))
114
+ return;
115
+
116
+ // Discard functions that use 'eval' function
117
+ if (object.$requiresEval) return;
118
+
119
+ // Check for 'this', 'arguments' (not allowed!)
120
+ var isIllegal = false;
121
+ walk(object, parents, (o, p) => {
122
+ if (
123
+ o.type === "ThisExpression" ||
124
+ o.type === "Super" ||
125
+ (o.type === "Identifier" && o.name === "arguments")
126
+ ) {
127
+ isIllegal = true;
128
+ return "EXIT";
129
+ }
130
+ });
71
131
 
72
- var collect: {
73
- location: Location;
74
- references: Set<string>;
75
- name?: string;
76
- }[] = [];
77
- var queue: Location[] = [];
78
- var names = new Map<string, number>();
79
- var referenceSignatures: { [name: string]: string } = {};
132
+ if (isIllegal) return;
80
133
 
81
- var definingNodes = new Map<string, Node>();
134
+ return () => {
135
+ // Make sure function is 'reference-less'
136
+ var definedMap = new Map<Node, Set<string>>();
137
+ var isReferenceLess = true;
138
+ var identifierPreventingTransformation: string;
82
139
 
83
- walk(contextObject, contextParents, (object, parents) => {
140
+ walk(object, parents, (o, p) => {
84
141
  if (
85
- object !== contextObject &&
86
- isFunction(object) &&
87
- !object.$requiresEval &&
88
- !object.async &&
89
- !object.generator &&
90
- getVarContext(parents[0], parents.slice(1)) === contextObject
142
+ o.type === "Identifier" &&
143
+ !reservedIdentifiers.has(o.name) &&
144
+ !this.options.globalVariables.has(o.name)
91
145
  ) {
92
- // Discard getter/setter methods
93
- if (parents[0].type === "Property" && parents[0].value === object) {
94
- if (
95
- parents[0].method ||
96
- parents[0].kind === "get" ||
97
- parents[0].kind === "set"
98
- ) {
99
- return;
100
- }
101
- }
102
-
103
- // Discard class methods
104
- if (
105
- parents[0].type === "MethodDefinition" &&
106
- parents[0].value === object
107
- ) {
146
+ var info = getIdentifierInfo(o, p);
147
+ if (!info.spec.isReferenced) {
108
148
  return;
109
149
  }
110
150
 
111
- // Avoid applying to the countermeasures function
112
- if (typeof this.options.lock?.countermeasures === "string") {
113
- // function countermeasures(){...}
114
- if (
115
- object.type === "FunctionDeclaration" &&
116
- object.id.type === "Identifier" &&
117
- object.id.name === this.options.lock.countermeasures
118
- ) {
119
- return;
120
- }
151
+ if (info.spec.isDefined) {
152
+ // Add to defined map
153
+ var definingContext = getDefiningContext(o, p);
121
154
 
122
- // var countermeasures = function(){...}
123
- if (
124
- parents[0].type === "VariableDeclarator" &&
125
- parents[0].init === object &&
126
- parents[0].id.type === "Identifier" &&
127
- parents[0].id.name === this.options.lock.countermeasures
128
- ) {
129
- return;
155
+ if (!definedMap.has(definingContext)) {
156
+ definedMap.set(definingContext, new Set([o.name]));
157
+ } else {
158
+ definedMap.get(definingContext).add(o.name);
130
159
  }
131
- }
132
-
133
- var defined = new Set<string>(),
134
- referenced = new Set<string>();
135
-
136
- var isBound = false;
137
-
138
- /**
139
- * The fnTraverses serves two important purposes
140
- *
141
- * - Identify all the variables referenced and defined here
142
- * - Identify is the 'this' keyword is used anywhere
143
- *
144
- * @param o
145
- * @param p
146
- * @returns
147
- */
148
- const fnTraverser = (o, p) => {
149
- if (
150
- o.type == "Identifier" &&
151
- !reservedIdentifiers.has(o.name) &&
152
- !this.options.globalVariables.has(o.name)
153
- ) {
154
- var info = getIdentifierInfo(o, p);
155
- if (!info.spec.isReferenced) {
156
- return;
157
- }
158
- if (info.spec.isDefined && getDefiningContext(o, p) === object) {
159
- defined.add(o.name);
160
- } else {
161
- referenced.add(o.name);
160
+ } else {
161
+ // This approach is dirty and does not account for hoisted FunctionDeclarations
162
+ var isDefinedAbove = false;
163
+ for (var pNode of p) {
164
+ if (definedMap.has(pNode)) {
165
+ if (definedMap.get(pNode).has(o.name)) {
166
+ isDefinedAbove = true;
167
+ break;
168
+ }
162
169
  }
163
170
  }
164
171
 
165
- if (o.type == "ThisExpression" || o.type == "Super") {
166
- isBound = true;
167
- }
168
- };
169
-
170
- walk(object.params, [object, ...parents], fnTraverser);
171
- walk(object.body, [object, ...parents], fnTraverser);
172
-
173
- if (!isBound) {
174
- defined.forEach((identifier) => {
175
- referenced.delete(identifier);
176
- });
177
-
178
- object.params.forEach((param) => {
179
- referenced.delete(param.name);
180
- });
181
-
182
- collect.push({
183
- location: [object, parents],
184
- references: referenced,
185
- name: object.id?.name,
186
- });
187
- }
188
- }
189
- });
190
-
191
- if (!collect.length) {
192
- return;
193
- }
194
-
195
- var miss = 0;
196
- var start = collect.length * 2;
197
-
198
- while (true) {
199
- var hit = false;
200
-
201
- collect.forEach(
202
- ({ name, references: references1, location: location1 }) => {
203
- if (!references1.size && name) {
204
- collect.forEach((o) => {
205
- if (
206
- o.location[0] !== location1[0] &&
207
- o.references.size &&
208
- o.references.delete(name)
209
- ) {
210
- // console.log(collect);
172
+ if (!isDefinedAbove) {
173
+ isReferenceLess = false;
174
+ identifierPreventingTransformation = o.name;
211
175
 
212
- hit = true;
213
- }
214
- });
176
+ return "EXIT";
215
177
  }
216
178
  }
217
- );
218
- if (hit) {
219
- miss = 0;
220
- } else {
221
- miss++;
222
179
  }
180
+ });
223
181
 
224
- if (miss > start) {
225
- break;
182
+ // This function is not 'reference-less', cannot be RGF'd
183
+ if (!isReferenceLess) {
184
+ if (object.id) {
185
+ this.log(
186
+ `${object?.id?.name}() cannot be transformed because of ${identifierPreventingTransformation}`
187
+ );
226
188
  }
189
+ return;
227
190
  }
228
191
 
229
- queue = [];
230
- collect.forEach((o) => {
231
- if (!o.references.size) {
232
- var [object, parents] = o.location;
233
-
234
- queue.push([object, parents]);
235
- if (
236
- object.type == "FunctionDeclaration" &&
237
- typeof object.id.name === "string"
238
- ) {
239
- var index = names.size;
240
-
241
- names.set(object.id.name, index);
242
- referenceSignatures[index] = getRandomString(10);
243
-
244
- definingNodes.set(object.id.name, object.id);
245
- }
246
- }
192
+ // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
193
+ // RGF runs early and needs completed code before converting to a string.
194
+ // (^ the variables haven't been renamed yet)
195
+ var obfuscator = new Obfuscator({
196
+ ...this.options,
197
+ stringEncoding: false,
198
+ compact: true,
247
199
  });
248
200
 
249
- if (!queue.length) {
250
- return;
201
+ if (obfuscator.options.lock) {
202
+ delete obfuscator.options.lock.countermeasures;
251
203
  }
252
204
 
253
- // An array containing all the function declarations
254
- var referenceArray = "_" + getRandomString(10);
255
-
256
- walk(contextObject, contextParents, (o, p) => {
257
- if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
258
- var index = names.get(o.name);
259
- if (typeof index === "number") {
260
- var info = getIdentifierInfo(o, p);
261
- if (info.spec.isReferenced && !info.spec.isDefined) {
262
- var location = getDefiningIdentifier(o, p);
263
- if (location) {
264
- var pointingTo = location[0];
265
- var shouldBe = definingNodes.get(o.name);
266
-
267
- // console.log(pointingTo, shouldBe);
268
-
269
- if (pointingTo == shouldBe) {
270
- this.log(o.name, "->", `${referenceArray}[${index}]`);
271
-
272
- var memberExpression = MemberExpression(
273
- Identifier(referenceArray),
274
- Literal(index),
275
- true
276
- );
277
-
278
- // Allow re-assignment to the RGF function
279
- if (
280
- p[0] &&
281
- p[0].type === "AssignmentExpression" &&
282
- p[0].left === o
283
- ) {
284
- // fn = ...
285
-
286
- this.replace(o, memberExpression);
287
- } else {
288
- // fn()
289
- // fn
290
-
291
- // In most cases the identifier is being used like this (call expression, or referenced to be called later)
292
- // Replace it with a simple wrapper function that will pass on the reference array
293
-
294
- var conditionalExpression = ConditionalExpression(
295
- Template(
296
- `typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
297
- referenceSignatures[index] || "_"
298
- }"]`
299
- ).single().expression,
300
- FunctionExpression(
301
- [],
302
- [
303
- ReturnStatement(
304
- // clone() is required!
305
- CallExpression(clone(memberExpression), [
306
- Identifier(referenceArray),
307
- SpreadElement(Identifier("arguments")),
308
- ])
309
- ),
310
- ]
311
- ),
312
- memberExpression
313
- );
314
-
315
- this.replace(o, conditionalExpression);
316
- }
317
- }
318
- }
319
- }
320
- }
321
- }
205
+ var transforms = obfuscator.array.filter(
206
+ (x) => x.priority > this.priority
207
+ );
208
+
209
+ var embeddedFunctionName = this.getPlaceholder();
210
+
211
+ var embeddedFunction = {
212
+ type: "FunctionDeclaration",
213
+ id: Identifier(embeddedFunctionName),
214
+ body: BlockStatement([...object.body.body]),
215
+ params: object.params,
216
+ async: false,
217
+ generator: false,
218
+ };
219
+
220
+ var tree = {
221
+ type: "Program",
222
+ body: [
223
+ embeddedFunction,
224
+ ReturnStatement(
225
+ CallExpression(
226
+ MemberExpression(
227
+ Identifier(embeddedFunctionName),
228
+ Literal("apply"),
229
+ true
230
+ ),
231
+ [ThisExpression(), Identifier("arguments")]
232
+ )
233
+ ),
234
+ ],
235
+ };
236
+
237
+ transforms.forEach((transform) => {
238
+ transform.apply(tree);
322
239
  });
323
240
 
324
- var arrayExpression = ArrayExpression([]);
325
- var variableDeclaration = VariableDeclaration([
326
- VariableDeclarator(Identifier(referenceArray), arrayExpression),
327
- ]);
328
-
329
- prepend(contextObject, variableDeclaration);
330
-
331
- queue.forEach(([object, parents]) => {
332
- var name = object?.id?.name;
333
- var signature = referenceSignatures[names.get(name)];
334
-
335
- var embeddedName = name || this.getPlaceholder();
336
-
337
- // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
338
- // RGF runs early and needs completed code before converting to a string.
339
- // (^ the variables haven't been renamed yet)
340
- var obfuscator = new Obfuscator({
341
- ...this.options,
342
- rgf: false,
343
- globalVariables: new Set([
344
- ...this.options.globalVariables,
345
- referenceArray,
346
- ]),
347
- lock: {
348
- integrity: false,
349
- },
350
- eval: false,
351
- hideInitializingCode: false,
352
- stringEncoding: false,
353
- });
354
- var transforms = Object.values(obfuscator.transforms).filter(
355
- (x) => x.priority > this.priority
356
- );
357
-
358
- var embeddedFunction = {
359
- ...object,
360
- type: "FunctionDeclaration",
361
- id: Identifier(embeddedName),
362
- };
363
-
364
- var tree = {
365
- type: "Program",
366
- body: [
367
- embeddedFunction,
368
- ReturnStatement(
369
- CallExpression(
370
- MemberExpression(
371
- Identifier(embeddedName),
372
- Literal("call"),
373
- true
374
- ),
375
- [
376
- Identifier("undefined"),
377
- SpreadElement(
378
- Template(
379
- `Array.prototype.slice.call(arguments, 1)`
380
- ).single().expression
381
- ),
382
- ]
383
- )
384
- ),
385
- ],
386
- };
387
-
388
- (tree as any).__hiddenDeclarations = VariableDeclaration(
389
- VariableDeclarator(referenceArray)
390
- );
391
- (tree as any).__hiddenDeclarations.hidden = true;
392
- (tree as any).__hiddenDeclarations.declarations[0].id.hidden = true;
393
-
394
- transforms.forEach((transform) => {
395
- transform.apply(tree);
396
- });
397
-
398
- // Find eval callbacks
399
- traverse(tree, (o, p) => {
400
- if (o.$eval) {
401
- return () => {
402
- o.$eval(o, p);
403
- };
404
- }
405
- });
241
+ var toString = compileJsSync(tree, obfuscator.options);
406
242
 
407
- var toString = compileJsSync(tree, this.options);
243
+ // new Function(code)
244
+ var newFunctionExpression = NewExpression(Identifier("Function"), [
245
+ Literal(toString),
246
+ ]);
408
247
 
409
- var newFunction = NewExpression(Identifier("Function"), [
410
- Literal(referenceArray),
411
- Literal(toString),
248
+ // The index where this function is placed in the array
249
+ var newFunctionExpressionIndex = this.arrayExpressionElements.length;
250
+
251
+ // Add it to the array
252
+ this.arrayExpressionElements.push(newFunctionExpression);
253
+
254
+ // The member expression to retrieve this function
255
+ var memberExpression = MemberExpression(
256
+ Identifier(this.arrayExpressionName),
257
+ Literal(newFunctionExpressionIndex),
258
+ true
259
+ );
260
+
261
+ // Replace based on type
262
+
263
+ // (1) Function Declaration:
264
+ // - Replace body with call to new function
265
+ if (object.type === "FunctionDeclaration") {
266
+ object.body = BlockStatement([
267
+ ReturnStatement(
268
+ CallExpression(
269
+ MemberExpression(memberExpression, Literal("apply"), true),
270
+ [ThisExpression(), Identifier("arguments")]
271
+ )
272
+ ),
412
273
  ]);
413
274
 
414
- function applySignature(fn) {
415
- if (!signature) {
416
- return fn;
417
- }
418
-
419
- // This code marks the function object with a unique property
420
- return CallExpression(
421
- FunctionExpression(
422
- [],
423
- [
424
- VariableDeclaration(VariableDeclarator("fn", fn)),
425
- ExpressionStatement(
426
- AssignmentExpression(
427
- "=",
428
- MemberExpression(
429
- Identifier("fn"),
430
- Literal(signature),
431
- true
432
- ),
433
- Literal(true)
434
- )
435
- ),
436
- ReturnStatement(Identifier("fn")),
437
- ]
438
- ),
439
- []
440
- );
441
- }
442
-
443
- if (object.type === "FunctionDeclaration") {
444
- arrayExpression.elements[names.get(name)] =
445
- applySignature(newFunction);
446
-
447
- if (Array.isArray(parents[0])) {
448
- parents[0].splice(parents[0].indexOf(object), 1);
449
- } else {
450
- this.error(
451
- new Error(
452
- "Error deleting function declaration: " +
453
- parents.map((x) => x.type).join(",")
454
- )
455
- );
456
- }
457
- } else {
458
- // The wrapper function passes the reference array around
459
- var wrapperFunction = FunctionExpression(
460
- [],
461
- [
462
- ReturnStatement(
463
- CallExpression(
464
- MemberExpression(newFunction, Literal("call"), true),
465
- [
466
- Identifier("undefined"),
467
- Identifier(referenceArray),
468
- SpreadElement(Identifier("arguments")),
469
- ]
470
- )
471
- ),
472
- ]
473
- );
275
+ // The parameters are no longer needed ('arguments' is used to capture them)
276
+ object.params = [];
277
+ return;
278
+ }
474
279
 
475
- this.replace(object, applySignature(wrapperFunction));
476
- }
477
- });
280
+ // (2) Function Expression:
281
+ // - Replace expression with member expression pointing to new function
282
+ if (object.type === "FunctionExpression") {
283
+ this.replace(object, memberExpression);
284
+ return;
285
+ }
478
286
  };
479
287
  }
480
288
  }