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

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 (78) hide show
  1. package/CHANGELOG.md +24 -6
  2. package/dist/constants.js +6 -2
  3. package/dist/index.js +12 -0
  4. package/dist/obfuscator.js +117 -6
  5. package/dist/order.js +0 -1
  6. package/dist/probability.js +1 -96
  7. package/dist/templates/getGlobalTemplate.js +4 -1
  8. package/dist/templates/stringCompressionTemplate.js +3 -3
  9. package/dist/templates/tamperProtectionTemplates.js +1 -1
  10. package/dist/templates/template.js +17 -12
  11. package/dist/transforms/controlFlowFlattening.js +2 -6
  12. package/dist/transforms/deadCode.js +8 -15
  13. package/dist/transforms/dispatcher.js +1 -2
  14. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +5 -0
  15. package/dist/transforms/extraction/objectExtraction.js +1 -2
  16. package/dist/transforms/finalizer.js +1 -1
  17. package/dist/transforms/flatten.js +2 -19
  18. package/dist/transforms/identifier/globalConcealing.js +1 -2
  19. package/dist/transforms/identifier/movedDeclarations.js +12 -5
  20. package/dist/transforms/identifier/renameVariables.js +7 -6
  21. package/dist/transforms/lock/lock.js +9 -2
  22. package/dist/transforms/minify.js +14 -1
  23. package/dist/transforms/opaquePredicates.js +5 -6
  24. package/dist/transforms/pack.js +5 -0
  25. package/dist/transforms/plugin.js +20 -39
  26. package/dist/transforms/renameLabels.js +1 -2
  27. package/dist/transforms/rgf.js +29 -11
  28. package/dist/transforms/shuffle.js +10 -11
  29. package/dist/transforms/string/stringCompression.js +14 -10
  30. package/dist/transforms/string/stringConcealing.js +4 -4
  31. package/dist/transforms/string/stringEncoding.js +4 -2
  32. package/dist/transforms/string/stringSplitting.js +4 -2
  33. package/dist/transforms/variableMasking.js +1 -2
  34. package/dist/utils/NameGen.js +3 -2
  35. package/dist/utils/PredicateGen.js +62 -0
  36. package/dist/utils/ast-utils.js +24 -9
  37. package/dist/validateOptions.js +2 -2
  38. package/package.json +2 -2
  39. package/src/constants.ts +6 -5
  40. package/src/index.ts +1 -0
  41. package/src/obfuscator.ts +148 -7
  42. package/src/options.ts +14 -6
  43. package/src/order.ts +0 -2
  44. package/src/probability.ts +0 -110
  45. package/src/templates/getGlobalTemplate.ts +5 -1
  46. package/src/templates/stringCompressionTemplate.ts +4 -28
  47. package/src/templates/tamperProtectionTemplates.ts +7 -3
  48. package/src/templates/template.ts +5 -3
  49. package/src/transforms/controlFlowFlattening.ts +2 -7
  50. package/src/transforms/deadCode.ts +11 -23
  51. package/src/transforms/dispatcher.ts +1 -2
  52. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +10 -1
  53. package/src/transforms/extraction/objectExtraction.ts +1 -2
  54. package/src/transforms/finalizer.ts +1 -1
  55. package/src/transforms/flatten.ts +3 -22
  56. package/src/transforms/identifier/globalConcealing.ts +4 -2
  57. package/src/transforms/identifier/movedDeclarations.ts +18 -6
  58. package/src/transforms/identifier/renameVariables.ts +10 -6
  59. package/src/transforms/lock/lock.ts +14 -3
  60. package/src/transforms/minify.ts +24 -2
  61. package/src/transforms/opaquePredicates.ts +5 -8
  62. package/src/transforms/pack.ts +6 -0
  63. package/src/transforms/plugin.ts +47 -69
  64. package/src/transforms/renameLabels.ts +1 -2
  65. package/src/transforms/rgf.ts +39 -14
  66. package/src/transforms/shuffle.ts +28 -26
  67. package/src/transforms/string/encoding.ts +1 -1
  68. package/src/transforms/string/stringCompression.ts +22 -13
  69. package/src/transforms/string/stringConcealing.ts +11 -7
  70. package/src/transforms/string/stringEncoding.ts +6 -2
  71. package/src/transforms/string/stringSplitting.ts +9 -4
  72. package/src/transforms/variableMasking.ts +1 -2
  73. package/src/utils/NameGen.ts +4 -2
  74. package/src/utils/PredicateGen.ts +61 -0
  75. package/src/utils/ast-utils.ts +16 -9
  76. package/src/validateOptions.ts +7 -4
  77. package/src/transforms/functionOutlining.ts +0 -225
  78. package/src/utils/ControlObject.ts +0 -141
