js-confuser 1.7.1 → 1.7.2

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 (123) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +12 -27
  3. package/dist/compiler.js +2 -8
  4. package/dist/constants.js +17 -10
  5. package/dist/index.js +7 -30
  6. package/dist/obfuscator.js +15 -62
  7. package/dist/options.js +21 -38
  8. package/dist/order.js +4 -7
  9. package/dist/parser.js +5 -13
  10. package/dist/precedence.js +6 -8
  11. package/dist/presets.js +4 -6
  12. package/dist/probability.js +13 -24
  13. package/dist/templates/bufferToString.js +100 -5
  14. package/dist/templates/crash.js +51 -9
  15. package/dist/templates/es5.js +125 -6
  16. package/dist/templates/functionLength.js +24 -6
  17. package/dist/templates/globals.js +9 -0
  18. package/dist/templates/template.js +71 -30
  19. package/dist/transforms/antiTooling.js +26 -22
  20. package/dist/transforms/calculator.js +18 -54
  21. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +236 -333
  22. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +46 -25
  23. package/dist/transforms/deadCode.js +528 -27
  24. package/dist/transforms/dispatcher.js +106 -110
  25. package/dist/transforms/es5/antiClass.js +70 -44
  26. package/dist/transforms/es5/antiDestructuring.js +14 -38
  27. package/dist/transforms/es5/antiES6Object.js +39 -48
  28. package/dist/transforms/es5/antiSpreadOperator.js +5 -14
  29. package/dist/transforms/es5/antiTemplate.js +10 -19
  30. package/dist/transforms/es5/es5.js +7 -40
  31. package/dist/transforms/extraction/classExtraction.js +83 -0
  32. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +41 -80
  33. package/dist/transforms/extraction/objectExtraction.js +24 -56
  34. package/dist/transforms/finalizer.js +6 -20
  35. package/dist/transforms/flatten.js +51 -99
  36. package/dist/transforms/identifier/globalAnalysis.js +8 -26
  37. package/dist/transforms/identifier/globalConcealing.js +35 -54
  38. package/dist/transforms/identifier/movedDeclarations.js +66 -38
  39. package/dist/transforms/identifier/renameVariables.js +29 -68
  40. package/dist/transforms/identifier/variableAnalysis.js +21 -48
  41. package/dist/transforms/lock/antiDebug.js +20 -25
  42. package/dist/transforms/lock/integrity.js +48 -47
  43. package/dist/transforms/lock/lock.js +62 -113
  44. package/dist/transforms/minify.js +77 -108
  45. package/dist/transforms/opaquePredicates.js +11 -48
  46. package/dist/transforms/preparation.js +17 -50
  47. package/dist/transforms/renameLabels.js +5 -22
  48. package/dist/transforms/rgf.js +93 -69
  49. package/dist/transforms/shuffle.js +41 -46
  50. package/dist/transforms/stack.js +35 -98
  51. package/dist/transforms/string/encoding.js +73 -27
  52. package/dist/transforms/string/stringCompression.js +44 -68
  53. package/dist/transforms/string/stringConcealing.js +125 -134
  54. package/dist/transforms/string/stringEncoding.js +6 -26
  55. package/dist/transforms/string/stringSplitting.js +5 -30
  56. package/dist/transforms/transform.js +50 -100
  57. package/dist/traverse.js +11 -18
  58. package/dist/util/compare.js +27 -29
  59. package/dist/util/gen.js +32 -86
  60. package/dist/util/guard.js +0 -1
  61. package/dist/util/identifiers.js +9 -72
  62. package/dist/util/insert.js +27 -77
  63. package/dist/util/math.js +0 -3
  64. package/dist/util/object.js +3 -7
  65. package/dist/util/random.js +5 -36
  66. package/dist/util/scope.js +6 -3
  67. package/package.json +3 -3
  68. package/src/constants.ts +12 -0
  69. package/src/options.ts +13 -0
  70. package/src/order.ts +2 -2
  71. package/src/templates/bufferToString.ts +49 -11
  72. package/src/templates/functionLength.ts +21 -3
  73. package/src/templates/globals.ts +3 -0
  74. package/src/templates/template.ts +85 -25
  75. package/src/transforms/antiTooling.ts +33 -11
  76. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +2 -2
  77. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +46 -10
  78. package/src/transforms/deadCode.ts +0 -16
  79. package/src/transforms/dispatcher.ts +91 -69
  80. package/src/transforms/es5/antiClass.ts +10 -1
  81. package/src/transforms/extraction/classExtraction.ts +168 -0
  82. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +9 -10
  83. package/src/transforms/extraction/objectExtraction.ts +4 -15
  84. package/src/transforms/flatten.ts +20 -5
  85. package/src/transforms/identifier/globalConcealing.ts +29 -65
  86. package/src/transforms/identifier/movedDeclarations.ts +90 -24
  87. package/src/transforms/minify.ts +27 -12
  88. package/src/transforms/rgf.ts +94 -5
  89. package/src/transforms/stack.ts +12 -3
  90. package/src/transforms/string/encoding.ts +85 -51
  91. package/src/transforms/string/stringCompression.ts +5 -8
  92. package/src/transforms/string/stringConcealing.ts +139 -113
  93. package/src/transforms/string/stringEncoding.ts +1 -2
  94. package/src/transforms/string/stringSplitting.ts +1 -2
  95. package/src/transforms/transform.ts +30 -1
  96. package/src/util/compare.ts +39 -5
  97. package/src/util/gen.ts +10 -3
  98. package/src/util/insert.ts +17 -0
  99. package/src/util/scope.ts +14 -2
  100. package/test/code/Cash.test.ts +10 -4
  101. package/test/code/StrictMode.src.js +65 -0
  102. package/test/code/StrictMode.test.js +37 -0
  103. package/test/compare.test.ts +62 -2
  104. package/test/options.test.ts +111 -55
  105. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +37 -18
  106. package/test/transforms/dispatcher.test.ts +55 -0
  107. package/test/transforms/extraction/classExtraction.test.ts +86 -0
  108. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +8 -0
  109. package/test/transforms/extraction/objectExtraction.test.ts +2 -0
  110. package/test/transforms/identifier/globalConcealing.test.ts +19 -0
  111. package/test/transforms/identifier/movedDeclarations.test.ts +61 -0
  112. package/test/transforms/minify.test.ts +37 -0
  113. package/test/transforms/rgf.test.ts +50 -0
  114. package/dist/transforms/controlFlowFlattening/choiceFlowObfuscation.js +0 -62
  115. package/dist/transforms/controlFlowFlattening/controlFlowObfuscation.js +0 -159
  116. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +0 -106
  117. package/dist/transforms/eval.js +0 -84
  118. package/dist/transforms/hexadecimalNumbers.js +0 -63
  119. package/dist/transforms/hideInitializingCode.js +0 -270
  120. package/dist/transforms/identifier/nameRecycling.js +0 -218
  121. package/dist/transforms/label.js +0 -67
  122. package/dist/transforms/preparation/nameConflicts.js +0 -116
  123. package/dist/transforms/preparation/preparation.js +0 -188
