js-confuser 2.0.0-alpha.2 → 2.0.0-alpha.4

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 (91) hide show
  1. package/.prettierrc +4 -0
  2. package/CHANGELOG.md +42 -8
  3. package/Migration.md +23 -8
  4. package/README.md +2 -2
  5. package/dist/constants.js +11 -2
  6. package/dist/index.js +49 -6
  7. package/dist/obfuscator.js +121 -10
  8. package/dist/order.js +0 -1
  9. package/dist/probability.js +1 -96
  10. package/dist/templates/getGlobalTemplate.js +4 -1
  11. package/dist/templates/integrityTemplate.js +1 -1
  12. package/dist/templates/stringCompressionTemplate.js +3 -3
  13. package/dist/templates/tamperProtectionTemplates.js +1 -1
  14. package/dist/templates/template.js +17 -12
  15. package/dist/transforms/controlFlowFlattening.js +112 -83
  16. package/dist/transforms/deadCode.js +21 -22
  17. package/dist/transforms/dispatcher.js +62 -37
  18. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +5 -0
  19. package/dist/transforms/extraction/objectExtraction.js +1 -2
  20. package/dist/transforms/finalizer.js +1 -1
  21. package/dist/transforms/flatten.js +2 -19
  22. package/dist/transforms/identifier/globalConcealing.js +3 -4
  23. package/dist/transforms/identifier/movedDeclarations.js +12 -5
  24. package/dist/transforms/identifier/renameVariables.js +40 -6
  25. package/dist/transforms/lock/integrity.js +9 -1
  26. package/dist/transforms/lock/lock.js +16 -9
  27. package/dist/transforms/minify.js +64 -27
  28. package/dist/transforms/opaquePredicates.js +6 -7
  29. package/dist/transforms/pack.js +32 -5
  30. package/dist/transforms/plugin.js +20 -39
  31. package/dist/transforms/preparation.js +25 -36
  32. package/dist/transforms/renameLabels.js +1 -2
  33. package/dist/transforms/rgf.js +36 -16
  34. package/dist/transforms/shuffle.js +10 -11
  35. package/dist/transforms/string/stringCompression.js +14 -10
  36. package/dist/transforms/string/stringConcealing.js +7 -5
  37. package/dist/transforms/string/stringEncoding.js +4 -2
  38. package/dist/transforms/string/stringSplitting.js +4 -2
  39. package/dist/transforms/variableMasking.js +3 -2
  40. package/dist/utils/NameGen.js +5 -2
  41. package/dist/utils/PredicateGen.js +62 -0
  42. package/dist/utils/ast-utils.js +24 -9
  43. package/dist/utils/random-utils.js +10 -0
  44. package/dist/validateOptions.js +2 -2
  45. package/index.d.ts +16 -2
  46. package/package.json +2 -2
  47. package/src/constants.ts +15 -5
  48. package/src/index.ts +15 -5
  49. package/src/obfuscationResult.ts +7 -1
  50. package/src/obfuscator.ts +152 -12
  51. package/src/options.ts +26 -8
  52. package/src/order.ts +0 -2
  53. package/src/templates/getGlobalTemplate.ts +5 -1
  54. package/src/templates/integrityTemplate.ts +14 -19
  55. package/src/templates/stringCompressionTemplate.ts +4 -28
  56. package/src/templates/tamperProtectionTemplates.ts +7 -3
  57. package/src/templates/template.ts +5 -3
  58. package/src/transforms/controlFlowFlattening.ts +139 -83
  59. package/src/transforms/deadCode.ts +27 -30
  60. package/src/transforms/dispatcher.ts +24 -5
  61. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +10 -1
  62. package/src/transforms/extraction/objectExtraction.ts +1 -2
  63. package/src/transforms/finalizer.ts +1 -1
  64. package/src/transforms/flatten.ts +3 -22
  65. package/src/transforms/identifier/globalConcealing.ts +26 -17
  66. package/src/transforms/identifier/movedDeclarations.ts +18 -6
  67. package/src/transforms/identifier/renameVariables.ts +48 -6
  68. package/src/transforms/lock/integrity.ts +11 -1
  69. package/src/transforms/lock/lock.ts +26 -10
  70. package/src/transforms/minify.ts +85 -38
  71. package/src/transforms/opaquePredicates.ts +6 -9
  72. package/src/transforms/pack.ts +41 -5
  73. package/src/transforms/plugin.ts +47 -69
  74. package/src/transforms/preparation.ts +33 -46
  75. package/src/transforms/renameLabels.ts +1 -2
  76. package/src/transforms/rgf.ts +52 -23
  77. package/src/transforms/shuffle.ts +28 -26
  78. package/src/transforms/string/encoding.ts +1 -1
  79. package/src/transforms/string/stringCompression.ts +22 -13
  80. package/src/transforms/string/stringConcealing.ts +13 -7
  81. package/src/transforms/string/stringEncoding.ts +6 -2
  82. package/src/transforms/string/stringSplitting.ts +9 -4
  83. package/src/transforms/variableMasking.ts +2 -2
  84. package/src/utils/NameGen.ts +13 -3
  85. package/src/utils/PredicateGen.ts +61 -0
  86. package/src/utils/ast-utils.ts +16 -9
  87. package/src/utils/random-utils.ts +14 -0
  88. package/src/validateOptions.ts +7 -4
  89. package/src/probability.ts +0 -110
  90. package/src/transforms/functionOutlining.ts +0 -225
  91. package/src/utils/ControlObject.ts +0 -141
