js-confuser 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +215 -170
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +4 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +89 -58
  11. package/dist/transforms/flatten.js +224 -147
  12. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  13. package/dist/transforms/identifier/renameVariables.js +94 -41
  14. package/dist/transforms/lock/lock.js +0 -37
  15. package/dist/transforms/minify.js +2 -2
  16. package/dist/transforms/rgf.js +139 -246
  17. package/dist/transforms/stack.js +42 -1
  18. package/dist/transforms/transform.js +1 -1
  19. package/dist/util/gen.js +2 -1
  20. package/dist/util/identifiers.js +37 -3
  21. package/dist/util/insert.js +24 -3
  22. package/docs/ControlFlowFlattening.md +595 -0
  23. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  24. package/{Integrity.md → docs/Integrity.md} +2 -2
  25. package/docs/RGF.md +419 -0
  26. package/package.json +1 -1
  27. package/src/constants.ts +3 -0
  28. package/src/obfuscator.ts +0 -4
  29. package/src/options.ts +9 -86
  30. package/src/presets.ts +6 -7
  31. package/src/templates/crash.ts +10 -10
  32. package/src/templates/functionLength.ts +14 -0
  33. package/src/transforms/dispatcher.ts +5 -1
  34. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +130 -129
  35. package/src/transforms/flatten.ts +357 -290
  36. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  37. package/src/transforms/identifier/renameVariables.ts +120 -56
  38. package/src/transforms/lock/lock.ts +1 -42
  39. package/src/transforms/minify.ts +11 -2
  40. package/src/transforms/rgf.ts +214 -404
  41. package/src/transforms/stack.ts +62 -0
  42. package/src/transforms/transform.ts +6 -2
  43. package/src/util/gen.ts +7 -2
  44. package/src/util/identifiers.ts +43 -2
  45. package/src/util/insert.ts +26 -2
  46. package/test/code/ES6.src.js +24 -0
  47. package/test/transforms/flatten.test.ts +352 -88
  48. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  49. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  50. package/test/transforms/lock/lock.test.ts +1 -48
  51. package/test/transforms/minify.test.ts +19 -0
  52. package/test/transforms/rgf.test.ts +262 -353
  53. package/test/transforms/stack.test.ts +52 -0
  54. package/test/util/identifiers.test.ts +113 -1
  55. package/test/util/insert.test.ts +57 -3
  56. package/src/transforms/eval.ts +0 -89
  57. package/src/transforms/identifier/nameRecycling.ts +0 -280
  58. package/test/transforms/eval.test.ts +0 -131
  59. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
package/dist/presets.js CHANGED
@@ -22,7 +22,7 @@ exports.default = void 0;
22
22
  * 10. Minified output
23
23
  *
24
24
  * ## **`Disabled features`**
25
- * - `eval` Use at your own risk!
25
+ * - `rgf` Use at your own risk!
26
26
  *
27
27
  * ### Potential Issues
28
28
  * 1. *String Encoding* can corrupt files. Disable `stringEncoding` manually if this happens.
@@ -57,7 +57,6 @@ const highPreset = {
57
57
  stringEncoding: true,
58
58
  stringSplitting: 0.75,
59
59
  // Use at own risk
60
- eval: false,
61
60
  rgf: false
62
61
  };