@@ -22,9 +22,10 @@ import {
22
22
  } from "../../util/gen";
23
23
  import { append, prepend } from "../../util/insert";
24
24
  import { chance, getRandomInteger } from "../../util/random";
25
- import { reservedIdentifiers } from "../../constants";
25
+ import { predictableFunctionTag, reservedIdentifiers } from "../../constants";
26
26
  import { ComputeProbabilityMap } from "../../probability";
27
27
  import GlobalAnalysis from "./globalAnalysis";
28
+ import { GetGlobalTemplate } from "../../templates/bufferToString";
28
29
 
29
30
  /**
30
31
  * Global Concealing hides global variables being accessed.
@@ -33,6 +34,7 @@ import GlobalAnalysis from "./globalAnalysis";
33
34
  */
34
35
  export default class GlobalConcealing extends Transform {
35
36
  globalAnalysis: GlobalAnalysis;
37
+ ignoreGlobals = new Set(["require", "__dirname", "eval"]);
36
38
 
37
39
  constructor(o) {
38
40
  super(o, ObfuscateOrder.GlobalConcealing);
@@ -52,7 +54,9 @@ export default class GlobalConcealing extends Transform {
52
54
  delete globals[del];
53
55
  });
54
56
 
55
- delete globals["require"];
57
+ for (var varName of this.ignoreGlobals) {
58
+ delete globals[varName];
59
+ }
56
60
 
57
61
  reservedIdentifiers.forEach((x) => {
58
62
  delete globals[x];
@@ -69,55 +73,33 @@ export default class GlobalConcealing extends Transform {
69
73
  });
70
74
 
71
75
  if (Object.keys(globals).length > 0) {
72
- var used = new Set();
76
+ var usedStates = new Set<number>();
73
77
 
74
78
  // Make getter function
75
79
 
76
80
  // holds "window" or "global"
77
81
  var globalVar = this.getPlaceholder();
78
82
 
79
- // holds outermost "this"
80
- var thisVar = this.getPlaceholder();
81
-
82
- // "window" or "global" in node
83
- var global =
84
- this.options.globalVariables.values().next().value || "window";
85
- var alternateGlobal = global === "window" ? "global" : "window";
86
-
87
- var getGlobalVariableFnName = this.getPlaceholder();
88
- var getThisVariableFnName = this.getPlaceholder();
83
+ var getGlobalVariableFnName =
84
+ this.getPlaceholder() + predictableFunctionTag;
89
85
 
90
86
  // Returns global variable or fall backs to `this`
91
- var getGlobalVariableFn = Template(`
92
- var ${getGlobalVariableFnName} = function(){
93
- try {
94
- return ${global} || ${alternateGlobal} || (new Function("return this"))();
95
- } catch (e){
96
- return ${getThisVariableFnName}["call"](this);
97
- }
98
- }`).single();
99
-
100
- var getThisVariableFn = Template(`
101
- var ${getThisVariableFnName} = function(){
102
- try {
103
- return this;
104
- } catch (e){
105
- return null;
106
- }
107
- }`).single();
87
+ var getGlobalVariableFn = GetGlobalTemplate.compile({
88
+ getGlobalFnName: getGlobalVariableFnName,
89
+ });
108
90
 
109
91
  // 2. Replace old accessors
110
- var globalFn = this.getPlaceholder();
92
+ var globalFn = this.getPlaceholder() + predictableFunctionTag;
111
93
 
112
94
  var newNames: { [globalVarName: string]: number } = Object.create(null);
113
95
 
114
96
  Object.keys(globals).forEach((name) => {
115
97
  var locations: Location[] = globals[name];
116
- var state;
98
+ var state: number;
117
99
  do {
118
- state = getRandomInteger(-1000, 1000 + used.size);
119
- } while (used.has(state));
120
- used.add(state);
100
+ state = getRandomInteger(-1000, 1000 + usedStates.size);
101
+ } while (usedStates.has(state));
102
+ usedStates.add(state);
121
103
 
122
104
  newNames[name] = state;
123
105
 
@@ -136,10 +118,10 @@ export default class GlobalConcealing extends Transform {
136
118
  do {
137
119
  state = getRandomInteger(
138
120
  0,
139
- 1000 + used.size + this.options.globalVariables.size * 100
121
+ 1000 + usedStates.size + this.options.globalVariables.size * 100
140
122
  );
141
- } while (used.has(state));
142
- used.add(state);
123
+ } while (usedStates.has(state));
124
+ usedStates.add(state);
143
125
 
144
126
  newNames[name] = state;
145
127
  }
@@ -159,15 +141,7 @@ export default class GlobalConcealing extends Transform {
159
141
  var code = newNames[name];
160
142
  var body: Node[] = [
161
143
  ReturnStatement(
162
- LogicalExpression(
163
- "||",
164
- MemberExpression(
165
- Identifier(globalVar),
166
- Literal(name),
167
- true
168
- ),
169
- MemberExpression(Identifier(thisVar), Literal(name), true)
170
- )
144
+ MemberExpression(Identifier(globalVar), Literal(name), true)
171
145
  ),
172
146
  ];
173
147
  if (chance(50)) {
@@ -180,7 +154,7 @@ export default class GlobalConcealing extends Transform {
180
154
  "||",
181
155
  Literal(name),
182
156
  MemberExpression(
183
- Identifier(thisVar),
157
+ Identifier(globalVar),
184
158
  Literal(name),
185
159
  true
186
160
  )
@@ -195,18 +169,10 @@ export default class GlobalConcealing extends Transform {
195
169
  })
196
170
  ),
197
171
  ReturnStatement(
198
- LogicalExpression(
199
- "||",
200
- MemberExpression(
201
- Identifier(globalVar),
202
- Identifier(returnName),
203
- true
204
- ),
205
- MemberExpression(
206
- Identifier(thisVar),
207
- Identifier(returnName),
208
- true
209
- )
172
+ MemberExpression(
173
+ Identifier(globalVar),
174
+ Identifier(returnName),
175
+ true
210
176
  )
211
177
  ),
212
178
  ]
@@ -215,7 +181,7 @@ export default class GlobalConcealing extends Transform {
215
181
  var tempVar = this.getPlaceholder();
216
182
 
217
183
  var variableDeclaration = Template(`
218
- var ${globalVar}, ${thisVar};
184
+ var ${globalVar};
219
185
  `).single();
220
186
 
221
187
  variableDeclaration.declarations.push(
@@ -226,11 +192,9 @@ export default class GlobalConcealing extends Transform {
226
192
  FunctionExpression(
227
193
  [],
228
194
  [
229
- getGlobalVariableFn,
230
- getThisVariableFn,
231
-
195
+ ...getGlobalVariableFn,
232
196
  Template(
233
- `return ${thisVar} = ${getThisVariableFnName}["call"](this, ${globalFn}), ${globalVar} = ${getGlobalVariableFnName}["call"](this)`
197
+ `return ${globalVar} = ${getGlobalVariableFnName}["call"](this)`
234
198
  ).single(),
235
199
  ]
236
200
  ),
@@ -6,10 +6,21 @@ import {
6
6
  Identifier,
7
7
  Node,
8
8
  VariableDeclarator,
9
+ AssignmentPattern,
9
10
  } from "../../util/gen";
10
- import { isForInitialize, prepend } from "../../util/insert";
11
+ import {
12
+ isForInitialize,
13
+ isFunction,
14
+ isStrictModeFunction,
15
+ prepend,
16
+ } from "../../util/insert";
11
17
  import { ok } from "assert";
12
18
  import { ObfuscateOrder } from "../../order";
19
+ import { choice } from "../../util/random";
20
+ import { predictableFunctionTag } from "../../constants";
21
+ import { isIndependent, isMoveable } from "../../util/compare";
22
+ import { getFunctionParameters } from "../../util/identifiers";
23
+ import { isLexicalScope } from "../../util/scope";
13
24
 
14
25
  /**
15
26
  * Defines all the names at the top of every lexical block.
@@ -33,35 +44,86 @@ export default class MovedDeclarations extends Transform {
33
44
  var forInitializeType = isForInitialize(object, parents);
34
45
 
35
46
  // Get the block statement or Program node
36
- var blockIndex = parents.findIndex((x) => isBlock(x));
47
+ var blockIndex = parents.findIndex((x) => isLexicalScope(x));
37
48
  var block = parents[blockIndex];
38
- var body = block.body;
39
- var bodyObject = parents[blockIndex - 2] || object;
49
+ var body: Node[] =
50
+ block.type === "SwitchCase" ? block.consequent : block.body;
51
+ ok(Array.isArray(body), "No body array found.");
40
52
 
41
- // Make sure in the block statement, and not already at the top of it
53
+ var bodyObject = parents[blockIndex - 2] || object;
42
54
  var index = body.indexOf(bodyObject);
43
- if (index === -1 || index === 0) return;
44
55
 
45
- var topVariableDeclaration;
46
- if (body[0].type === "VariableDeclaration" && body[0].kind === "var") {
47
- topVariableDeclaration = body[0];
56
+ var varName = object.declarations[0].id.name;
57
+ ok(typeof varName === "string");
58
+
59
+ var predictableFunctionIndex = parents.findIndex((x) => isFunction(x));
60
+ var predictableFunction = parents[predictableFunctionIndex];
61
+
62
+ var deleteStatement = false;
63
+
64
+ if (
65
+ predictableFunction &&
66
+ ((predictableFunction.id &&
67
+ predictableFunction.id.name.includes(predictableFunctionTag)) ||
68
+ predictableFunction[predictableFunctionTag]) && // Must have predictableFunctionTag in the name, or on object
69
+ predictableFunction[predictableFunctionTag] !== false && // If === false, the function is deemed not predictable
70
+ predictableFunction.params.length < 1000 && // Max 1,000 parameters
71
+ !predictableFunction.params.find((x) => x.type === "RestElement") && // Cannot add parameters after spread operator
72
+ !(
73
+ ["Property", "MethodDefinition"].includes(
74
+ parents[predictableFunctionIndex + 1]?.type
75
+ ) && parents[predictableFunctionIndex + 1]?.kind !== "init"
76
+ ) && // Preserve getter/setter methods
77
+ !getFunctionParameters(
78
+ predictableFunction,
79
+ parents.slice(predictableFunctionIndex)
80
+ ).find((entry) => entry[0].name === varName) // Ensure not duplicate param name
81
+ ) {
82
+ // Use function f(..., x, y, z) to declare name
83
+
84
+ var value = object.declarations[0].init;
85
+ var isPredictablyComputed =
86
+ predictableFunction.body === block &&
87
+ !isStrictModeFunction(predictableFunction) &&
88
+ value &&
89
+ isIndependent(value, []) &&
90
+ isMoveable(value, [object.declarations[0], object, ...parents]);
91
+
92
+ var defineWithValue = isPredictablyComputed;
93
+
94
+ if (defineWithValue) {
95
+ predictableFunction.params.push(
96
+ AssignmentPattern(Identifier(varName), value)
97
+ );
98
+ object.declarations[0].init = null;
99
+ deleteStatement = true;
100
+ } else {
101
+ predictableFunction.params.push(Identifier(varName));
102
+ }
48
103
  } else {
49
- topVariableDeclaration = {
50
- type: "VariableDeclaration",
51
- declarations: [],
52
- kind: "var",
53
- };
104
+ // Use 'var x, y, z' to declare name
54
105
 
55
- prepend(block, topVariableDeclaration);
56
- }
106
+ // Make sure in the block statement, and not already at the top of it
107
+ if (index === -1 || index === 0) return;
57
108
 
58
- var varName = object.declarations[0].id.name;
59
- ok(typeof varName === "string");
109
+ var topVariableDeclaration;
110
+ if (body[0].type === "VariableDeclaration" && body[0].kind === "var") {
111
+ topVariableDeclaration = body[0];
112
+ } else {
113
+ topVariableDeclaration = {
114
+ type: "VariableDeclaration",
115
+ declarations: [],
116
+ kind: "var",
117
+ };
60
118
 
61
- // Add `var x` at the top of the block
62
- topVariableDeclaration.declarations.push(
63
- VariableDeclarator(Identifier(varName))
64
- );
119
+ prepend(block, topVariableDeclaration);
120
+ }
121
+
122
+ // Add `var x` at the top of the block
123
+ topVariableDeclaration.declarations.push(
124
+ VariableDeclarator(Identifier(varName))
125
+ );
126
+ }
65
127
 
66
128
  var assignmentExpression = AssignmentExpression(
67
129
  "=",
@@ -79,8 +141,12 @@ export default class MovedDeclarations extends Transform {
79
141
  this.replace(object, Identifier(varName));
80
142
  }
81
143
  } else {
82
- // Replace `var x = value` to `x = value`
83
- this.replace(object, ExpressionStatement(assignmentExpression));
144
+ if (deleteStatement && index !== -1) {
145
+ body.splice(index, 1);
146
+ } else {
147
+ // Replace `var x = value` to `x = value`
148
+ this.replace(object, ExpressionStatement(assignmentExpression));
149
+ }
84
150
  }
85
151
  };
86
152
  }
@@ -29,6 +29,8 @@ import { walk, isBlock } from "../traverse";
29
29
  import { ok } from "assert";
30
30
  import { isLexicalScope } from "../util/scope";
31
31
  import Template from "../templates/template";
32
+ import { ObjectDefineProperty } from "../templates/globals";
33
+ import { getIdentifierInfo } from "../util/identifiers";
32
34
 
33
35
  /**
34
36
  * Basic transformations to reduce code size.
@@ -259,25 +261,34 @@ export default class Minify extends Transform {
259
261
  append(
260
262
  parents[parents.length - 1] || object,
261
263
  Template(`
262
- function ${this.arrowFunctionName}(arrowFn, functionLength){
264
+ function ${this.arrowFunctionName}(arrowFn, functionLength = 0){
263
265
  var functionObject = function(){ return arrowFn(...arguments) };
264
266
 
265
- Object["defineProperty"](functionObject, "length", {
266
- "value": functionLength,
267
- "configurable": true
268
- });
269
-
270
- return functionObject;
267
+ ${
268
+ this.options.preserveFunctionLength
269
+ ? `return {ObjectDefineProperty}(functionObject, "length", {
270
+ "value": functionLength,
271
+ "configurable": true
272
+ });`
273
+ : `return functionObject`
274
+ }
275
+
271
276
  }
272
- `).single()
277
+ `).single({
278
+ ObjectDefineProperty: this.options.preserveFunctionLength
279
+ ? this.createInitVariable(ObjectDefineProperty, parents)
280
+ : undefined,
281
+ })
273
282
  );
274
283
  }
275
284
 
276
285
  const wrap = (object: Node) => {
277
- return CallExpression(Identifier(this.arrowFunctionName), [
278
- clone(object),
279
- Literal(computeFunctionLength(object.params)),
280
- ]);
286
+ var args: Node[] = [clone(object)];
287
+ var fnLength = computeFunctionLength(object.params);
288
+ if (this.options.preserveFunctionLength && fnLength != 0) {
289
+ args.push(Literal(fnLength));
290
+ }
291
+ return CallExpression(Identifier(this.arrowFunctionName), args);
281
292
  };
282
293
 
283
294
  if (object.type == "FunctionExpression") {
@@ -619,6 +630,7 @@ export default class Minify extends Transform {
619
630
 
620
631
  if (
621
632
  object.id.type == "ObjectPattern" &&
633
+ object.init &&
622
634
  object.init.type == "ObjectExpression"
623
635
  ) {
624
636
  if (
@@ -673,6 +685,9 @@ export default class Minify extends Transform {
673
685
  }
674
686
  if (object.type == "Identifier") {
675
687
  return () => {
688
+ var info = getIdentifierInfo(object, parents);
689
+ if (info.spec.isDefined || info.spec.isModified) return;
690
+
676
691
  if (object.name == "undefined" && !isForInitialize(object, parents)) {
677
692
  this.replaceIdentifierOrLiteral(
678
693
  object,
@@ -1,13 +1,16 @@
1
1
  import { compileJsSync } from "../compiler";
2
- import { reservedIdentifiers } from "../constants";
2
+ import { predictableFunctionTag, reservedIdentifiers } from "../constants";
3
3
  import Obfuscator from "../obfuscator";
4
4
  import { ObfuscateOrder } from "../order";
5
5
  import { ComputeProbabilityMap } from "../probability";
6
+ import { FunctionLengthTemplate } from "../templates/functionLength";
7
+ import { ObjectDefineProperty } from "../templates/globals";
6
8
  import { walk } from "../traverse";
7
9
  import {
8
10
  ArrayExpression,
9
11
  BlockStatement,
10
12
  CallExpression,
13
+ ExpressionStatement,
11
14
  Identifier,
12
15
  Literal,
13
16
  MemberExpression,
@@ -19,7 +22,11 @@ import {
19
22
  VariableDeclarator,
20
23
  } from "../util/gen";
21
24
  import { getIdentifierInfo } from "../util/identifiers";
22
- import { prepend, getDefiningContext } from "../util/insert";
25
+ import {
26
+ prepend,
27
+ getDefiningContext,
28
+ computeFunctionLength,
29
+ } from "../util/insert";
23
30
  import Integrity from "./lock/integrity";
24
31
  import Transform from "./transform";
25
32
 
@@ -38,6 +45,16 @@ export default class RGF extends Transform {
38
45
  // The name of the array holding all the `new Function` expressions
39
46
  arrayExpressionName: string;
40
47
 
48
+ functionLengthName: string;
49
+
50
+ getFunctionLengthName(parents: Node[]) {
51
+ if (!this.functionLengthName) {
52
+ this.functionLengthName = this.getPlaceholder();
53
+ }
54
+
55
+ return this.functionLengthName;
56
+ }
57
+
41
58
  constructor(o) {
42
59
  super(o, ObfuscateOrder.RGF);
43
60
 
@@ -60,6 +77,19 @@ export default class RGF extends Transform {
60
77
  )
61
78
  );
62
79
  }
80
+
81
+ // The function.length helper function must be placed last
82
+ if (this.functionLengthName) {
83
+ prepend(
84
+ tree,
85
+ FunctionLengthTemplate.single({
86
+ name: this.functionLengthName,
87
+ ObjectDefineProperty: this.createInitVariable(ObjectDefineProperty, [
88
+ tree,
89
+ ]),
90
+ })
91
+ );
92
+ }
63
93
  }
64
94
 
65
95
  match(object, parents) {
@@ -141,6 +171,7 @@ export default class RGF extends Transform {
141
171
  walk(object, parents, (o, p) => {
142
172
  if (
143
173
  o.type === "Identifier" &&
174
+ o.name !== this.arrayExpressionName &&
144
175
  !reservedIdentifiers.has(o.name) &&
145
176
  !this.options.globalVariables.has(o.name)
146
177
  ) {
@@ -226,9 +257,26 @@ export default class RGF extends Transform {
226
257
  generator: false,
227
258
  };
228
259
 
260
+ // The new program will look like this
261
+ // new Function(`
262
+ // var rgf_array = this[0]
263
+ // function greet(message){
264
+ // console.log(message)
265
+ // }
266
+ // return greet.apply(this[1], arguments)
267
+ // `)
268
+ //
269
+ // And called like
270
+ // f.apply([ rgf_array, this ], arguments)
229
271
  var tree = {
230
272
  type: "Program",
231
273
  body: [
274
+ VariableDeclaration(
275
+ VariableDeclarator(
276
+ this.arrayExpressionName,
277
+ MemberExpression(ThisExpression(), Literal(0))
278
+ )
279
+ ),
232
280
  embeddedFunction,
233
281
  ReturnStatement(
234
282
  CallExpression(
@@ -237,7 +285,10 @@ export default class RGF extends Transform {
237
285
  Literal("apply"),
238
286
  true
239
287
  ),
240
- [ThisExpression(), Identifier("arguments")]
288
+ [
289
+ MemberExpression(ThisExpression(), Literal(1)),
290
+ Identifier("arguments"),
291
+ ]
241
292
  )
242
293
  ),
243
294
  ],
@@ -261,12 +312,14 @@ export default class RGF extends Transform {
261
312
  this.arrayExpressionElements.push(newFunctionExpression);
262
313
 
263
314
  // The member expression to retrieve this function
264
- var memberExpression = MemberExpression(
315
+ var memberExpression: Node = MemberExpression(
265
316
  Identifier(this.arrayExpressionName),
266
317
  Literal(newFunctionExpressionIndex),
267
318
  true
268
319
  );
269
320
 
321
+ var originalFunctionLength = computeFunctionLength(object.params);
322
+
270
323
  // Replace based on type
271
324
 
272
325
  // (1) Function Declaration:
@@ -276,19 +329,55 @@ export default class RGF extends Transform {
276
329
  ReturnStatement(
277
330
  CallExpression(
278
331
  MemberExpression(memberExpression, Literal("apply"), true),
279
- [ThisExpression(), Identifier("arguments")]
332
+ [
333
+ ArrayExpression([
334
+ Identifier(this.arrayExpressionName),
335
+ ThisExpression(),
336
+ ]),
337
+ Identifier("arguments"),
338
+ ]
280
339
  )
281
340
  ),
282
341
  ]);
283
342
 
284
343
  // The parameters are no longer needed ('arguments' is used to capture them)
285
344
  object.params = [];
345
+
346
+ // The function is no longer guaranteed to not have extraneous parameters passed in
347
+ object[predictableFunctionTag] = false;
348
+
349
+ if (
350
+ this.options.preserveFunctionLength &&
351
+ originalFunctionLength !== 0
352
+ ) {
353
+ var body = parents[0] as unknown as Node[];
354
+
355
+ body.splice(
356
+ body.indexOf(object),
357
+ 0,
358
+ ExpressionStatement(
359
+ CallExpression(Identifier(this.getFunctionLengthName(parents)), [
360
+ Identifier(object.id.name),
361
+ Literal(originalFunctionLength),
362
+ ])
363
+ )
364
+ );
365
+ }
286
366
  return;
287
367
  }
288
368
 
289
369
  // (2) Function Expression:
290
370
  // - Replace expression with member expression pointing to new function
291
371
  if (object.type === "FunctionExpression") {
372
+ if (
373
+ this.options.preserveFunctionLength &&
374
+ originalFunctionLength !== 0
375
+ ) {
376
+ memberExpression = CallExpression(
377
+ Identifier(this.getFunctionLengthName(parents)),
378
+ [memberExpression, Literal(originalFunctionLength)]
379
+ );
380
+ }
292
381
  this.replace(object, memberExpression);
293
382
  return;
294
383
  }
@@ -1,7 +1,7 @@
1
1
  import { ok } from "assert";
2
2
  import { ObfuscateOrder } from "../order";
3
3
  import { ComputeProbabilityMap } from "../probability";
4
- import Template from "../templates/template";
4
+ import Template, { ITemplate } from "../templates/template";
5
5
  import { walk } from "../traverse";
6
6
  import {
7
7
  AssignmentExpression,
@@ -16,6 +16,8 @@ import {
16
16
  RestElement,
17
17
  ReturnStatement,
18
18
  SequenceExpression,
19
+ VariableDeclaration,
20
+ VariableDeclarator,
19
21
  } from "../util/gen";
20
22
  import { getIdentifierInfo } from "../util/identifiers";
21
23
  import {
@@ -32,6 +34,7 @@ import { chance, choice, getRandomInteger } from "../util/random";
32
34
  import Transform from "./transform";
33
35
  import { noRenameVariablePrefix } from "../constants";
34
36
  import { FunctionLengthTemplate } from "../templates/functionLength";
37
+ import { ObjectDefineProperty } from "../templates/globals";
35
38
 
36
39
  export default class Stack extends Transform {
37
40
  mangledExpressionsMade: number;
@@ -497,12 +500,18 @@ export default class Stack extends Transform {
497
500
  Template(`${stackName}["length"] = ${startingSize}`).single()
498
501
  );
499
502
 
500
- if (originalFunctionLength !== 0) {
503
+ if (this.options.preserveFunctionLength && originalFunctionLength !== 0) {
501
504
  if (!this.functionLengthName) {
502
505
  this.functionLengthName = this.getPlaceholder();
503
506
  prepend(
504
507
  parents[parents.length - 1] || object,
505
- FunctionLengthTemplate.single({ name: this.functionLengthName })
508
+ FunctionLengthTemplate.single({
509
+ name: this.functionLengthName,
510
+ ObjectDefineProperty: this.createInitVariable(
511
+ ObjectDefineProperty,
512
+ parents
513
+ ),
514
+ })
506
515
  );
507
516
  }
508
517