@@ -3,7 +3,6 @@ import { PluginArg, PluginObject } from "./plugin";
3
3
  import { Order } from "../order";
4
4
  import * as t from "@babel/types";
5
5
  import Obfuscator from "../obfuscator";
6
- import { computeProbabilityMap } from "../probability";
7
6
  import {
8
7
  append,
9
8
  getFunctionName,
@@ -13,6 +12,7 @@ import {
13
12
  prepend,
14
13
  } from "../utils/ast-utils";
15
14
  import {
15
+ MULTI_TRANSFORM,
16
16
  NodeSymbol,
17
17
  PREDICTABLE,
18
18
  reservedIdentifiers,
@@ -24,6 +24,8 @@ import { numericLiteral } from "../utils/node";
24
24
  import Template from "../templates/template";
25
25
  import { createEvalIntegrityTemplate } from "../templates/tamperProtectionTemplates";
26
26
 
27
+ const RGF_ELIGIBLE = Symbol("rgfEligible");
28
+
27
29
  /**
28
30
  * RGF (Runtime-Generated-Function) uses the `new Function("code")` syntax to create executable code from strings.
29
31
  *
@@ -96,14 +98,21 @@ export default ({ Plugin }: PluginArg): PluginObject => {
96
98
  },
97
99
  },
98
100
  "FunctionDeclaration|FunctionExpression": {
99
- exit(_path) {
101
+ enter(_path) {
100
102
  if (!active) return;
103
+
104
+ // On enter, determine if Function is eligible for RGF transformation
105
+
101
106
  const path = _path as NodePath<
102
107
  t.FunctionDeclaration | t.FunctionExpression
103
108
  >;
104
109
 
105
110
  if (me.isSkipped(path)) return;
106
111
 
112
+ // Skip nested functions if the parent function is already deemed eligible
113
+ if (path.find((p) => p.node[RGF_ELIGIBLE] || p.node[MULTI_TRANSFORM]))
114
+ return;
115
+
107
116
  // Skip async and generator functions
108
117
  if (path.node.async || path.node.generator) return;
109
118
 
@@ -111,10 +120,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
111
120
  if (name === me.options.lock?.countermeasures) return;
112
121
  if (me.obfuscator.isInternalVariable(name)) return;
113
122
 
114
- me.log(name);
115
-
116
123
  if (
117
- !computeProbabilityMap(
124
+ !me.computeProbabilityMap(
118
125
  me.options.rgf,
119
126
  name,
120
127
  path.getFunctionParent() === null
@@ -140,13 +147,14 @@ export default ({ Plugin }: PluginArg): PluginObject => {
140
147
 
141
148
  const binding = idPath.scope.getBinding(name);
142
149
  if (!binding) {
143
- identifierPreventingTransform = name;
144
- idPath.stop();
150
+ // Global variables are allowed
145
151
  return;
146
152
  }
147
153
 
154
+ var isOutsideVariable =
155
+ path.scope.parent.getBinding(name) === binding;
148
156
  // If the binding is not in the current scope, it is an outside reference
149
- if (binding.scope !== path.scope) {
157
+ if (isOutsideVariable) {
150
158
  identifierPreventingTransform = name;
151
159
  idPath.stop();
152
160
  }
@@ -163,9 +171,24 @@ export default ({ Plugin }: PluginArg): PluginObject => {
163
171
  return;
164
172
  }
165
173
 
174
+ me.log("Function " + name + " is eligible for RGF transformation");
175
+ path.node[RGF_ELIGIBLE] = true;
176
+ },
177
+ exit(_path) {
178
+ if (!active) return;
179
+
180
+ const path = _path as NodePath<
181
+ t.FunctionDeclaration | t.FunctionExpression
182
+ >;
183
+
184
+ if (me.isSkipped(path)) return;
185
+
186
+ // Function is not eligible for RGF transformation
187
+ if (!path.node[RGF_ELIGIBLE]) return;
188
+
166
189
  const embeddedName = me.getPlaceholder() + "_embedded";
167
190
  const replacementName = me.getPlaceholder() + "_replacement";
168
- const thisName = me.getPlaceholder() + "_this";
191
+ const argumentsName = me.getPlaceholder() + "_args";
169
192
 
170
193
  const lastNode = t.expressionStatement(t.identifier(embeddedName));
171
194
  (lastNode as NodeSymbol)[SKIP] = true;
@@ -179,10 +202,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
179
202
  t.variableDeclaration("var", [
180
203
  t.variableDeclarator(
181
204
  t.arrayPattern([
182
- t.identifier(thisName),
183
205
  t.identifier(rgfArrayName),
206
+ t.identifier(argumentsName),
184
207
  ]),
185
- t.thisExpression()
208
+ t.identifier("arguments")
186
209
  ),
187
210
  ]),
188
211
  t.functionDeclaration(
@@ -196,7 +219,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
196
219
  t.identifier(replacementName),
197
220
  t.identifier("apply")
198
221
  ),
199
- [t.identifier(thisName), t.identifier("arguments")]
222
+ [t.thisExpression(), t.identifier(argumentsName)]
200
223
  )
201
224
  ),
202
225
  ])
@@ -272,17 +295,19 @@ export default ({ Plugin }: PluginArg): PluginObject => {
272
295
  true
273
296
  ),
274
297
  [
298
+ t.thisExpression(),
275
299
  t.arrayExpression([
276
- t.thisExpression(),
277
300
  t.identifier(rgfArrayName),
301
+ t.identifier("arguments"),
278
302
  ]),
279
- t.identifier("arguments"),
280
303
  ]
281
304
  )
282
305
  ),
283
306
  ])
284
307
  );
285
308
 
309
+ path.skip();
310
+
286
311
  me.setFunctionLength(path, originalLength);
287
312
 
288
313
  me.changeData.functions++;
@@ -1,13 +1,12 @@
1
- import { NodePath } from "@babel/traverse";
2
1
  import { PluginArg, PluginObject } from "./plugin";
3
2
  import * as t from "@babel/types";
4
- import { computeProbabilityMap } from "../probability";
5
3
  import { getRandomInteger } from "../utils/random-utils";
6
4
  import Template from "../templates/template";
7
5
  import { Order } from "../order";
8
6
  import { isStaticValue } from "../utils/static-utils";
9
- import { NodeSymbol, PREDICTABLE } from "../constants";
7
+ import { PREDICTABLE } from "../constants";
10
8
  import { numericLiteral } from "../utils/node";
9
+ import { prependProgram } from "../utils/ast-utils";
11
10
 
12
11
  export default ({ Plugin }: PluginArg): PluginObject => {
13
12
  const me = Plugin(Order.Shuffle, {
@@ -16,6 +15,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
16
15
  },
17
16
  });
18
17
 
18
+ let fnName: string | null = null;
19
+
19
20
  return {
20
21
  visitor: {
21
22
  ArrayExpression: {
@@ -29,10 +30,31 @@ export default ({ Plugin }: PluginArg): PluginObject => {
29
30
 
30
31
  if (illegalElement) return;
31
32
 
32
- if (!computeProbabilityMap(me.options.shuffle)) {
33
+ if (!me.computeProbabilityMap(me.options.shuffle)) {
33
34
  return;
34
35
  }
35
36
 
37
+ // Create un-shuffling function
38
+ if (!fnName) {
39
+ fnName = me.getPlaceholder() + "_shuffle";
40
+
41
+ prependProgram(
42
+ path,
43
+ new Template(
44
+ `
45
+ function ${fnName}(arr, shift) {
46
+ for (var i = 0; i < shift; i++) {
47
+ arr["push"](arr["shift"]());
48
+ }
49
+ return arr;
50
+ }
51
+ `
52
+ )
53
+ .addSymbols(PREDICTABLE)
54
+ .single<t.FunctionDeclaration>()
55
+ );
56
+ }
57
+
36
58
  var shift = getRandomInteger(
37
59
  1,
38
60
  Math.min(30, path.node.elements.length * 6)
@@ -43,30 +65,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
43
65
  shiftedElements.unshift(shiftedElements.pop());
44
66
  }
45
67
 
46
- var block = path.find((p) => p.isBlock()) as NodePath<t.Block>;
47
-
48
- var functionExpression = new Template(
49
- `
50
- (function(arr) {
51
- for (var i = 0; i < {shiftNode}; i++) {
52
- arr.push(arr.shift());
53
- }
54
- return arr;
55
- })
56
- `
57
- ).expression<t.FunctionExpression>({
58
- shiftNode: numericLiteral(shift),
59
- });
60
-
61
- (functionExpression as NodeSymbol)[PREDICTABLE] = true;
62
-
63
- var memberExpression = me
64
- .getControlObject(block)
65
- .addProperty(functionExpression);
66
-
67
68
  path.replaceWith(
68
- t.callExpression(memberExpression, [
69
+ t.callExpression(t.identifier(fnName), [
69
70
  t.arrayExpression(shiftedElements),
71
+ numericLiteral(shift),
70
72
  ])
71
73
  );
72
74
 
@@ -1,6 +1,6 @@
1
1
  import { CustomStringEncoding } from "../../options";
2
2
  import Template from "../../templates/template";
3
- import { choice, shuffle } from "../../utils/random-utils";
3
+ import { shuffle } from "../../utils/random-utils";
4
4
  import * as t from "@babel/types";
5
5
 
6
6
  let hasAllEncodings = false;
@@ -1,20 +1,20 @@
1
1
  import { PluginArg, PluginObject } from "../plugin";
2
2
  import * as t from "@babel/types";
3
3
  import { Order } from "../../order";
4
- import { computeProbabilityMap } from "../../probability";
5
4
  import {
6
5
  ensureComputedExpression,
6
+ isModuleImport,
7
7
  prependProgram,
8
8
  } from "../../utils/ast-utils";
9
9
  import { numericLiteral } from "../../utils/node";
10
10
  import {
11
- PakoInflateMin,
11
+ StringCompressionLibraryMinified,
12
12
  StringCompressionTemplate,
13
13
  } from "../../templates/stringCompressionTemplate";
14
14
  import Obfuscator from "../../obfuscator";
15
15
  import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
16
16
  import { NO_RENAME } from "../../constants";
17
- const pako = require("pako");
17
+ const LZString = require("lz-string");
18
18
 
19
19
  export default ({ Plugin }: PluginArg): PluginObject => {
20
20
  const me = Plugin(Order.StringCompression, {
@@ -23,6 +23,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
23
23
  },
24
24
  });
25
25
 
26
+ // String Compression is only applied to the main obfuscator
27
+ // Any RGF functions will not have string compression due to the size of the decompression function
28
+
26
29
  const stringDelimiter = "|";
27
30
 
28
31
  return {
@@ -36,6 +39,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
36
39
  programPath.traverse({
37
40
  StringLiteral: {
38
41
  exit: (path) => {
42
+ // Don't change module imports
43
+ if (isModuleImport(path)) return;
44
+
39
45
  const originalValue = path.node.value;
40
46
 
41
47
  // Must be at least 3 characters long
@@ -48,7 +54,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
48
54
  if (typeof index === "undefined") {
49
55
  // Allow user option to skip compression for certain strings
50
56
  if (
51
- !computeProbabilityMap(
57
+ !me.computeProbabilityMap(
52
58
  me.options.stringCompression,
53
59
  originalValue
54
60
  )
@@ -81,11 +87,10 @@ export default ({ Plugin }: PluginArg): PluginObject => {
81
87
  );
82
88
 
83
89
  // Compress the string
84
- var compressedBuffer: Uint8Array = pako.deflate(stringPayload);
85
- var compressedBase64 =
86
- Buffer.from(compressedBuffer).toString("base64");
90
+ var compressedString = LZString.compressToUTF16(stringPayload);
87
91
 
88
- let pakoName = me.obfuscator.getStringCompressionLibraryName();
92
+ let stringCompressionLibraryName =
93
+ me.obfuscator.getStringCompressionLibraryName();
89
94
  let insertStringCompressionLibrary = !me.obfuscator.parentObfuscator;
90
95
 
91
96
  prependProgram(
@@ -95,19 +100,23 @@ export default ({ Plugin }: PluginArg): PluginObject => {
95
100
  stringName: me.getPlaceholder(),
96
101
  stringArray: me.getPlaceholder(),
97
102
  stringDelimiter: () => t.stringLiteral(stringDelimiter),
98
- stringValue: () => t.stringLiteral(compressedBase64),
103
+ stringValue: () => t.stringLiteral(compressedString),
99
104
  GetGlobalTemplate: createGetGlobalTemplate(me, programPath),
100
105
  getGlobalFnName: me.getPlaceholder(),
101
- pakoName: pakoName,
106
+ StringCompressionLibrary: stringCompressionLibraryName,
102
107
  })
103
108
  );
104
109
 
105
110
  if (insertStringCompressionLibrary) {
106
- // RGF function should also clone the entire decompression function
111
+ // RGF functions should not clone the entire decompression function
107
112
  prependProgram(
108
113
  programPath,
109
- Obfuscator.parseCode(PakoInflateMin.replace(/{pako}/g, pakoName))
110
- .program.body
114
+ Obfuscator.parseCode(
115
+ StringCompressionLibraryMinified.replace(
116
+ /{StringCompressionLibrary}/g,
117
+ stringCompressionLibraryName
118
+ )
119
+ ).program.body
111
120
  )[0]
112
121
  .get("declarations")[0]
113
122
  .get("id").node[NO_RENAME] = true;
@@ -3,7 +3,6 @@ import { NodePath } from "@babel/traverse";
3
3
  import Template from "../../templates/template";
4
4
  import { PluginArg, PluginObject } from "../plugin";
5
5
  import { Order } from "../../order";
6
- import { computeProbabilityMap } from "../../probability";
7
6
  import { ok } from "assert";
8
7
  import { BufferToStringTemplate } from "../../templates/bufferToStringTemplate";
9
8
  import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
@@ -22,6 +21,7 @@ import {
22
21
  import { CustomStringEncoding } from "../../options";
23
22
  import { createDefaultStringEncoding } from "./encoding";
24
23
  import { numericLiteral } from "../../utils/node";
24
+ import { NO_REMOVE } from "../../constants";
25
25
 
26
26
  interface StringConcealingInterface {
27
27
  encodingImplementation: CustomStringEncoding;
@@ -139,7 +139,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
139
139
 
140
140
  // Check user setting
141
141
  if (
142
- !computeProbabilityMap(
142
+ !me.computeProbabilityMap(
143
143
  me.options.stringConcealing,
144
144
  originalValue
145
145
  )
@@ -271,17 +271,21 @@ export default ({ Plugin }: PluginArg): PluginObject => {
271
271
  ok(encodingImplementation.code instanceof Template);
272
272
 
273
273
  // The decoder function
274
- const decoder = encodingImplementation.code.compile({
275
- fnName: decodeFnName,
276
- __bufferToStringFunction__: bufferToStringName,
277
- });
274
+ const decoder = encodingImplementation.code
275
+ .addSymbols(NO_REMOVE)
276
+ .compile({
277
+ fnName: decodeFnName,
278
+ __bufferToStringFunction__: bufferToStringName,
279
+ });
278
280
 
279
281
  // The main function to get the string value
280
282
  const retrieveFunctionDeclaration = new Template(`
281
283
  function ${fnName}(index) {
282
284
  return ${decodeFnName}(${stringArrayName}[index]);
283
285
  }
284
- `).single<t.FunctionDeclaration>();
286
+ `)
287
+ .addSymbols(NO_REMOVE)
288
+ .single<t.FunctionDeclaration>();
285
289
 
286
290
  prepend(block, [...decoder, retrieveFunctionDeclaration]);
287
291
  }
@@ -1,8 +1,8 @@
1
1
  import { PluginInstance, PluginObject } from "../plugin";
2
2
  import * as t from "@babel/types";
3
3
  import { choice } from "../../utils/random-utils";
4
- import { computeProbabilityMap } from "../../probability";
5
4
  import { GEN_NODE, NodeSymbol } from "../../constants";
5
+ import { isModuleImport } from "../../utils/ast-utils";
6
6
 
7
7
  function pad(x: string, len: number): string {
8
8
  while (x.length < len) {
@@ -51,10 +51,14 @@ export default (me: PluginInstance): PluginObject => {
51
51
  visitor: {
52
52
  StringLiteral: {
53
53
  exit(path) {
54
+ // Ignore module imports
55
+ if (isModuleImport(path)) return;
56
+
54
57
  const { value } = path.node;
55
58
 
56
59
  // Allow percentages
57
- if (!computeProbabilityMap(me.options.stringEncoding, value)) return;
60
+ if (!me.computeProbabilityMap(me.options.stringEncoding, value))
61
+ return;
58
62
 
59
63
  var type = choice(["hexadecimal", "unicode"]);
60
64
 
@@ -1,10 +1,12 @@
1
- import { PluginArg, PluginInstance, PluginObject } from "../plugin";
1
+ import { PluginArg, PluginObject } from "../plugin";
2
2
  import { getRandomInteger, splitIntoChunks } from "../../utils/random-utils";
3
- import { computeProbabilityMap } from "../../probability";
4
3
  import { binaryExpression, stringLiteral } from "@babel/types";
5
4
  import { ok } from "assert";
6
5
  import { Order } from "../../order";
7
- import { ensureComputedExpression } from "../../utils/ast-utils";
6
+ import {
7
+ ensureComputedExpression,
8
+ isModuleImport,
9
+ } from "../../utils/ast-utils";
8
10
 
9
11
  export default ({ Plugin }: PluginArg): PluginObject => {
10
12
  const me = Plugin(Order.StringSplitting, {
@@ -19,6 +21,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
19
21
  exit(path) {
20
22
  var object = path.node;
21
23
 
24
+ // Don't change module imports
25
+ if (isModuleImport(path)) return;
26
+
22
27
  var size = Math.round(
23
28
  Math.max(6, object.value.length / getRandomInteger(3, 8))
24
29
  );
@@ -32,7 +37,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
32
37
  }
33
38
 
34
39
  if (
35
- !computeProbabilityMap(me.options.stringSplitting, object.value)
40
+ !me.computeProbabilityMap(me.options.stringSplitting, object.value)
36
41
  ) {
37
42
  return;
38
43
  }
@@ -2,7 +2,6 @@ import { Binding, NodePath } from "@babel/traverse";
2
2
  import { PluginArg, PluginObject } from "./plugin";
3
3
  import * as t from "@babel/types";
4
4
  import Template from "../templates/template";
5
- import { computeProbabilityMap } from "../probability";
6
5
  import { Order } from "../order";
7
6
  import {
8
7
  NodeSymbol,
@@ -65,7 +64,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
65
64
 
66
65
  const functionName = getFunctionName(fnPath);
67
66
 
68
- if (!computeProbabilityMap(me.options.variableMasking, functionName)) {
67
+ if (!me.computeProbabilityMap(me.options.variableMasking, functionName)) {
69
68
  return;
70
69
  }
71
70
 
@@ -2,8 +2,8 @@ import { ok } from "assert";
2
2
  import { ObfuscateOptions } from "../options";
3
3
  import { alphabeticalGenerator, createZeroWidthGenerator } from "./gen-utils";
4
4
  import { choice, getRandomHexString, getRandomInteger } from "./random-utils";
5
- import { computeProbabilityMap } from "../probability";
6
5
  import { reservedKeywords, reservedObjectPrototype } from "../constants";
6
+ import Obfuscator from "../obfuscator";
7
7
 
8
8
  /**
9
9
  * Generate random names for variables and properties.
@@ -33,7 +33,9 @@ export class NameGen {
33
33
  return value;
34
34
  }
35
35
 
36
- var mode = computeProbabilityMap(this.identifierGenerator);
36
+ var mode = Obfuscator.prototype.computeProbabilityMap(
37
+ this.identifierGenerator
38
+ );
37
39
 
38
40
  const randomizedLength = getRandomInteger(6, 8);
39
41
 
@@ -0,0 +1,61 @@
1
+ import { NodePath } from "@babel/traverse";
2
+ import { PluginInstance } from "../transforms/plugin";
3
+ import * as t from "@babel/types";
4
+ import { prepend } from "./ast-utils";
5
+ import { NameGen } from "./NameGen";
6
+ import Template from "../templates/template";
7
+
8
+ export default class PredicateGen {
9
+ constructor(public plugin: PluginInstance) {}
10
+
11
+ dummyFunctionName: string | null = null;
12
+ programPath: NodePath<t.Program> | null = null;
13
+
14
+ ensureCreated() {
15
+ if (this.dummyFunctionName) return;
16
+
17
+ this.dummyFunctionName = this.plugin.getPlaceholder("dummyFunction");
18
+
19
+ // Insert dummy function
20
+ prepend(
21
+ this.programPath,
22
+
23
+ this.plugin.skip(
24
+ t.functionDeclaration(
25
+ t.identifier(this.dummyFunctionName),
26
+ [],
27
+ t.blockStatement([])
28
+ )
29
+ )
30
+ );
31
+ }
32
+
33
+ generateTrueExpression(path: NodePath): t.Expression {
34
+ return t.unaryExpression("!", this.generateFalseExpression(path));
35
+ }
36
+
37
+ generateFalseExpression(path: NodePath): t.Expression {
38
+ this.programPath = path.find((p) => p.isProgram()) as NodePath<t.Program>;
39
+ this.ensureCreated();
40
+
41
+ // Overcomplicated way to get a random property name that doesn't exist on the Function
42
+ var randomProperty: string;
43
+ var nameGen = new NameGen("randomized");
44
+
45
+ function PrototypeCollision() {}
46
+ PrototypeCollision(); // Call it for code coverage :D
47
+
48
+ do {
49
+ randomProperty = nameGen.generate();
50
+ } while (
51
+ !randomProperty ||
52
+ PrototypeCollision[randomProperty] !== undefined
53
+ );
54
+
55
+ return this.plugin.skip(
56
+ new Template(
57
+ `"${randomProperty}" in ${this.dummyFunctionName}`
58
+ ).expression()
59
+ );
60
+ }
61
+ }
@@ -312,17 +312,21 @@ export function prepend(
312
312
  // Preserve import declarations
313
313
  // Filter out import declarations
314
314
  const body = listParent.get("body");
315
- const lastImportIndex = body.findIndex(
316
- (path) => !path.isImportDeclaration()
317
- );
315
+ let afterImport = 0;
316
+ for (var stmt of body) {
317
+ if (!stmt.isImportDeclaration()) {
318
+ break;
319
+ }
320
+ afterImport++;
321
+ }
318
322
 
319
- if (lastImportIndex === 0 || lastImportIndex === -1) {
320
- // No non-import declarations, so we can safely unshift everything
323
+ if (afterImport === 0) {
324
+ // No import declarations, so we can safely unshift everything
321
325
  return registerPaths(listParent.unshiftContainer("body", nodes));
322
- } else {
323
- // Insert the nodes after the last import declaration
324
- return registerPaths(body[lastImportIndex - 1].insertAfter(nodes));
325
326
  }
327
+
328
+ // Insert the nodes after the last import declaration
329
+ return registerPaths(body[afterImport - 1].insertAfter(nodes));
326
330
  }
327
331
 
328
332
  if (listParent.isFunction()) {
@@ -343,7 +347,9 @@ export function prepend(
343
347
 
344
348
  if (listParent.isBlock()) {
345
349
  return registerPaths(listParent.unshiftContainer("body", nodes));
346
- } else if (listParent.isSwitchCase()) {
350
+ }
351
+
352
+ if (listParent.isSwitchCase()) {
347
353
  return registerPaths(listParent.unshiftContainer("consequent", nodes));
348
354
  }
349
355
 
@@ -356,6 +362,7 @@ export function prependProgram(
356
362
  ) {
357
363
  var program = path.find((p) => p.isProgram());
358
364
  ok(program);
365
+ ok(program.isProgram());
359
366
  return prepend(program, ...nodes);
360
367
  }
361
368
 
@@ -190,10 +190,6 @@ export function applyDefaultsToOptions(
190
190
  "alert",
191
191
  "confirm",
192
192
  "location",
193
- "btoa",
194
- "atob",
195
- "unescape",
196
- "encodeURIComponent",
197
193
  ].forEach((x) => options.globalVariables.add(x));
198
194
  } else {
199
195
  // node
@@ -248,7 +244,14 @@ export function applyDefaultsToOptions(
248
244
  "Uint8Array",
249
245
  "Uint16Array",
250
246
  "Uint32Array",
247
+ "Int8Array",
248
+ "Int16Array",
249
+ "Int32Array",
251
250
  "ArrayBuffer",
251
+ "btoa",
252
+ "atob",
253
+ "unescape",
254
+ "encodeURIComponent",
252
255
  ].forEach((x) => options.globalVariables.add(x));
253
256
  }
254
257