63
62
  /**
@@ -71,9 +70,9 @@ const mediumPreset = {
71
70
  calculator: true,
72
71
  compact: true,
73
72
  hexadecimalNumbers: true,
74
- controlFlowFlattening: 0.5,
73
+ controlFlowFlattening: 0.25,
75
74
  deadCode: 0.025,
76
- dispatcher: 0.75,
75
+ dispatcher: 0.5,
77
76
  duplicateLiteralsRemoval: 0.5,
78
77
  globalConcealing: true,
79
78
  identifierGenerator: "randomized",
@@ -99,10 +98,10 @@ const lowPreset = {
99
98
  calculator: true,
100
99
  compact: true,
101
100
  hexadecimalNumbers: true,
102
- controlFlowFlattening: 0.25,
101
+ controlFlowFlattening: 0.1,
103
102
  deadCode: 0.01,
104
- dispatcher: 0.5,
105
- duplicateLiteralsRemoval: true,
103
+ dispatcher: 0.25,
104
+ duplicateLiteralsRemoval: 0.5,
106
105
  identifierGenerator: "randomized",
107
106
  minify: true,
108
107
  movedDeclarations: true,
@@ -9,9 +9,9 @@ var _template = _interopRequireDefault(require("./template"));
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
12
- const CrashTemplate1 = (0, _template.default)("\nvar {var} = \"a\";\nwhile(1){\n {var} = {var} += \"a\"; //add as much as the browser can handle\n}\n");
12
+ const CrashTemplate1 = (0, _template.default)("\nvar {var} = \"a\";\nwhile(1){\n {var} = {var} += \"a\";\n}\n");
13
13
  exports.CrashTemplate1 = CrashTemplate1;
14
14
  const CrashTemplate2 = (0, _template.default)("\nwhile(true) {\n var {var} = 99;\n for({var} = 99; {var} == {var}; {var} *= {var}) {\n !{var} && console.log({var});\n if ({var} <= 10){\n break;\n }\n };\n if({var} === 100) {\n {var}--\n }\n };");
15
15
  exports.CrashTemplate2 = CrashTemplate2;
16
- const CrashTemplate3 = (0, _template.default)("\ntry {\n function {$2}(y, x){\n return x;\n }\n \n var {$1} = {$2}(this, function () {\n var {$3} = function () {\n var regExp = {$3}\n .constructor('return /\" + this + \"/')()\n .constructor('^([^ ]+( +[^ ]+)+)+[^ ]}');\n \n return !regExp.call({$1});\n };\n \n return {$3}();\n });\n \n {$1}();\n} catch ( e ) {\n while(e ? e : !e){\n var b;\n var c = 0;\n (e ? !e : e) ? (function(e){\n c = e ? 0 : !e ? 1 : 0;\n })(e) : b = 1;\n\n if(b&&c){break;}\n if(b){continue;}\n }\n}\n");
16
+ const CrashTemplate3 = (0, _template.default)("\ntry {\n function {$2}(y, x){\n return x;\n }\n \n var {$1} = {$2}(this, function () {\n var {$3} = function () {\n var regExp = {$3}\n .constructor('return /\" + this + \"/')()\n .constructor('^([^ ]+( +[^ ]+)+)+[^ ]}');\n \n return !regExp.call({$1});\n };\n \n return {$3}();\n });\n \n {$1}();\n} catch ( {$1}e ) {\n while({$1}e ? {$1}e : !{$1}e){\n var {$1}b;\n var {$1}c = 0;\n ({$1}e ? !{$1}e : {$1}e) ? (function({$1}e){\n {$1}c = {$1}e ? 0 : !{$1}e ? 1 : 0;\n })({$1}e) : {$1}b = 1;\n\n if({$1}b&&{$1}c){break;}\n if({$1}b){continue;}\n }\n}\n");
17
17
  exports.CrashTemplate3 = CrashTemplate3;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FunctionLengthTemplate = void 0;
7
+
8
+ var _template = _interopRequireDefault(require("./template"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ /**
13
+ * Helper function to set `function.length` property.
14
+ */
15
+ const FunctionLengthTemplate = (0, _template.default)("\nfunction {name}(functionObject, functionLength){\n Object[\"defineProperty\"](functionObject, \"length\", {\n \"value\": functionLength,\n \"configurable\": true\n });\n return functionObject;\n}\n");
16
+ exports.FunctionLengthTemplate = FunctionLengthTemplate;
@@ -56,9 +56,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
56
56
  * 3. using `this`
57
57
  */
