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
@@ -1,16 +1,47 @@
1
- import Template from "../../templates/template";
2
-
3
- const Encoding: {
4
- [encoding_name: string]: {
5
- encode: (s) => string;
6
- decode: (s) => string;
7
- template: ReturnType<typeof Template>;
8
- };
9
- } = {
10
- base91: {
1
+ import Template, { ITemplate } from "../../templates/template";
2
+ import { choice, shuffle } from "../../util/random";
3
+
4
+ /**
5
+ * Defines an encoding implementation the Obfuscator
6
+ */
7
+ export interface EncodingImplementation {
8
+ identity: string;
9
+
10
+ encode(s): string;
11
+ decode(s): string;
12
+ template: ITemplate;
13
+ }
14
+
15
+ let _hasAllEncodings = false;
16
+ export function hasAllEncodings() {
17
+ return _hasAllEncodings;
18
+ }
19
+
20
+ export function createEncodingImplementation(): EncodingImplementation {
21
+ if (_hasAllEncodings) {
22
+ return EncodingImplementations[
23
+ choice(Object.keys(EncodingImplementations))
24
+ ];
25
+ }
26
+
27
+ // create base91 encoding
28
+ let strTable =
29
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"';
30
+
31
+ // shuffle table
32
+ strTable = shuffle(strTable.split("")).join("");
33
+
34
+ let identity = "base91_" + strTable;
35
+
36
+ if (EncodingImplementations.hasOwnProperty(identity)) {
37
+ _hasAllEncodings = true;
38
+ return EncodingImplementations[identity];
39
+ }
40
+
41
+ var encodingImplementation = {
42
+ identity,
11
43
  encode(str) {
12
- const table =
13
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"';
44
+ const table = strTable;
14
45
 
15
46
  const raw = Buffer.from(str, "utf-8");
16
47
  const len = raw.length;
@@ -45,8 +76,7 @@ const Encoding: {
45
76
  return ret;
46
77
  },
47
78
  decode(str) {
48
- const table =
49
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"';
79
+ const table = strTable;
50
80
 
51
81
  const raw = "" + (str || "");
52
82
  const len = raw.length;
@@ -81,45 +111,51 @@ const Encoding: {
81
111
  return Buffer.from(ret).toString("utf-8");
82
112
  },
83
113
  template: Template(`
84
- function {name}(str){
85
- const table =
86
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_\`{|}~"';
87
-
88
- const raw = "" + (str || "");
89
- const len = raw.length;
90
- const ret = [];
91
-
92
- let b = 0;
93
- let n = 0;
94
- let v = -1;
95
-
96
- for (let i = 0; i < len; i++) {
97
- const p = table.indexOf(raw[i]);
98
- if (p === -1) continue;
99
- if (v < 0) {
100
- v = p;
101
- } else {
102
- v += p * 91;
103
- b |= v << n;
104
- n += (v & 8191) > 88 ? 13 : 14;
105
- do {
106
- ret.push(b & 0xff);
107
- b >>= 8;
108
- n -= 8;
109
- } while (n > 7);
110
- v = -1;
114
+ function {__fnName__}(str){
115
+ var table = '${strTable}';
116
+
117
+ var raw = "" + (str || "");
118
+ var len = raw.length;
119
+ var ret = [];
120
+
121
+ var b = 0;
122
+ var n = 0;
123
+ var v = -1;
124
+
125
+ for (var i = 0; i < len; i++) {
126
+ var p = table.indexOf(raw[i]);
127
+ if (p === -1) continue;
128
+ if (v < 0) {
129
+ v = p;
130
+ } else {
131
+ v += p * 91;
132
+ b |= v << n;
133
+ n += (v & 8191) > 88 ? 13 : 14;
134
+ do {
135
+ ret.push(b & 0xff);
136
+ b >>= 8;
137
+ n -= 8;
138
+ } while (n > 7);
139
+ v = -1;
140
+ }
111
141
  }
142
+
143
+ if (v > -1) {
144
+ ret.push((b | (v << n)) & 0xff);
145
+ }
146
+
147
+ return {__bufferToString__}(ret);
112
148
  }
149
+ `).ignoreMissingVariables(),
150
+ };
113
151
 
114
- if (v > -1) {
115
- ret.push((b | (v << n)) & 0xff);
116
- }
117
-
118
- return {bufferToString}(ret);
119
- }
120
- `),
121
- },
152
+ EncodingImplementations[identity] = encodingImplementation;
153
+ return encodingImplementation;
154
+ }
122
155
 
156
+ export const EncodingImplementations: {
157
+ [encodingIdentity: string]: EncodingImplementation;
158
+ } = {
123
159
  /* ascii85: { This implementation is flaky and causes decoding errors
124
160
  encode(a) {
125
161
  var b, c, d, e, f, g, h, i, j, k;
@@ -209,5 +245,3 @@ const Encoding: {
209
245
  `),
210
246
  }, */
211
247
  };
212
-
213
- export default Encoding;
@@ -2,7 +2,7 @@ import { ok } from "assert";
2
2
  import { ObfuscateOrder } from "../../order";
3
3
  import { ComputeProbabilityMap } from "../../probability";
4
4
  import Template from "../../templates/template";
5
- import { isDirective } from "../../util/compare";
5
+ import { isDirective, isModuleSource } from "../../util/compare";
6
6
  import {
7
7
  CallExpression,
8
8
  FunctionDeclaration,
@@ -16,8 +16,8 @@ import {
16
16
  } from "../../util/gen";
17
17
  import { append, prepend } from "../../util/insert";
18
18
  import Transform from "../transform";
19
- import { isModuleSource } from "./stringConcealing";
20
- import { chance } from "../../util/random";
19
+ import { predictableFunctionTag } from "../../constants";
20
+
21
21
  function LZ_encode(c) {
22
22
  ok(c);
23
23
  var x = "charCodeAt",
@@ -94,7 +94,7 @@ export default class StringCompression extends Transform {
94
94
  this.map = new Map();
95
95
  this.ignore = new Set();
96
96
  this.string = "";
97
- this.fnName = this.getPlaceholder();
97
+ this.fnName = this.getPlaceholder() + predictableFunctionTag;
98
98
  }
99
99
 
100
100
  apply(tree) {
@@ -107,7 +107,7 @@ export default class StringCompression extends Transform {
107
107
 
108
108
  var split = this.getPlaceholder();
109
109
  var decoder = this.getPlaceholder();
110
- var getStringName = this.getPlaceholder();
110
+ var getStringName = this.getPlaceholder() + predictableFunctionTag; // Returns the string payload
111
111
 
112
112
  var encoded = LZ_encode(this.string);
113
113
  if (LZ_decode(encoded) !== this.string) {
@@ -210,9 +210,6 @@ export default class StringCompression extends Transform {
210
210
  return;
211
211
  }
212
212
 
213
- // HARD CODED LIMIT of 10,000 (after 1,000 elements)
214
- if (this.map.size > 1000 && !chance(this.map.size / 100)) return;
215
-
216
213
  var index = this.map.get(object.value);
217
214
 
218
215
  // New string, add it!
@@ -1,12 +1,11 @@
1
1
  import { ok } from "assert";
2
2
  import { ObfuscateOrder } from "../../order";
3
3
  import Template from "../../templates/template";
4
- import { isBlock } from "../../traverse";
5
- import { isDirective } from "../../util/compare";
4
+ import { getBlock } from "../../traverse";
5
+ import { isDirective, isModuleSource } from "../../util/compare";
6
6
  import {
7
7
  ArrayExpression,
8
8
  CallExpression,
9
- FunctionExpression,
10
9
  Identifier,
11
10
  Literal,
12
11
  MemberExpression,
@@ -22,54 +21,36 @@ import {
22
21
  choice,
23
22
  getRandomInteger,
24
23
  getRandomString,
24
+ shuffle,
25
25
  } from "../../util/random";
26
26
  import Transform from "../transform";
27
- import Encoding from "./encoding";
27
+ import {
28
+ EncodingImplementation,
29
+ EncodingImplementations,
30
+ createEncodingImplementation,
31
+ hasAllEncodings,
32
+ } from "./encoding";
28
33
  import { ComputeProbabilityMap } from "../../probability";
29
34
  import { BufferToStringTemplate } from "../../templates/bufferToString";
35
+ import { criticalFunctionTag, predictableFunctionTag } from "../../constants";
30
36
 
31
- export function isModuleSource(object: Node, parents: Node[]) {
32
- if (!parents[0]) {
33
- return false;
34
- }
35
-
36
- if (parents[0].type == "ImportDeclaration" && parents[0].source == object) {
37
- return true;
38
- }
39
-
40
- if (parents[0].type == "ImportExpression" && parents[0].source == object) {
41
- return true;
42
- }
43
-
44
- if (
45
- parents[1] &&
46
- parents[1].type == "CallExpression" &&
47
- parents[1].arguments[0] === object &&
48
- parents[1].callee.type == "Identifier"
49
- ) {
50
- if (
51
- parents[1].callee.name == "require" ||
52
- parents[1].callee.name == "import"
53
- ) {
54
- return true;
55
- }
56
- }
57
-
58
- return false;
37
+ interface FunctionObject {
38
+ block: Node;
39
+ fnName: string;
40
+ encodingImplementation: EncodingImplementation;
59
41
  }
60
42
 
61
43
  export default class StringConcealing extends Transform {
62
44
  arrayExpression: Node;
63
45
  set: Set<string>;
64
- index: { [str: string]: [number, string] };
46
+ index: { [str: string]: [number, string, Node] }; // index, fnName, block
65
47
 
66
48
  arrayName = this.getPlaceholder();
67
49
  ignore = new Set<string>();
68
50
  variablesMade = 1;
69
- encoding: { [type: string]: string } = Object.create(null);
70
51
  gen: ReturnType<Transform["getGenerator"]>;
71
52
 
72
- hasAllEncodings: boolean;
53
+ functionObjects: FunctionObject[] = [];
73
54
 
74
55
  constructor(o) {
75
56
  super(o, ObfuscateOrder.StringConcealing);
@@ -77,84 +58,110 @@ export default class StringConcealing extends Transform {
77
58
  this.set = new Set();
78
59
  this.index = Object.create(null);
79
60
  this.arrayExpression = ArrayExpression([]);
80
- this.hasAllEncodings = false;
81
61
  this.gen = this.getGenerator();
62
+ }
63
+
64
+ apply(tree) {
65
+ super.apply(tree);
82
66
 
83
67
  // Pad array with useless strings
84
68
  var dead = getRandomInteger(5, 15);
85
69
  for (var i = 0; i < dead; i++) {
86
70
  var str = getRandomString(getRandomInteger(5, 40));
87
- var fn = this.transform(Literal(str), []);
71
+ var fn = this.transform(Literal(str), [tree]);
88
72
  if (fn) {
89
73
  fn();
90
74
  }
91
75
  }
92
- }
93
-
94
- apply(tree) {
95
- super.apply(tree);
96
76
 
97
77
  var cacheName = this.getPlaceholder();
98
- var bufferToStringName = this.getPlaceholder();
78
+ var bufferToStringName = this.getPlaceholder() + predictableFunctionTag;
99
79
 
100
80
  // This helper functions convert UInt8 Array to UTf-string
101
81
  prepend(
102
82
  tree,
103
- ...BufferToStringTemplate.compile({ name: bufferToStringName })
83
+ ...BufferToStringTemplate.compile({
84
+ name: bufferToStringName,
85
+ getGlobalFnName: this.getPlaceholder() + predictableFunctionTag,
86
+ })
104
87
  );
105
88
 
106
- Object.keys(this.encoding).forEach((type) => {
107
- var { template } = Encoding[type];
108
- var decodeFn = this.getPlaceholder();
109
- var getterFn = this.encoding[type];
89
+ for (var functionObject of this.functionObjects) {
90
+ var {
91
+ block,
92
+ fnName: getterFnName,
93
+ encodingImplementation,
94
+ } = functionObject;
95
+
96
+ var decodeFn =
97
+ this.getPlaceholder() + predictableFunctionTag + criticalFunctionTag;
110
98
 
111
99
  append(
112
- tree,
113
- template.single({ name: decodeFn, bufferToString: bufferToStringName })
100
+ block,
101
+ encodingImplementation.template.single({
102
+ __fnName__: decodeFn,
103
+ __bufferToString__: bufferToStringName,
104
+ })
114
105
  );
106
+ // All these are fake and never ran
107
+ var ifStatements = Template(`if ( z == x ) {
108
+ return y[${cacheName}[z]] = ${getterFnName}(x, y);
109
+ }
110
+ if ( y ) {
111
+ [b, y] = [a(b), x || z]
112
+ return ${getterFnName}(x, b, z)
113
+ }
114
+ if ( z && a !== ${decodeFn} ) {
115
+ ${getterFnName} = ${decodeFn}
116
+ return ${getterFnName}(x, -1, z, a, b)
117
+ }
118
+ if ( a === ${getterFnName} ) {
119
+ ${decodeFn} = y
120
+ return ${decodeFn}(z)
121
+ }
122
+ if( a === undefined ) {
123
+ ${getterFnName} = b
124
+ }
125
+ if( z == a ) {
126
+ return y ? x[b[y]] : ${cacheName}[x] || (z=(b[x] || a), ${cacheName}[x] = z(${this.arrayName}[x]))
127
+ }
128
+ `).compile();
115
129
 
116
- append(
117
- tree,
130
+ // Not all fake if-statements are needed
131
+ ifStatements = ifStatements.filter(() => chance(50));
132
+
133
+ // This one is always used
134
+ ifStatements.push(
118
135
  Template(`
119
-
120
- function ${getterFn}(x, y, z, a = ${decodeFn}, b = ${cacheName}){
121
- if ( z ) {
122
- return y[${cacheName}[z]] = ${getterFn}(x, y);
123
- } else if ( y ) {
124
- [b, y] = [a(b), x || z]
125
- }
126
-
127
- return y ? x[b[y]] : ${cacheName}[x] || (z=(b[x], a), ${cacheName}[x] = z(${this.arrayName}[x]))
128
- }
129
-
130
- `).single()
136
+ if ( x !== y ) {
137
+ return b[x] || (b[x] = a(${this.arrayName}[x]))
138
+ }
139
+ `).single()
131
140
  );
132
- });
133
141
 
134
- var flowIntegrity = this.getPlaceholder();
142
+ shuffle(ifStatements);
143
+
144
+ var varDeclaration = Template(`
145
+ var ${getterFnName} = (x, y, z, a, b)=>{
146
+ if(typeof a === "undefined") {
147
+ a = ${decodeFn}
148
+ }
149
+ if(typeof b === "undefined") {
150
+ b = ${cacheName}
151
+ }
152
+ }
153
+ `).single();
154
+
155
+ varDeclaration.declarations[0].init.body.body.push(...ifStatements);
156
+
157
+ prepend(block, varDeclaration);
158
+ }
135
159
 
136
160
  prepend(
137
161
  tree,
138
162
  VariableDeclaration([
139
163
  VariableDeclarator(cacheName, ArrayExpression([])),
140
- VariableDeclarator(flowIntegrity, Literal(0)),
141
- VariableDeclarator(
142
- this.arrayName,
143
- CallExpression(
144
- FunctionExpression(
145
- [],
146
- [
147
- VariableDeclaration(
148
- VariableDeclarator("a", this.arrayExpression)
149
- ),
150
- Template(
151
- `return (${flowIntegrity} ? a["pop"]() : ${flowIntegrity}++, a)`
152
- ).single(),
153
- ]
154
- ),
155
- []
156
- )
157
- ),
164
+ VariableDeclarator(this.arrayName, this.arrayExpression),
158
165
  ])
159
166
  );
160
167
  }
@@ -192,48 +199,67 @@ export default class StringConcealing extends Transform {
192
199
  return;
193
200
  }
194
201
 
195
- // HARD CODED LIMIT of 10,000 (after 1,000 elements)
196
- if (this.set.size > 1000 && !chance(this.set.size / 100)) return;
202
+ var currentBlock = getBlock(object, parents);
197
203
 
198
- var types = Object.keys(this.encoding);
204
+ // Find created functions
205
+ var functionObjects: FunctionObject[] = parents
206
+ .filter((node) => node.$stringConcealingFunctionObject)
207
+ .map((item) => item.$stringConcealingFunctionObject);
199
208
 
200
- var type = choice(types);
201
- if (!type || (!this.hasAllEncodings && chance(10))) {
202
- var allowed = Object.keys(Encoding).filter(
203
- (type) => !this.encoding[type]
204
- );
209
+ // Choose random functionObject to use
210
+ var functionObject = choice(functionObjects);
211
+
212
+ if (
213
+ !functionObject ||
214
+ (!hasAllEncodings() &&
215
+ chance(25 / this.functionObjects.length) &&
216
+ !currentBlock.$stringConcealingFunctionObject)
217
+ ) {
218
+ // No functions, create one
205
219
 
206
- if (!allowed.length) {
207
- this.hasAllEncodings = true;
208
- } else {
209
- var random = choice(allowed);
210
- type = random;
220
+ var newFunctionObject: FunctionObject = {
221
+ block: currentBlock,
222
+ encodingImplementation: createEncodingImplementation(),
223
+ fnName: this.getPlaceholder() + predictableFunctionTag,
224
+ };
211
225
 
212
- this.encoding[random] = this.getPlaceholder();
213
- }
226
+ this.functionObjects.push(newFunctionObject);
227
+ currentBlock.$stringConcealingFunctionObject = newFunctionObject;
228
+ functionObject = newFunctionObject;
214
229
  }
215
230
 
216
- var fnName = this.encoding[type];
217
- var encoder = Encoding[type];
231
+ var { fnName, encodingImplementation } = functionObject;
218
232
 
219
- // The decode function must return correct result
220
- var encoded = encoder.encode(object.value);
221
- if (encoder.decode(encoded) != object.value) {
222
- this.ignore.add(object.value);
223
- this.warn(type, object.value.slice(0, 100));
224
- return;
233
+ var index = -1;
234
+
235
+ // String already decoded?
236
+ if (this.set.has(object.value)) {
237
+ var row = this.index[object.value];
238
+ if (parents.includes(row[2])) {
239
+ [index, fnName] = row;
240
+ ok(typeof index === "number");
241
+ }
225
242
  }
226
243
 
227
- var index = -1;
228
- if (!this.set.has(object.value)) {
244
+ if (index == -1) {
245
+ // The decode function must return correct result
246
+ var encoded = encodingImplementation.encode(object.value);
247
+ if (encodingImplementation.decode(encoded) !== object.value) {
248
+ this.ignore.add(object.value);
249
+ this.warn(
250
+ encodingImplementation.identity,
251
+ object.value.slice(0, 100)
252
+ );
253
+ delete EncodingImplementations[encodingImplementation.identity];
254
+
255
+ return;
256
+ }
257
+
229
258
  this.arrayExpression.elements.push(Literal(encoded));
230
259
  index = this.arrayExpression.elements.length - 1;
231
- this.index[object.value] = [index, fnName];
260
+ this.index[object.value] = [index, fnName, currentBlock];
232
261
 
233
262
  this.set.add(object.value);
234
- } else {
235
- [index, fnName] = this.index[object.value];
236
- ok(typeof index === "number");
237
263
  }
238
264
 
239
265
  ok(index != -1, "index == -1");
@@ -269,7 +295,7 @@ export default class StringConcealing extends Transform {
269
295
 
270
296
  var constantReferenceType = choice(["variable", "array", "object"]);
271
297
 
272
- var place = choice(parents.filter((node) => isBlock(node)));
298
+ var place = currentBlock;
273
299
  if (!place) {
274
300
  this.error(new Error("No lexical block to insert code"));
275
301
  }
@@ -1,7 +1,6 @@
1
1
  import Transform from "../transform";
2
2
  import { choice } from "../../util/random";
3
- import { isDirective } from "../../util/compare";
4
- import { isModuleSource } from "./stringConcealing";
3
+ import { isDirective, isModuleSource } from "../../util/compare";
5
4
  import { ComputeProbabilityMap } from "../../probability";
6
5
  import { Identifier } from "../../util/gen";
7
6
 
@@ -3,8 +3,7 @@ import { Node, Literal, BinaryExpression } from "../../util/gen";
3
3
  import { clone } from "../../util/insert";
4
4
  import { getRandomInteger, shuffle, splitIntoChunks } from "../../util/random";
5
5
  import { ObfuscateOrder } from "../../order";
6
- import { isModuleSource } from "./stringConcealing";
7
- import { isDirective } from "../../util/compare";
6
+ import { isDirective, isModuleSource } from "../../util/compare";
8
7
  import { ok } from "assert";
9
8
  import { ComputeProbabilityMap } from "../../probability";
10
9
 
@@ -1,5 +1,10 @@
1
1
  import traverse, { ExitCallback } from "../traverse";
2
- import { AddComment, Node } from "../util/gen";
2
+ import {
3
+ AddComment,
4
+ Node,
5
+ VariableDeclaration,
6
+ VariableDeclarator,
7
+ } from "../util/gen";
3
8
  import {
4
9
  alphabeticalGenerator,
5
10
  choice,
@@ -15,6 +20,8 @@ import {
15
20
  reservedKeywords,
16
21
  } from "../constants";
17
22
  import { ObfuscateOrder } from "../order";
23
+ import { ITemplate } from "../templates/template";
24
+ import { prepend } from "../util/insert";
18
25
 
19
26
  /**
20
27
  * Base-class for all transformations.
@@ -78,6 +85,8 @@ export default class Transform {
78
85
  */
79
86
  after: Transform[];
80
87
 
88
+ initVariables = new Map<string, string>();
89
+
81
90
  constructor(obfuscator, priority: number = -1) {
82
91
  ok(obfuscator instanceof Obfuscator, "obfuscator should be an Obfuscator");
83
92
 
@@ -336,6 +345,26 @@ export default class Transform {
336
345
  return identifier;
337
346
  }
338
347
 
348
+ createInitVariable = (value: ITemplate, parents: Node[]) => {
349
+ var key = value.templates[0];
350
+ if (this.initVariables.has(key)) {
351
+ return this.initVariables.get(key);
352
+ }
353
+
354
+ var root = parents[parents.length - 1];
355
+ ok(root.type === "Program");
356
+
357
+ var name = this.getPlaceholder();
358
+ this.initVariables.set(key, name);
359
+
360
+ prepend(
361
+ root,
362
+ VariableDeclaration(VariableDeclarator(name, value.single().expression))
363
+ );
364
+
365
+ return name;
366
+ };
367
+
339
368
  /**
340
369
  * Smartly appends a comment to a Node.
341
370
  * - Includes the transformation's name.