@@ -4,10 +4,10 @@ import { NameGen } from "../../utils/NameGen";
4
4
  import Template from "../../templates/template";
5
5
  import { PluginArg, PluginObject } from "../plugin";
6
6
  import { Order } from "../../order";
7
- import { computeProbabilityMap } from "../../probability";
8
7
  import {
9
8
  MULTI_TRANSFORM,
10
9
  reservedIdentifiers,
10
+ reservedNodeModuleIdentifiers,
11
11
  variableFunctionName,
12
12
  } from "../../constants";
13
13
  import {
@@ -16,11 +16,15 @@ import {
16
16
  prepend,
17
17
  } from "../../utils/ast-utils";
18
18
  import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
19
- import { getRandomInteger, getRandomString } from "../../utils/random-utils";
19
+ import {
20
+ getRandomInteger,
21
+ getRandomString,
22
+ shuffle,
23
+ } from "../../utils/random-utils";
20
24
  import { ok } from "assert";
21
25
 
22
26
  const ignoreGlobals = new Set([
23
- "require",
27
+ ...reservedNodeModuleIdentifiers,
24
28
  "__dirname",
25
29
  "eval",
26
30
  "arguments",
@@ -52,19 +56,21 @@ export default ({ Plugin }: PluginArg): PluginObject => {
52
56
  }
53
57
 
54
58
  const createSwitchStatement = () => {
55
- const cases = Array.from(globalMapping.keys()).map((originalName) => {
56
- var mappedKey = globalMapping.get(originalName);
57
-
58
- return t.switchCase(t.stringLiteral(mappedKey), [
59
- t.returnStatement(
60
- t.memberExpression(
61
- t.identifier(globalVarName),
62
- t.stringLiteral(originalName),
63
- true
64
- )
65
- ),
66
- ]);
67
- });
59
+ const cases = shuffle(Array.from(globalMapping.keys())).map(
60
+ (originalName) => {
61
+ var mappedKey = globalMapping.get(originalName);
62
+
63
+ return t.switchCase(t.stringLiteral(mappedKey), [
64
+ t.returnStatement(
65
+ t.memberExpression(
66
+ t.identifier(globalVarName),
67
+ t.stringLiteral(originalName),
68
+ true
69
+ )
70
+ ),
71
+ ]);
72
+ }
73
+ );
68
74
 
69
75
  return t.switchStatement(t.identifier("mapping"), cases);
70
76
  };
@@ -132,7 +138,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
132
138
  if (!mapping) {
133
139
  // Allow user to disable custom global variables
134
140
  if (
135
- !computeProbabilityMap(me.options.globalConcealing, globalName)
141
+ !me.computeProbabilityMap(
142
+ me.options.globalConcealing,
143
+ globalName
144
+ )
136
145
  )
137
146
  continue;
138
147
 
@@ -65,8 +65,12 @@ export default ({ Plugin }: PluginArg): PluginObject => {
65
65
  if (!functionPath || !(functionPath.node as NodeSymbol)[PREDICTABLE])
66
66
  return;
67
67
 
68
+ var fnBody = functionPath.get("body");
69
+
70
+ if (!fnBody.isBlockStatement()) return;
71
+
68
72
  // Must be direct child of the function
69
- if (path.parentPath !== functionPath.get("body")) return;
73
+ if (path.parentPath !== fnBody) return;
70
74
 
71
75
  const functionName = path.node.id.name;
72
76
 
@@ -99,7 +103,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
99
103
  }
100
104
 
101
105
  prepend(
102
- functionPath,
106
+ fnBody,
103
107
  new Template(`
104
108
  if(!${functionName}) {
105
109
  ${functionName} = {functionExpression};
@@ -113,6 +117,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
113
117
  },
114
118
  VariableDeclaration: {
115
119
  exit(path) {
120
+ if (me.isSkipped(path)) return;
116
121
  if (path.node.kind !== "var") return;
117
122
  if (path.node.declarations.length !== 1) return;
118
123
 
@@ -150,7 +155,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
150
155
  let isDefinedAtTop = false;
151
156
  const parentPath = path.parentPath;
152
157
  if (parentPath.isBlock()) {
153
- isDefinedAtTop = parentPath.get("body").indexOf(path) === 0;
158
+ isDefinedAtTop =
159
+ parentPath
160
+ .get("body")
161
+ .filter((x) => x.type !== "ImportDeclaration")
162
+ .indexOf(path) === 0;
154
163
  }
155
164
 
156
165
  // Already at the top - nothing will change
@@ -214,7 +223,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
214
223
  path.isBlock()
215
224
  ) as NodePath<t.Block>;
216
225
 
217
- var topNode = block.node.body[0];
226
+ var topNode = block.node.body.filter(
227
+ (x) => x.type !== "ImportDeclaration"
228
+ )[0];
218
229
  const variableDeclarator = t.variableDeclarator(
219
230
  t.identifier(name)
220
231
  );
@@ -223,8 +234,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
223
234
  topNode.declarations.push(variableDeclarator);
224
235
  break;
225
236
  } else {
226
- block.node.body.unshift(
227
- t.variableDeclaration("var", [variableDeclarator])
237
+ prepend(
238
+ block,
239
+ me.skip(t.variableDeclaration("var", [variableDeclarator]))
228
240
  );
229
241
  }
230
242
 
@@ -6,9 +6,7 @@ import { Order } from "../../order";
6
6
  import {
7
7
  noRenameVariablePrefix,
8
8
  placeholderVariablePrefix,
9
- variableFunctionName,
10
9
  } from "../../constants";
11
- import { computeProbabilityMap } from "../../probability";
12
10
  import {
13
11
  getParentFunctionOrProgram,
14
12
  isDefiningIdentifier,
@@ -30,6 +28,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
30
28
 
31
29
  const definedMap = new Map<t.Node, Set<string>>();
32
30
  const referencedMap = new Map<t.Node, Set<string>>();
31
+ const paramMap = new Map<t.Node, Set<string>>(); // Used for default function parameter special case
33
32
  const bindingMap = new Map<t.Node, Map<string, NodePath<t.Identifier>>>();
34
33
 
35
34
  const renamedVariables = new Map<t.Node, Map<string, string>>();
@@ -50,9 +49,12 @@ export default ({ Plugin }: PluginArg): PluginObject => {
50
49
  ];
51
50
 
52
51
  let isDefined = false;
52
+ let isParameter = false;
53
53
 
54
54
  if (path.isBindingIdentifier() && isDefiningIdentifier(path)) {
55
55
  isDefined = true;
56
+ const binding = path.scope.getBinding(path.node.name);
57
+ if (binding?.kind === "param") isParameter = true;
56
58
 
57
59
  // Function ID is defined in the parent's function declaration
58
60
  if (
@@ -117,7 +119,30 @@ export default ({ Plugin }: PluginArg): PluginObject => {
117
119
 
118
120
  var newName = null;
119
121
 
122
+ const skippedPaths = new Set();
123
+
120
124
  for (let contextPath of contextPaths) {
125
+ if (skippedPaths.has(contextPath)) continue;
126
+
127
+ if (contextPath.isFunction()) {
128
+ var assignmentPattern = contextPath.find(
129
+ (p) => p.listKey === "params" && p.parentPath.isFunction()
130
+ );
131
+
132
+ if (assignmentPattern?.isAssignmentPattern()) {
133
+ var functionPath = assignmentPattern.getFunctionParent();
134
+
135
+ if (functionPath) {
136
+ // The parameters can be still accessed...
137
+ const params = paramMap.get(functionPath.node);
138
+ if (params?.has(identifierName)) {
139
+ } else {
140
+ skippedPaths.add(functionPath);
141
+ }
142
+ }
143
+ }
144
+ }
145
+
121
146
  const { node } = contextPath;
122
147
 
123
148
  const defined = definedMap.get(node);
@@ -140,6 +165,17 @@ export default ({ Plugin }: PluginArg): PluginObject => {
140
165
  // 5. Update Identifier node's 'name' property
141
166
  node.name = newName;
142
167
  node[RENAMED] = true;
168
+
169
+ // 6. Additional parameter mapping
170
+ const binding = identifierPath.scope.getBinding(identifierName);
171
+ if (binding?.kind === "param") {
172
+ var mapNode = binding.scope.path.node;
173
+ if (!paramMap.has(mapNode)) {
174
+ paramMap.set(mapNode, new Set([identifierName]));
175
+ } else {
176
+ paramMap.get(mapNode).add(identifierName);
177
+ }
178
+ }
143
179
  }
144
180
  },
145
181
 
@@ -215,19 +251,25 @@ export default ({ Plugin }: PluginArg): PluginObject => {
215
251
  // Placeholder variables should always be renamed
216
252
  if (name.startsWith(placeholderVariablePrefix)) return true;
217
253
 
218
- // Do not rename exports
219
- if (isExportedIdentifier(bindings?.get(name))) return false;
254
+ const binding = bindings?.get(name);
255
+
256
+ if (binding) {
257
+ // Do not rename exports
258
+ if (isExportedIdentifier(binding)) return false;
259
+ }
220
260
 
221
261
  if (name === me.obfuscator.getStringCompressionLibraryName())
222
262
  return false;
223
263
 
224
264
  // Global variables are additionally checked against user option
225
265
  if (isGlobal) {
226
- if (!computeProbabilityMap(me.options.renameGlobals, name))
266
+ if (!me.computeProbabilityMap(me.options.renameGlobals, name))
227
267
  return false;
228
268
  }
229
269
 
230
- if (!computeProbabilityMap(me.options.renameVariables, name, isGlobal))
270
+ if (
271
+ !me.computeProbabilityMap(me.options.renameVariables, name, isGlobal)
272
+ )
231
273
  return false;
232
274
 
233
275
  return true;
@@ -5,6 +5,7 @@ import { HashFunction } from "../../templates/integrityTemplate";
5
5
  import * as t from "@babel/types";
6
6
  import Template from "../../templates/template";
7
7
  import { NodePath } from "@babel/traverse";
8
+ import { NameGen } from "../../utils/NameGen";
8
9
 
9
10
  export interface IntegrityInterface {
10
11
  fnPath: NodePath<t.FunctionDeclaration>;
@@ -32,6 +33,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
32
33
  },
33
34
  });
34
35
 
36
+ const nameGen = new NameGen(me.options.identifierGenerator, {
37
+ avoidObjectPrototype: true,
38
+ avoidReserved: true,
39
+ });
40
+
35
41
  return {
36
42
  visitor: {
37
43
  Program: {
@@ -78,12 +84,16 @@ export default ({ Plugin }: PluginArg): PluginObject => {
78
84
 
79
85
  var hashCode = HashFunction(codeTrimmed, seed);
80
86
 
87
+ const selfName = funcDecPath.node.id.name;
88
+ const selfCacheProperty = nameGen.generate();
89
+ const selfCacheString = `${selfName}.${selfCacheProperty}`;
90
+
81
91
  // me.log(codeTrimmed, hashCode);
82
92
  me.changeData.functions++;
83
93
 
84
94
  funcDecPath.node.body = t.blockStatement(
85
95
  new Template(`
86
- var hash = ${obfuscatedHashFnName}(${newFunctionDeclaration.id.name}, ${seed});
96
+ var hash = ${selfCacheString} || (${selfCacheString} = ${obfuscatedHashFnName}(${newFunctionDeclaration.id.name}, ${seed}));
87
97
  if(hash === ${hashCode}) {
88
98
  {originalBody}
89
99
  } else {
@@ -26,7 +26,6 @@ import {
26
26
  NativeFunctionTemplate,
27
27
  StrictModeTemplate,
28
28
  } from "../../templates/tamperProtectionTemplates";
29
- import { computeProbabilityMap } from "../../probability";
30
29
 
31
30
  export default ({ Plugin }: PluginArg): PluginObject => {
32
31
  const me = Plugin(Order.Lock, {
@@ -36,6 +35,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
36
35
  });
37
36
 
38
37
  if (me.options.lock.startDate instanceof Date) {
38
+ // Ensure date is in the past
39
+ if (me.options.lock.startDate.getTime() > Date.now()) {
40
+ me.warn("lock.startDate is detected to be in the future");
41
+ }
42
+
39
43
  me.options.lock.customLocks.push({
40
44
  code: [
41
45
  `
@@ -54,6 +58,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
54
58
  }
55
59
 
56
60
  if (me.options.lock.endDate instanceof Date) {
61
+ // Ensure date is in the future
62
+ if (me.options.lock.endDate.getTime() < Date.now()) {
63
+ me.warn("lock.endDate is detected to be in the past");
64
+ }
65
+
57
66
  me.options.lock.customLocks.push({
58
67
  code: [
59
68
  `
@@ -107,6 +116,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
107
116
  }
108
117
  }
109
118
 
119
+ // Prepack Breaker
120
+ if(Math.random() > 1) {
121
+ while(true) {
122
+ console.log("Prepack Breaker");
123
+ }
124
+ }
125
+
110
126
  return namedFunction();
111
127
  }
112
128
  )();
@@ -127,7 +143,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
127
143
  const timesMap = new WeakMap<CustomLock, number>();
128
144
 
129
145
  let countermeasuresNode: NodePath<t.Identifier>;
130
- let invokeCountermeasuresFnName;
146
+ let invokeCountermeasuresFnName: string;
131
147
 
132
148
  if (me.options.lock.countermeasures) {
133
149
  invokeCountermeasuresFnName = me.getPlaceholder("invokeCountermeasures");
@@ -149,15 +165,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
149
165
  };
150
166
  me.globalState.lock.createCountermeasuresCode = createCountermeasuresCode;
151
167
 
152
- function applyLockToBlock(path: NodePath<t.Block>, customLock: CustomLock) {
153
- let times = timesMap.get(customLock);
168
+ const defaultMaxCount = me.options.lock.defaultMaxCount ?? 25;
154
169
 
155
- if (typeof times === "undefined") {
156
- times = 0;
157
- }
170
+ function applyLockToBlock(path: NodePath<t.Block>, customLock: CustomLock) {
171
+ let times = timesMap.get(customLock) || 0;
158
172
 
159
- let maxCount = customLock.maxCount || 100; // 100 is default max count
160
- let minCount = customLock.minCount || 1; // 1 is default min count
173
+ let maxCount = customLock.maxCount ?? defaultMaxCount; // 25 is default max count
174
+ let minCount = customLock.minCount ?? 1; // 1 is default min count
161
175
 
162
176
  if (maxCount >= 0 && times > maxCount) {
163
177
  // Limit creation, allowing -1 to disable the limit entirely
@@ -356,7 +370,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
356
370
  // Don't apply to invokeCountermeasures function (Intended)
357
371
  if (me.obfuscator.isInternalVariable(functionName)) return;
358
372
 
359
- if (!computeProbabilityMap(me.options.lock.integrity, functionName))
373
+ if (
374
+ !me.computeProbabilityMap(me.options.lock.integrity, functionName)
375
+ )
360
376
  return;
361
377
 
362
378
  var newFnName = me.getPlaceholder();
@@ -8,7 +8,12 @@ import {
8
8
  isUndefined,
9
9
  } from "../utils/ast-utils";
10
10
  import { Binding, Scope } from "@babel/traverse";
11
- import { NodeSymbol, placeholderVariablePrefix, UNSAFE } from "../constants";
11
+ import {
12
+ NO_REMOVE,
13
+ NodeSymbol,
14
+ placeholderVariablePrefix,
15
+ UNSAFE,
16
+ } from "../constants";
12
17
 
13
18
  const identifierMap = new Map<string, () => t.Expression>();
14
19
  identifierMap.set("undefined", () =>
@@ -18,6 +23,45 @@ identifierMap.set("Infinity", () =>
18
23
  t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0))
19
24
  );
20
25
 
26
+ function trySimpleDestructuring(id, init) {
27
+ // Simple array/object destructuring
28
+ if (id.isArrayPattern() && init.isArrayExpression()) {
29
+ const elements = id.get("elements");
30
+ const initElements = init.get("elements");
31
+
32
+ if (elements.length === 1 && initElements.length === 1) {
33
+ id.replaceWith(elements[0]);
34
+ init.replaceWith(initElements[0]);
35
+ }
36
+ }
37
+
38
+ if (id.isObjectPattern() && init.isObjectExpression()) {
39
+ const properties = id.get("properties");
40
+ const initProperties = init.get("properties");
41
+
42
+ if (properties.length === 1 && initProperties.length === 1) {
43
+ const firstProperty = properties[0];
44
+ const firstInitProperty = initProperties[0];
45
+
46
+ if (
47
+ firstProperty.isObjectProperty() &&
48
+ firstInitProperty.isObjectProperty()
49
+ ) {
50
+ const firstKey = firstProperty.get("key");
51
+ const firstInitKey = firstInitProperty.get("key");
52
+ if (
53
+ firstKey.isIdentifier() &&
54
+ firstInitKey.isIdentifier() &&
55
+ firstKey.node.name === firstInitKey.node.name
56
+ ) {
57
+ id.replaceWith(firstProperty.node.value);
58
+ init.replaceWith(firstInitProperty.node.value);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+
21
65
  /**
22
66
  * Minify removes unnecessary code and shortens the length for file size.
23
67
  *
@@ -120,6 +164,19 @@ export default ({ Plugin }: PluginArg): PluginObject => {
120
164
  }
121
165
  },
122
166
  },
167
+ // "a" + "b" -> "ab"
168
+ BinaryExpression: {
169
+ exit(path) {
170
+ if (path.node.operator !== "+") return;
171
+
172
+ const left = path.get("left");
173
+ const right = path.get("right");
174
+
175
+ if (!left.isStringLiteral() || !right.isStringLiteral()) return;
176
+
177
+ path.replaceWith(t.stringLiteral(left.node.value + right.node.value));
178
+ },
179
+ },
123
180
  // a["key"] -> a.key
124
181
  MemberExpression: {
125
182
  exit(path) {
@@ -215,7 +272,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
215
272
  const id = path.get("id");
216
273
  if (
217
274
  id.isIdentifier() &&
218
- !id.node.name.startsWith(placeholderVariablePrefix)
275
+ !id.node.name.startsWith(placeholderVariablePrefix) &&
276
+ !(path.node as NodeSymbol)[NO_REMOVE]
219
277
  ) {
220
278
  const binding = path.scope.getBinding(id.node.name);
221
279
  if (
@@ -241,42 +299,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
241
299
  const id = path.get("id");
242
300
  const init = path.get("init");
243
301
 
244
- // Simple array/object destructuring
245
- if (id.isArrayPattern() && init.isArrayExpression()) {
246
- const elements = id.get("elements");
247
- const initElements = init.get("elements");
248
-
249
- if (elements.length === 1 && initElements.length === 1) {
250
- id.replaceWith(elements[0]);
251
- init.replaceWith(initElements[0]);
252
- }
253
- }
254
-
255
- if (id.isObjectPattern() && init.isObjectExpression()) {
256
- const properties = id.get("properties");
257
- const initProperties = init.get("properties");
258
-
259
- if (properties.length === 1 && initProperties.length === 1) {
260
- const firstProperty = properties[0];
261
- const firstInitProperty = initProperties[0];
262
-
263
- if (
264
- firstProperty.isObjectProperty() &&
265
- firstInitProperty.isObjectProperty()
266
- ) {
267
- const firstKey = firstProperty.get("key");
268
- const firstInitKey = firstInitProperty.get("key");
269
- if (
270
- firstKey.isIdentifier() &&
271
- firstInitKey.isIdentifier() &&
272
- firstKey.node.name === firstInitKey.node.name
273
- ) {
274
- id.replaceWith(firstProperty.node.value);
275
- init.replaceWith(firstInitProperty.node.value);
276
- }
277
- }
278
- }
279
- }
302
+ trySimpleDestructuring(id, init);
280
303
 
281
304
  // Remove unused variables
282
305
  // Can only remove if it's pure
@@ -285,6 +308,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
285
308
  const fn = getParentFunctionOrProgram(path);
286
309
  if ((fn.node as NodeSymbol)[UNSAFE]) return;
287
310
 
311
+ // Node explicitly marked as not to be removed
312
+ if ((id as NodeSymbol)[NO_REMOVE]) return;
313
+
288
314
  const binding = path.scope.getBinding(id.node.name);
289
315
 
290
316
  if (
@@ -304,6 +330,27 @@ export default ({ Plugin }: PluginArg): PluginObject => {
304
330
  }
305
331
  },
306
332
  },
333
+ // Simple destructuring
334
+ // Simple arithmetic operations
335
+ AssignmentExpression: {
336
+ exit(path) {
337
+ if (path.node.operator === "=") {
338
+ trySimpleDestructuring(path.get("left"), path.get("right"));
339
+ }
340
+ if (path.node.operator === "+=") {
341
+ const left = path.get("left");
342
+ const right = path.get("right");
343
+
344
+ // a += 1 -> a++
345
+ if (right.isNumericLiteral({ value: 1 })) {
346
+ if (left.isIdentifier() || left.isMemberExpression()) {
347
+ path.replaceWith(t.updateExpression("++", left.node));
348
+ }
349
+ }
350
+ }
351
+ },
352
+ },
353
+
307
354
  // return undefined->return
308
355
  ReturnStatement: {
309
356
  exit(path) {
@@ -2,9 +2,8 @@ import { PluginArg, PluginObject } from "./plugin";
2
2
  import { Order } from "../order";
3
3
  import { NodePath } from "@babel/traverse";
4
4
  import * as t from "@babel/types";
5
- import { getBlock } from "../utils/ast-utils";
6
5
  import { chance, getRandomString } from "../utils/random-utils";
7
- import { computeProbabilityMap } from "../probability";
6
+ import PredicateGen from "../utils/PredicateGen";
8
7
 
9
8
  export default ({ Plugin }: PluginArg): PluginObject => {
10
9
  const me = Plugin(Order.OpaquePredicates, {
@@ -13,12 +12,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
13
12
  },
14
13
  });
15
14
 
16
- function createTruePredicate(path: NodePath) {
17
- const controlObject = me.getControlObject(getBlock(path));
18
-
19
- var trueValue = controlObject.createTruePredicate();
15
+ const predicateGen = new PredicateGen(me);
20
16
 
21
- return trueValue;
17
+ function createTruePredicate(path: NodePath) {
18
+ return predicateGen.generateTrueExpression(path);
22
19
  }
23
20
 
24
21
  let active = true;
@@ -27,13 +24,13 @@ export default ({ Plugin }: PluginArg): PluginObject => {
27
24
  if (!active) return false;
28
25
  if (path.find((p) => me.isSkipped(p))) return false;
29
26
 
30
- if (!computeProbabilityMap(me.options.opaquePredicates)) return false;
27
+ if (!me.computeProbabilityMap(me.options.opaquePredicates)) return false;
31
28
 
32
29
  transformCount++;
33
30
 
34
31
  const depth = path.getAncestry().length;
35
32
 
36
- return chance(1000 - transformCount - depth * 100);
33
+ return chance(500 - transformCount - depth * 100);
37
34
  }
38
35
 
39
36
  function wrapWithPredicate(path: NodePath) {
@@ -10,22 +10,43 @@ import {
10
10
  GEN_NODE,
11
11
  NodeSymbol,
12
12
  reservedIdentifiers,
13
+ reservedNodeModuleIdentifiers,
13
14
  variableFunctionName,
14
15
  WITH_STATEMENT,
15
16
  } from "../constants";
16
17
  import { PluginArg, PluginObject } from "./plugin";
17
18
  import { Order } from "../order";
18
19
 
20
+ export interface PackInterface {
21
+ objectName: string;
22
+ mappings: Map<string, string>;
23
+ setterPropsNeeded: Set<string>;
24
+ typeofMappings: Map<string, string>;
25
+ }
26
+
19
27
  export default function pack({ Plugin }: PluginArg): PluginObject {
20
28
  const me = Plugin(Order.Pack, {
21
29
  changeData: {
22
30
  globals: 0,
23
31
  },
24
32
  });
25
- const objectName = me.obfuscator.nameGen.generate();
26
- const mappings = new Map<string, string>();
27
- const setterPropsNeeded = new Set<string>();
28
- const typeofMappings = new Map<string, string>();
33
+
34
+ // RGF functions will re-use parent Pack Interface
35
+ let packInterface = me.obfuscator.parentObfuscator?.packInterface;
36
+
37
+ // Create new Pack Interface (root)
38
+ if (!packInterface) {
39
+ packInterface = {
40
+ objectName: me.obfuscator.nameGen.generate(),
41
+ mappings: new Map<string, string>(),
42
+ setterPropsNeeded: new Set<string>(),
43
+ typeofMappings: new Map<string, string>(),
44
+ };
45
+ me.obfuscator.packInterface = packInterface;
46
+ }
47
+
48
+ const { objectName, mappings, setterPropsNeeded, typeofMappings } =
49
+ packInterface;
29
50
 
30
51
  const prependNodes: t.Statement[] = [];
31
52
 
@@ -53,10 +74,23 @@ export default function pack({ Plugin }: PluginArg): PluginObject {
53
74
 
54
75
  const identifierName = path.node.name;
55
76
  if (reservedIdentifiers.has(identifierName)) return;
56
- if (me.obfuscator.options.globalVariables.has(identifierName)) return;
77
+ if (
78
+ me.options.target === "node" &&
79
+ reservedNodeModuleIdentifiers.has(identifierName)
80
+ ) {
81
+ // Allow module.exports and require
82
+ } else {
83
+ if (me.options.globalVariables.has(identifierName)) return;
84
+ }
57
85
  if (identifierName === variableFunctionName) return;
86
+ if (identifierName === objectName) return;
58
87
 
59
88
  if (!path.scope.hasGlobal(identifierName)) return;
89
+ if (path.scope.hasBinding(identifierName)) return;
90
+
91
+ // Check user's custom implementation
92
+ if (!me.computeProbabilityMap(me.options.pack, identifierName))
93
+ return;
60
94
 
61
95
  if (
62
96
  path.key === "argument" &&
@@ -105,6 +139,8 @@ export default function pack({ Plugin }: PluginArg): PluginObject {
105
139
  // Final AST handler
106
140
  // Very last step in the obfuscation process
107
141
  finalASTHandler(ast) {
142
+ if (me.obfuscator.parentObfuscator) return ast; // Only for root obfuscator
143
+
108
144
  // Create object expression
109
145
  // Very similar to flatten, maybe refactor to use the same code
110
146
  const objectProperties: t.ObjectMethod[] = [];