58
58
  class Dispatcher extends _transform.default {
59
+ // Debug mode preserves function names
59
60
  constructor(o) {
60
61
  super(o, _order.ObfuscateOrder.Dispatcher);
61
62
 
63
+ _defineProperty(this, "isDebug", false);
64
+
62
65
  _defineProperty(this, "count", void 0);
63
66
 
64
67
  this.count = 0;
@@ -158,7 +161,7 @@ class Dispatcher extends _transform.default {
158
161
 
159
162
  var gen = this.getGenerator();
160
163
  Object.keys(functionDeclarations).forEach(name => {
161
- newFnNames[name] = gen.generate();
164
+ newFnNames[name] = this.isDebug ? "_dispatcher_" + this.count + "_" + name : gen.generate();
162
165
  }); // set containing new name
163
166
 
164
167
  var set = new Set(Object.keys(newFnNames)); // Only make a dispatcher function if it caught any functions
@@ -23,6 +23,8 @@ var _assert = require("assert");
23
23
 
24
24
  var _random = require("../../util/random");
25
25
 
26
+ var _traverse = require("../../traverse");
27
+
26
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
29
 
28
30
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -46,12 +48,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
46
48
  * ```
47
49
  */
48
50
  class DuplicateLiteralsRemoval extends _transform.default {
51
+ // The array holding all the duplicate literals
52
+ // The array expression node to be inserted into the program
53
+
54
+ /**
55
+ * Literals in the array
56
+ */
57
+
49
58
  /**
50
- * getter fn name -> accumulative shift
59
+ * Literals are saved here the first time they are seen.
51
60
  */
52
61
 
53
62
  /**
54
- * lex context -> getter fn name
63
+ * Block -> { functionName, indexShift }
55
64
  */
56
65
  constructor(o) {
57
66
  super(o, _order.ObfuscateOrder.DuplicateLiteralsRemoval);
@@ -64,23 +73,45 @@ class DuplicateLiteralsRemoval extends _transform.default {
64
73
 
65
74
  _defineProperty(this, "first", void 0);
66
75
 
67
- _defineProperty(this, "fnShifts", void 0);
68
-
69
- _defineProperty(this, "fnGetters", void 0);
76
+ _defineProperty(this, "functions", void 0);
70
77
 
71
78
  this.map = new Map();
72
79
  this.first = new Map();
73
- this.fnShifts = new Map();
74
- this.fnGetters = new Map();
80
+ this.functions = new Map();
75
81
  }
76
82
 
77
83
  apply(tree) {
78
84
  super.apply(tree);
79
85
 
80
- if (this.arrayName && this.arrayExpression.elements.length) {
86
+ if (this.arrayName && this.arrayExpression.elements.length > 0) {
87
+ // This function simply returns the array
81
88
  var getArrayFn = this.getPlaceholder();
82
- (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)]));
83
- (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)()]))));
89
+ (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)])); // This variable holds the array
90
+
91
+ (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Literal)("call"), true), [(0, _gen.ThisExpression)()])))); // Create all the functions needed
92
+
93
+ for (var blockNode of this.functions.keys()) {
94
+ var {
95
+ functionName,
96
+ indexShift
97
+ } = this.functions.get(blockNode);
98
+ var propertyNode = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(indexShift));
99
+ var indexRangeInclusive = [0 + indexShift - 1, this.map.size + indexShift]; // The function uses mangling to hide the index being accessed
100
+
101
+ var mangleCount = (0, _random.getRandomInteger)(1, 10);
102
+
103
+ for (var i = 0; i < mangleCount; i++) {
104
+ var operator = (0, _random.choice)([">", "<"]);
105
+ var compareValue = (0, _random.choice)(indexRangeInclusive);
106
+ var test = (0, _gen.BinaryExpression)(operator, (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(compareValue));
107
+ var alternate = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100)));
108
+ var testValue = operator === ">" && compareValue === indexRangeInclusive[0] || operator === "<" && compareValue === indexRangeInclusive[1];
109
+ propertyNode = (0, _gen.ConditionalExpression)(test, testValue ? propertyNode : alternate, !testValue ? propertyNode : alternate);
110
+ }
111
+
112
+ var returnArgument = (0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), propertyNode, true);
113
+ (0, _insert.prepend)(blockNode, (0, _gen.FunctionDeclaration)(functionName, [(0, _gen.Identifier)("index_param")], [(0, _gen.ReturnStatement)(returnArgument)]));
114
+ }
84
115
  }
85
116
  }
86
117
 
@@ -95,43 +126,39 @@ class DuplicateLiteralsRemoval extends _transform.default {
95
126
  */
96
127
 
97
128
 
98
- toCaller(object, parents, index) {
99
- // get all the getters defined here or higher
100
- var getterNames = [object, ...parents].map(x => this.fnGetters.get(x)).filter(x => x); // use random getter function
101
-
102
- var getterName = (0, _random.choice)(getterNames); // get this literals context
129
+ transformLiteral(object, parents, index) {
130
+ var blockNode = (0, _random.choice)(parents.filter(x => this.functions.has(x))); // Create initial function if none exist
103
131
 
104
- var lexContext = (0, _insert.getLexContext)(object, parents);
105
- var hasGetterHere = this.fnGetters.has(lexContext); // create one if none are available (or by random chance if none are here locally)
132
+ if (this.functions.size === 0) {
133
+ var root = parents[parents.length - 1];
134
+ var rootFunctionName = this.getPlaceholder() + "_dLR_0";
135
+ this.functions.set(root, {
136
+ functionName: rootFunctionName,
137
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
138
+ });
139
+ blockNode = root;
140
+ } // If no function here exist, possibly create new chained function
106
141
 
107
- var shouldCreateNew = !getterName || !hasGetterHere && Math.random() > 0.9;
108
142
 
109
- if (shouldCreateNew) {
110
- (0, _assert.ok)(!this.fnGetters.has(lexContext));
111
- var lexContextIndex = parents.findIndex(x => x !== lexContext && (0, _insert.isLexContext)(x));
112
- var basedOn = lexContextIndex !== -1 ? (0, _random.choice)(parents.slice(lexContextIndex + 1).map(x => this.fnGetters.get(x)).filter(x => x)) : null;
113
- var body = [];
114
- var thisShift = (0, _random.getRandomInteger)(-250, 250); // the name of the getter
143
+ var block = (0, _traverse.getBlock)(object, parents);
115
144
 
116
- getterName = this.getPlaceholder() + "_dLR_" + this.fnGetters.size;
145
+ if (!this.functions.has(block) && (0, _random.chance)(50 - this.functions.size)) {
146
+ var newFunctionName = this.getPlaceholder() + "_dLR_" + this.functions.size;
147
+ this.functions.set(block, {
148
+ functionName: newFunctionName,
149
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
150
+ });
151
+ blockNode = block;
152
+ } // Derive the function to call from the selected blockNode
117
153
 
118
- if (basedOn) {
119
- var shift = this.fnShifts.get(basedOn);
120
- (0, _assert.ok)(typeof shift === "number");
121
- body = [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(basedOn), [(0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift))]))];
122
- this.fnShifts.set(getterName, shift + thisShift);
123
- } else {
124
- // from scratch
125
- body = [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift)), true))];
126
- this.fnShifts.set(getterName, thisShift);
127
- }
128
154
 
129
- this.fnGetters.set(lexContext, getterName);
130
- (0, _insert.prepend)(lexContext, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(getterName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.FunctionExpression)([(0, _gen.Identifier)("index")], body))]), []))));
131
- }
155
+ var {
156
+ functionName,
157
+ indexShift
158
+ } = this.functions.get(blockNode); // Call the function given it's indexShift
132
159
 
133
- var theShift = this.fnShifts.get(getterName);
134
- this.replaceIdentifierOrLiteral(object, (0, _gen.CallExpression)((0, _gen.Identifier)(getterName), [(0, _gen.Literal)(index - theShift)]), parents);
160
+ var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(functionName), [(0, _gen.Literal)(index + indexShift)]);
161
+ this.replaceIdentifierOrLiteral(object, callExpression, parents);
135
162
  }
136
163
 
137
164
  transform(object, parents) {
@@ -147,19 +174,19 @@ class DuplicateLiteralsRemoval extends _transform.default {
147
174
  } // HARD CODED LIMIT of 10,000 (after 1,000 elements)
148
175
 
149
176
 
150
- if (this.map.size > 1000 && !(0, _random.chance)(this.map.size / 100)) return;
177
+ if (this.map.size > 1000 && (0, _random.chance)(this.map.size / 100)) return;
151
178
 
152
179
  if (this.arrayName && parents[0].object && parents[0].object.name == this.arrayName) {
153
180
  return;
154
181
  }
155
182
 
156
- var value;
183
+ var stringValue;
157
184
 
158
185
  if (object.type == "Literal") {
159
- value = typeof object.value + ":" + object.value;
186
+ stringValue = typeof object.value + ":" + object.value;
160
187
 
161
188
  if (object.value === null) {
162
- value = "null:null";
189
+ stringValue = "null:null";
163
190
  } else {
164
191
  // Skip empty strings
165
192
  if (typeof object.value === "string" && !object.value) {
@@ -167,38 +194,42 @@ class DuplicateLiteralsRemoval extends _transform.default {
167
194
  }
168
195
  }
169
196
  } else if (object.type == "Identifier") {
170
- value = "identifier:" + object.name;
197
+ stringValue = "identifier:" + object.name;
171
198
  } else {
172
199
  throw new Error("Unsupported primitive type: " + object.type);
173
200
  }
174
201
 
175
- (0, _assert.ok)(value);
202
+ (0, _assert.ok)(stringValue);
176
203
 
177
- if (!this.first.has(value) && !this.map.has(value)) {
178
- this.first.set(value, [object, parents]);
179
- } else {
204
+ if (this.map.has(stringValue) || this.first.has(stringValue)) {
205
+ // Create the array if not already made
180
206
  if (!this.arrayName) {
181
207
  this.arrayName = this.getPlaceholder();
182
208
  this.arrayExpression = (0, _gen.ArrayExpression)([]);
183
- }
209
+ } // Delete with first location
184
210
 
185
- var firstLocation = this.first.get(value);
211
+
212
+ var firstLocation = this.first.get(stringValue);
186
213
 
187
214
  if (firstLocation) {
188
- this.first.set(value, null);
189
215
  var index = this.map.size;
190
- (0, _assert.ok)(!this.map.has(value));
191
- this.map.set(value, index);
216
+ (0, _assert.ok)(!this.map.has(stringValue));
217
+ this.map.set(stringValue, index);
218
+ this.first.delete(stringValue);
192
219
  var pushing = (0, _insert.clone)(object);
193
220
  this.arrayExpression.elements.push(pushing);
194
221
  (0, _assert.ok)(this.arrayExpression.elements[index] === pushing);
195
- this.toCaller(firstLocation[0], firstLocation[1], index);
222
+ this.transformLiteral(firstLocation[0], firstLocation[1], index);
196
223
  }
197
224
 
198
- var index = this.map.get(value);
225
+ var index = this.map.get(stringValue);
199
226
  (0, _assert.ok)(typeof index === "number");
200
- this.toCaller(object, parents, index);
201
- }
227
+ this.transformLiteral(object, parents, index);
228
+ return;
229
+ } // Save this, maybe a duplicate will be found.
230
+
231
+
232
+ this.first.set(stringValue, [object, parents]);
202
233
  };
203
234
  }
204
235