js-confuser 2.1.0 → 2.1.1

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.
@@ -15,6 +15,7 @@ var _IntGen = require("../utils/IntGen");
15
15
  var _assert = require("assert");
16
16
  var _NameGen = require("../utils/NameGen");
17
17
  var _constants = require("../constants");
18
+ var _xorStringTemplate = require("../templates/xorStringTemplate.ts");
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
19
20
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
20
21
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
@@ -74,9 +75,9 @@ var _default = exports["default"] = function _default(_ref) {
74
75
  var addPredicateTests = true; // case scope.A + 10: ...
75
76
  var mangleNumericalLiterals = true; // 50 => state + X
76
77
  var mangleBooleanLiterals = true; // true => state == X
77
- var addWithStatement = true; // Disabling not supported yet
78
- var addGeneratorFunction = true; // Wrap in generator function?
78
+ var mangleStringLiterals = true; // "hi" => xor("fOq", state + X)
79
79
 
80
+ var stateVarsRange = [3, 10];
80
81
  var cffPrefix = me.getPlaceholder();
81
82
 
82
83
  // Amount of blocks changed by Control Flow Flattening
@@ -86,1170 +87,1170 @@ var _default = exports["default"] = function _default(_ref) {
86
87
  var fnOrProgram = (0, _astUtils.getParentFunctionOrProgram)(path);
87
88
  fnOrProgram.node[CFF_UNSAFE] = reason;
88
89
  }
89
- return {
90
- post: function post() {
91
- for (var _i = 0, _functionsModified = functionsModified; _i < _functionsModified.length; _i++) {
92
- var node = _functionsModified[_i];
93
- node[_constants.UNSAFE] = true;
90
+ var needsSumFunction = false;
91
+ var sumFnName = me.getPlaceholder() + "_cff_sum";
92
+ var xorFnName = me.getPlaceholder() + "_cff_xor";
93
+ function cffMain(_path) {
94
+ var programOrFunctionPath = _path;
95
+
96
+ // if (cffOptions.excludeLoops) {
97
+ // // Exclude loops
98
+ // if (programOrFunctionPath.find((p) => p.isForStatement() || p.isWhile()))
99
+ // return;
100
+ // }
101
+ // var debugName = (programOrFunctionPath?.node as any)?.id?.name;
102
+
103
+ // Exclude 'CFF_UNSAFE' functions
104
+ if (programOrFunctionPath.node[CFF_UNSAFE]) {
105
+ // console.log(`Skipping ${debugName} because ${programOrFunctionPath.node[CFF_UNSAFE]}`);
106
+ return;
107
+ }
108
+ var programPath = _path.isProgram() ? _path : null;
109
+ var functionPath = _path.isFunction() ? _path : null;
110
+ var blockPath;
111
+ if (programPath) {
112
+ blockPath = programPath;
113
+ } else {
114
+ var fnBlockPath = functionPath.get("body");
115
+ if (!fnBlockPath.isBlock()) return;
116
+ blockPath = fnBlockPath;
117
+ }
118
+
119
+ // Don't apply to strict mode blocks
120
+ var strictModeEnforcingBlock = programOrFunctionPath.find(function (path) {
121
+ return (0, _astUtils.isStrictMode)(path);
122
+ });
123
+ if (strictModeEnforcingBlock) return;
124
+
125
+ // Must be at least 3 statements or more
126
+ if (blockPath.node.body.length < 3) return;
127
+
128
+ // Check user's threshold setting
129
+ if (!me.computeProbabilityMap(me.options.controlFlowFlattening)) {
130
+ return;
131
+ }
132
+ if (functionPath) {
133
+ // Avoid unsafe functions
134
+ if (functionPath.node[_constants.UNSAFE]) {
135
+ // console.log(`Skipping ${debugName} because of UNSAFE flag`);
136
+ return;
94
137
  }
95
- },
96
- visitor: {
97
- // Unsafe detection
98
- ThisExpression: function ThisExpression(path) {
99
- flagFunctionToAvoid(path, "this");
100
- },
101
- VariableDeclaration: function VariableDeclaration(path) {
102
- if (path.node.declarations.length !== 1) {
103
- path.getAncestry().forEach(function (p) {
104
- p.node[CFF_UNSAFE] = "multipleDeclarations";
105
- });
106
- }
107
- },
138
+ if (functionPath.node.async || functionPath.node.generator) return;
139
+ }
140
+ programOrFunctionPath.scope.crawl();
141
+ var hasIllegalNode = false;
142
+ var bindingNames = new Set();
143
+ blockPath.traverse({
108
144
  Identifier: function Identifier(path) {
109
- if (path.node.name === _constants.variableFunctionName || path.node.name === "arguments") {
110
- flagFunctionToAvoid(path, "arguments");
145
+ if (!path.isBindingIdentifier()) return;
146
+ var binding = path.scope.getBinding(path.node.name);
147
+ if (!binding) return;
148
+ var fnParent = path.getFunctionParent();
149
+ if (path.key === "id" && path.parentPath.isFunctionDeclaration()) {
150
+ fnParent = path.parentPath.getFunctionParent();
111
151
  }
112
- },
113
- "Super|MetaProperty|AwaitExpression|YieldExpression": function SuperMetaPropertyAwaitExpressionYieldExpression(path) {
114
- flagFunctionToAvoid(path, "functionSpecific");
115
- },
116
- // Main CFF transformation
117
- "Program|Function": {
118
- exit: function exit(_path) {
119
- var programOrFunctionPath = _path;
120
-
121
- // Exclude loops
122
- if (programOrFunctionPath.find(function (p) {
123
- return p.isForStatement() || p.isWhile();
124
- })) return;
125
-
126
- // Exclude 'CFF_UNSAFE' functions
127
- if (programOrFunctionPath.node[CFF_UNSAFE]) return;
128
- var programPath = _path.isProgram() ? _path : null;
129
- var functionPath = _path.isFunction() ? _path : null;
130
- var blockPath;
131
- if (programPath) {
132
- blockPath = programPath;
133
- } else {
134
- var fnBlockPath = functionPath.get("body");
135
- if (!fnBlockPath.isBlock()) return;
136
- blockPath = fnBlockPath;
152
+ if (fnParent !== functionPath) return;
153
+ if (!(0, _astUtils.isDefiningIdentifier)(path)) {
154
+ return;
155
+ }
156
+ if (bindingNames.has(path.node.name)) {
157
+ hasIllegalNode = true;
158
+ path.stop();
159
+ return;
160
+ }
161
+ bindingNames.add(path.node.name);
162
+ }
163
+ });
164
+ if (hasIllegalNode) {
165
+ return;
166
+ }
167
+ me.changeData.blocks++;
168
+
169
+ // Limit how many numbers get entangled
170
+ var mangledLiteralsCreated = 0;
171
+ var cffIndex = ++cffCounter; // Start from 1
172
+ var prefix = cffPrefix + "_" + cffIndex;
173
+ var identifier = function identifier(suffix) {
174
+ var name;
175
+ if (isDebug) {
176
+ name = prefix + "_" + suffix;
177
+ } else {
178
+ name = me.obfuscator.nameGen.generate(false);
179
+ }
180
+ var id = t.identifier(name);
181
+ return id;
182
+ };
183
+ var mainFnName = identifier("main");
184
+ var scopeVar = identifier("scope");
185
+ var statesVar = identifier("states");
186
+ var runtimeVar = identifier("runtime");
187
+ var stateVars = new Array(isDebug ? 1 : (0, _randomUtils.getRandomInteger)(stateVarsRange[0], stateVarsRange[1])).fill("").map(function (_, i) {
188
+ return t.memberExpression((0, _node.deepClone)(statesVar), t.numericLiteral(i), true);
189
+ });
190
+ var argVar = identifier("_arg");
191
+ var usedArgVar = false;
192
+ var _didReturnVar = identifier("return");
193
+ var basicBlocks = new Map();
194
+
195
+ // Map labels to states
196
+ var stateIntGen = new _IntGen.IntGen();
197
+ var defaultBlockPath = blockPath;
198
+ var scopeCounter = 0;
199
+ var scopeNameGen = new _NameGen.NameGen(me.options.identifierGenerator);
200
+ if (!isDebug) {
201
+ scopeNameGen = me.obfuscator.nameGen;
202
+ }
203
+ var ScopeManager = /*#__PURE__*/function () {
204
+ function ScopeManager(scope, initializingBasicBlock) {
205
+ _classCallCheck(this, ScopeManager);
206
+ _defineProperty(this, "isNotUsed", true);
207
+ _defineProperty(this, "requiresInitializing", true);
208
+ _defineProperty(this, "nameMap", new Map());
209
+ _defineProperty(this, "nameGen", new _NameGen.NameGen(me.options.identifierGenerator));
210
+ this.scope = scope;
211
+ this.initializingBasicBlock = initializingBasicBlock;
212
+ this.propertyName = isDebug ? "_" + cffIndex + "_" + scopeCounter++ : scopeNameGen.generate();
213
+ }
214
+ return _createClass(ScopeManager, [{
215
+ key: "getNewName",
216
+ value: function getNewName(name, originalNode) {
217
+ if (!this.nameMap.has(name)) {
218
+ var newName = this.nameGen.generate(false);
219
+ if (isDebug) {
220
+ newName = "_" + name;
221
+ }
222
+
223
+ // console.log(name, newName);
224
+
225
+ this.nameMap.set(name, newName);
226
+ me.changeData.variables++;
227
+
228
+ // console.log(
229
+ // `Renaming ${name} to ${newName}: ${this.scope.path.type} (scope ${this.propertyName})`,
230
+ // );
231
+
232
+ return newName;
137
233
  }
234
+ return this.nameMap.get(name);
235
+ }
236
+ }, {
237
+ key: "getScopeObject",
238
+ value: function getScopeObject() {
239
+ return t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(this.propertyName), true);
240
+ }
241
+ }, {
242
+ key: "getInitializingStatement",
243
+ value: function getInitializingStatement() {
244
+ return t.expressionStatement(t.assignmentExpression("=", this.getScopeObject(), this.getInitializingObjectExpression()));
245
+ }
246
+ }, {
247
+ key: "getInitializingObjectExpression",
248
+ value: function getInitializingObjectExpression() {
249
+ return isDebug ? new _template["default"]("\n ({\n identity: \"".concat(this.propertyName, "\"\n })\n ")).expression() : new _template["default"]("({})").expression();
250
+ }
251
+ }, {
252
+ key: "getMemberExpression",
253
+ value: function getMemberExpression(name) {
254
+ var object = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getScopeObject();
255
+ var memberExpression = t.memberExpression(object, t.stringLiteral(name), true);
256
+ return memberExpression;
257
+ }
258
+ }, {
259
+ key: "parent",
260
+ get: function get() {
261
+ return scopeToScopeManager.get(this.scope.parent);
262
+ }
263
+ }, {
264
+ key: "getObjectExpression",
265
+ value: function getObjectExpression(refreshLabel) {
266
+ var refreshScope = basicBlocks.get(refreshLabel).scopeManager;
267
+ var propertyMap = {};
268
+ var cursor = this.scope;
269
+ while (cursor) {
270
+ var parentScopeManager = scopeToScopeManager.get(cursor);
271
+ if (parentScopeManager) {
272
+ propertyMap[parentScopeManager.propertyName] = t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(parentScopeManager.propertyName), true);
273
+ }
274
+ cursor = cursor.parent;
275
+ }
276
+ propertyMap[refreshScope.propertyName] = refreshScope.getInitializingObjectExpression();
277
+ var properties = [];
278
+ for (var key in propertyMap) {
279
+ properties.push(t.objectProperty(t.stringLiteral(key), propertyMap[key], true));
280
+ }
281
+ return t.objectExpression(properties);
282
+ }
283
+ }, {
284
+ key: "hasOwnName",
285
+ value: function hasOwnName(name) {
286
+ return this.nameMap.has(name);
287
+ }
288
+ }]);
289
+ }();
290
+ var getImpossibleBasicBlocks = function getImpossibleBasicBlocks() {
291
+ return Array.from(basicBlocks.values()).filter(function (block) {
292
+ return block.options.impossible;
293
+ });
294
+ };
295
+ var scopeToScopeManager = new Map();
296
+ /**
297
+ * A Basic Block is a sequence of instructions with no diversion except at the entry and exit points.
298
+ */
299
+ var BasicBlock = /*#__PURE__*/function () {
300
+ function BasicBlock(label, parentPath) {
301
+ var _this = this;
302
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
303
+ _classCallCheck(this, BasicBlock);
304
+ this.label = label;
305
+ this.parentPath = parentPath;
306
+ this.options = options;
307
+ this.createPath();
308
+ if (isDebug) {
309
+ // States in debug mode are just 1, 2, 3, ...
310
+ this.totalState = basicBlocks.size + 1;
311
+ } else {
312
+ this.totalState = stateIntGen.generate();
313
+ }
138
314
 
139
- // Don't apply to strict mode blocks
140
- var strictModeEnforcingBlock = programOrFunctionPath.find(function (path) {
141
- return (0, _astUtils.isStrictMode)(path);
315
+ // Correct state values
316
+ // Start with random numbers
317
+ this.stateValues = stateVars.map(function () {
318
+ return (0, _randomUtils.getRandomInteger)(-250, 250);
319
+ });
320
+
321
+ // Try to re-use old state values to make diffs smaller
322
+ if (basicBlocks.size > 1) {
323
+ var lastBlock = _toConsumableArray(basicBlocks.values()).at(-1);
324
+ this.stateValues = lastBlock.stateValues.map(function (oldValue, i) {
325
+ // Increase chance for re-using old values to make state transitions less drastic
326
+ return (0, _randomUtils.chance)(90) ? oldValue : _this.stateValues[i];
142
327
  });
143
- if (strictModeEnforcingBlock) return;
328
+ }
329
+
330
+ // Correct one of the values so that the accumulated sum is equal to the state
331
+ var correctIndex = (0, _randomUtils.getRandomInteger)(0, this.stateValues.length);
332
+ var getCurrentState = function getCurrentState() {
333
+ return _this.stateValues.reduce(function (a, b) {
334
+ return a + b;
335
+ }, 0);
336
+ };
144
337
 
145
- // Must be at least 3 statements or more
146
- if (blockPath.node.body.length < 3) return;
338
+ // Correct the value
339
+ this.stateValues[correctIndex] = this.totalState - (getCurrentState() - this.stateValues[correctIndex]);
340
+ (0, _assert.ok)(getCurrentState() === this.totalState);
147
341
 
148
- // Check user's threshold setting
149
- if (!me.computeProbabilityMap(me.options.controlFlowFlattening)) {
150
- return;
342
+ // Store basic block
343
+ basicBlocks.set(label, this);
344
+
345
+ // Create a new scope manager if it doesn't exist
346
+ if (!scopeToScopeManager.has(this.scope)) {
347
+ scopeToScopeManager.set(this.scope, new ScopeManager(this.scope, this));
348
+ }
349
+ this.initializedScope = this.scopeManager;
350
+ }
351
+ return _createClass(BasicBlock, [{
352
+ key: "createPath",
353
+ value: function createPath() {
354
+ var newPath = _traverse.NodePath.get({
355
+ hub: this.parentPath.hub,
356
+ parentPath: this.parentPath,
357
+ parent: this.parentPath.node,
358
+ container: this.parentPath.node.body,
359
+ listKey: "body",
360
+ // Set the correct list key
361
+ key: "virtual" // Set the index of the new node
362
+ });
363
+ newPath.scope = this.parentPath.scope;
364
+ newPath.parentPath = this.parentPath;
365
+ newPath.node = t.blockStatement([]);
366
+ this.thisPath = newPath;
367
+ this.thisNode = newPath.node;
368
+ }
369
+ }, {
370
+ key: "insertAfter",
371
+ value: function insertAfter(newNode) {
372
+ this.body.push(newNode);
373
+ }
374
+ }, {
375
+ key: "scope",
376
+ get: function get() {
377
+ return this.parentPath.scope;
378
+ }
379
+ }, {
380
+ key: "scopeManager",
381
+ get: function get() {
382
+ return scopeToScopeManager.get(this.scope);
383
+ }
384
+ }, {
385
+ key: "body",
386
+ get: function get() {
387
+ return this.thisPath.node.body;
388
+ }
389
+ }, {
390
+ key: "createFalsePredicate",
391
+ value: function createFalsePredicate() {
392
+ var predicate = this.createPredicate();
393
+ if (predicate.value) {
394
+ // Make predicate false
395
+ return t.unaryExpression("!", predicate.node);
151
396
  }
152
- if (functionPath) {
153
- // Avoid unsafe functions
154
- if (functionPath.node[_constants.UNSAFE]) return;
155
- if (functionPath.node.async || functionPath.node.generator) return;
397
+ return predicate.node;
398
+ }
399
+ }, {
400
+ key: "createTruePredicate",
401
+ value: function createTruePredicate() {
402
+ var predicate = this.createPredicate();
403
+ if (!predicate.value) {
404
+ // Make predicate true
405
+ return t.unaryExpression("!", predicate.node);
156
406
  }
157
- programOrFunctionPath.scope.crawl();
158
- var hasIllegalNode = false;
159
- var bindingNames = new Set();
160
- blockPath.traverse({
161
- Identifier: function Identifier(path) {
162
- if (!path.isBindingIdentifier()) return;
163
- var binding = path.scope.getBinding(path.node.name);
164
- if (!binding) return;
165
- var fnParent = path.getFunctionParent();
166
- if (path.key === "id" && path.parentPath.isFunctionDeclaration()) {
167
- fnParent = path.parentPath.getFunctionParent();
168
- }
169
- if (fnParent !== functionPath) return;
170
- if (!(0, _astUtils.isDefiningIdentifier)(path)) {
171
- return;
172
- }
173
- if (bindingNames.has(path.node.name)) {
174
- hasIllegalNode = true;
175
- path.stop();
176
- return;
177
- }
178
- bindingNames.add(path.node.name);
179
- }
180
- });
181
- if (hasIllegalNode) {
182
- return;
407
+ return predicate.node;
408
+ }
409
+ }, {
410
+ key: "createPredicate",
411
+ value: function createPredicate() {
412
+ var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
413
+ var stateValue = this.stateValues[stateVarIndex];
414
+ var compareValue = (0, _randomUtils.choice)([stateValue, (0, _randomUtils.getRandomInteger)(-250, 250)]);
415
+ var operator = (0, _randomUtils.choice)(["==", "!=", "<", ">"]);
416
+ var compareResult;
417
+ switch (operator) {
418
+ case "==":
419
+ compareResult = stateValue === compareValue;
420
+ break;
421
+ case "!=":
422
+ compareResult = stateValue !== compareValue;
423
+ break;
424
+ case "<":
425
+ compareResult = stateValue < compareValue;
426
+ break;
427
+ case ">":
428
+ compareResult = stateValue > compareValue;
429
+ break;
183
430
  }
184
- me.changeData.blocks++;
185
-
186
- // Limit how many numbers get entangled
187
- var mangledLiteralsCreated = 0;
188
- var cffIndex = ++cffCounter; // Start from 1
189
- var prefix = cffPrefix + "_" + cffIndex;
190
- var withIdentifier = function withIdentifier(suffix) {
191
- var name;
192
- if (isDebug) {
193
- name = prefix + "_" + suffix;
194
- } else {
195
- name = me.obfuscator.nameGen.generate(false);
196
- }
197
- var id = t.identifier(name);
198
- id[_constants.NO_RENAME] = cffIndex;
199
- return id;
431
+ return {
432
+ node: t.binaryExpression(operator, (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(compareValue)),
433
+ value: compareResult
200
434
  };
201
- var mainFnName = withIdentifier("main");
202
- var scopeVar = withIdentifier("scope");
203
- var stateVars = new Array(isDebug ? 1 : (0, _randomUtils.getRandomInteger)(2, 5)).fill("").map(function (_, i) {
204
- return withIdentifier("state_".concat(i));
205
- });
206
- var argVar = withIdentifier("_arg");
207
- var usedArgVar = false;
208
- var _didReturnVar = withIdentifier("return");
209
- var basicBlocks = new Map();
210
-
211
- // Map labels to states
212
- var stateIntGen = new _IntGen.IntGen();
213
- var defaultBlockPath = blockPath;
214
- var scopeCounter = 0;
215
- var scopeNameGen = new _NameGen.NameGen(me.options.identifierGenerator);
216
- if (!isDebug) {
217
- scopeNameGen = me.obfuscator.nameGen;
218
- }
435
+ }
436
+ }, {
437
+ key: "identifier",
438
+ value: function identifier(identifierName, scopeManager) {
439
+ return scopeManager.getMemberExpression(identifierName);
440
+ }
441
+ }]);
442
+ }();
443
+ /**
444
+ * Stage 1: Flatten the code into Basic Blocks
445
+ *
446
+ * This involves transforming the Control Flow / Scopes into blocks with 'goto' statements
447
+ *
448
+ * - A block is simply a sequence of statements
449
+ * - A block can have a 'goto' statement to another block
450
+ * - A block original scope is preserved
451
+ *
452
+ * 'goto' & Scopes are transformed in Stage 2
453
+ */
454
+ var switchLabel = me.getPlaceholder();
455
+ var breakStatement = function breakStatement() {
456
+ return t.breakStatement(t.identifier(switchLabel));
457
+ };
458
+ var startLabel = me.getPlaceholder();
459
+ var endLabel = me.getPlaceholder();
460
+ var currentBasicBlock = new BasicBlock(startLabel, blockPath);
461
+ var gotoFunctionName = "GOTO__" + me.getPlaceholder() + "__IF_YOU_CAN_READ_THIS_THERE_IS_A_BUG";
462
+ function GotoControlStatement(label) {
463
+ return new _template["default"]("\n ".concat(gotoFunctionName, "(\"").concat(label, "\");\n ")).single();
464
+ }
219
465
 
220
- // Create 'with' object - Determines which scope gets top-level variable access
221
- var withProperty = isDebug ? "with" : scopeNameGen.generate(false);
222
- var withMemberExpression = new _template["default"]("".concat(scopeVar.name, "[\"").concat(withProperty, "\"]")).expression();
223
- withMemberExpression.object[_constants.NO_RENAME] = cffIndex;
224
- var ScopeManager = /*#__PURE__*/function () {
225
- function ScopeManager(scope, initializingBasicBlock) {
226
- _classCallCheck(this, ScopeManager);
227
- _defineProperty(this, "isNotUsed", true);
228
- _defineProperty(this, "requiresInitializing", true);
229
- _defineProperty(this, "nameMap", new Map());
230
- _defineProperty(this, "nameGen", addWithStatement ? me.obfuscator.nameGen : new _NameGen.NameGen(me.options.identifierGenerator));
231
- this.scope = scope;
232
- this.initializingBasicBlock = initializingBasicBlock;
233
- this.propertyName = isDebug ? "_" + cffIndex + "_" + scopeCounter++ : scopeNameGen.generate();
466
+ // Ends the current block and starts a new one
467
+ function endCurrentBasicBlock() {
468
+ var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
469
+ _ref2$jumpToNext = _ref2.jumpToNext,
470
+ jumpToNext = _ref2$jumpToNext === void 0 ? true : _ref2$jumpToNext,
471
+ _ref2$nextLabel = _ref2.nextLabel,
472
+ nextLabel = _ref2$nextLabel === void 0 ? me.getPlaceholder() : _ref2$nextLabel,
473
+ _ref2$prevJumpTo = _ref2.prevJumpTo,
474
+ prevJumpTo = _ref2$prevJumpTo === void 0 ? null : _ref2$prevJumpTo,
475
+ _ref2$nextBlockPath = _ref2.nextBlockPath,
476
+ nextBlockPath = _ref2$nextBlockPath === void 0 ? null : _ref2$nextBlockPath;
477
+ (0, _assert.ok)(nextBlockPath);
478
+ if (prevJumpTo) {
479
+ currentBasicBlock.insertAfter(GotoControlStatement(prevJumpTo));
480
+ } else if (jumpToNext) {
481
+ currentBasicBlock.insertAfter(GotoControlStatement(nextLabel));
482
+ }
483
+ currentBasicBlock = new BasicBlock(nextLabel, nextBlockPath);
484
+ }
485
+ var prependNodes = [];
486
+ var functionExpressions = [];
487
+ function flattenIntoBasicBlocks(bodyIn) {
488
+ // if (!Array.isArray(bodyIn) && bodyIn.isBlock()) {
489
+ // currentBasicBlock.parentPath = bodyIn;
490
+ // }
491
+ var body = Array.isArray(bodyIn) ? bodyIn : bodyIn.get("body");
492
+ var nextBlockPath = Array.isArray(bodyIn) ? currentBasicBlock.parentPath : bodyIn;
493
+ var _loop = function _loop() {
494
+ var statement = body[index];
495
+
496
+ // Keep Imports before everything else
497
+ if (statement.isImportDeclaration()) {
498
+ prependNodes.push(statement.node);
499
+ return 0; // continue
500
+ }
501
+ if (statement.isFunctionDeclaration()) {
502
+ var fnName = statement.node.id.name;
503
+ var isIllegal = false;
504
+ if (!flattenFunctionDeclarations || statement.node.async || statement.node.generator || statement.node[_constants.UNSAFE] || statement.node[CFF_UNSAFE] || (0, _astUtils.isStrictMode)(statement)) {
505
+ isIllegal = true;
234
506
  }
235
- return _createClass(ScopeManager, [{
236
- key: "findBestWithDiscriminant",
237
- value: function findBestWithDiscriminant(basicBlock) {
238
- var _this$parent;
239
- // This initializing block is forbidden to have a with discriminant
240
- // (As no previous code is able to prepare the with discriminant)
241
- if (basicBlock !== this.initializingBasicBlock) {
242
- // If no variables were defined in this scope, don't use it
243
- if (Object.keys(this.scope.bindings).length > 0) return this;
244
- }
245
- return (_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.findBestWithDiscriminant(basicBlock);
246
- }
247
- }, {
248
- key: "getNewName",
249
- value: function getNewName(name, originalNode) {
250
- if (!this.nameMap.has(name)) {
251
- var newName = this.nameGen.generate(false);
252
- if (isDebug) {
253
- newName = "_" + name;
254
- }
507
+ var oldBasicBlock = currentBasicBlock;
508
+ var _fnLabel = me.getPlaceholder();
509
+ var sm = currentBasicBlock.scopeManager;
510
+ var rename = sm.getNewName(fnName);
511
+ var hoistedBasicBlock = Array.from(basicBlocks.values()).find(function (block) {
512
+ return block.parentPath === currentBasicBlock.parentPath;
513
+ });
255
514
 
256
- // console.log(name, newName);
515
+ // Added into scope object due to function flattening causes redeclaration (Test #42 and #44)
516
+ hoistedBasicBlock.scope.bindings[fnName].kind = "var";
517
+ if (isIllegal) {
518
+ // Function must be converted to function expression as hoisted function declaration
519
+ // gets redeclared (and losing prototype methods) each iteration (Test #43)
520
+ var asFunctionExpression = statement.node;
521
+ asFunctionExpression.type = "FunctionExpression";
522
+ hoistedBasicBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(fnName), asFunctionExpression)]));
523
+ return 0; // continue
524
+ }
525
+ me.changeData.functions++;
526
+ var functionExpression = t.functionExpression(null, [], t.blockStatement([]));
527
+ functionExpressions.push([fnName, _fnLabel, currentBasicBlock, functionExpression]);
528
+
529
+ // Change the function declaration to a variable declaration
530
+ hoistedBasicBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(fnName), functionExpression)]));
531
+ var blockStatement = statement.get("body");
532
+ endCurrentBasicBlock({
533
+ nextLabel: _fnLabel,
534
+ nextBlockPath: blockStatement,
535
+ jumpToNext: false
536
+ });
537
+ var fnTopBlock = currentBasicBlock;
257
538
 
258
- this.nameMap.set(name, newName);
259
- me.changeData.variables++;
539
+ // Implicit return
540
+ blockStatement.node.body.push(t.returnStatement(t.identifier("undefined")));
541
+ flattenIntoBasicBlocks(blockStatement);
542
+ scopeToScopeManager.get(statement.scope).requiresInitializing = false;
260
543
 
261
- // console.log(
262
- // "Renaming " +
263
- // name +
264
- // " to " +
265
- // newName +
266
- // " : " +
267
- // this.scope.path.type
268
- // );
544
+ // Debug label
545
+ if (isDebug) {
546
+ fnTopBlock.body.unshift(t.expressionStatement(t.stringLiteral("Function " + statement.node.id.name + " -> Renamed to " + rename)));
547
+ }
269
548
 
270
- return newName;
271
- }
272
- return this.nameMap.get(name);
273
- }
274
- }, {
275
- key: "getScopeObject",
276
- value: function getScopeObject() {
277
- return t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(this.propertyName), true);
278
- }
279
- }, {
280
- key: "getInitializingStatement",
281
- value: function getInitializingStatement() {
282
- return t.expressionStatement(t.assignmentExpression("=", this.getScopeObject(), this.getInitializingObjectExpression()));
283
- }
284
- }, {
285
- key: "getInitializingObjectExpression",
286
- value: function getInitializingObjectExpression() {
287
- return isDebug ? new _template["default"]("\n ({\n identity: \"".concat(this.propertyName, "\"\n })\n ")).expression() : new _template["default"]("({})").expression();
288
- }
289
- }, {
290
- key: "getMemberExpression",
291
- value: function getMemberExpression(name) {
292
- var object = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getScopeObject();
293
- var memberExpression = t.memberExpression(object, t.stringLiteral(name), true);
294
- return memberExpression;
295
- }
296
- }, {
297
- key: "parent",
298
- get: function get() {
299
- return scopeToScopeManager.get(this.scope.parent);
300
- }
301
- }, {
302
- key: "getObjectExpression",
303
- value: function getObjectExpression(refreshLabel) {
304
- var refreshScope = basicBlocks.get(refreshLabel).scopeManager;
305
- var propertyMap = {};
306
- var cursor = this.scope;
307
- while (cursor) {
308
- var parentScopeManager = scopeToScopeManager.get(cursor);
309
- if (parentScopeManager) {
310
- propertyMap[parentScopeManager.propertyName] = t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(parentScopeManager.propertyName), true);
549
+ // Unpack parameters from the parameter 'argVar'
550
+ if (statement.node.params.length > 0) {
551
+ usedArgVar = true;
552
+ fnTopBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(statement.node.params), (0, _node.deepClone)(argVar))]));
553
+
554
+ // Change bindings from 'param' to 'var'
555
+ statement.get("params").forEach(function (param) {
556
+ var ids = param.getBindingIdentifierPaths();
557
+ // Loop over the record of binding identifiers
558
+ for (var identifierName in ids) {
559
+ var identifierPath = ids[identifierName];
560
+ if (identifierPath.getFunctionParent() === statement) {
561
+ var binding = statement.scope.getBinding(identifierName);
562
+ if (binding) {
563
+ binding.kind = "var";
564
+ }
311
565
  }
312
- cursor = cursor.parent;
313
- }
314
- propertyMap[refreshScope.propertyName] = refreshScope.getInitializingObjectExpression();
315
- var properties = [];
316
- for (var key in propertyMap) {
317
- properties.push(t.objectProperty(t.stringLiteral(key), propertyMap[key], true));
318
566
  }
319
- return t.objectExpression(properties);
320
- }
321
- }, {
322
- key: "hasOwnName",
323
- value: function hasOwnName(name) {
324
- return this.nameMap.has(name);
325
- }
326
- }]);
327
- }();
328
- var getImpossibleBasicBlocks = function getImpossibleBasicBlocks() {
329
- return Array.from(basicBlocks.values()).filter(function (block) {
330
- return block.options.impossible;
331
- });
332
- };
333
- var scopeToScopeManager = new Map();
334
- /**
335
- * A Basic Block is a sequence of instructions with no diversion except at the entry and exit points.
336
- */
337
- var BasicBlock = /*#__PURE__*/function () {
338
- function BasicBlock(label, parentPath) {
339
- var _this = this;
340
- var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
341
- _classCallCheck(this, BasicBlock);
342
- _defineProperty(this, "allowWithDiscriminant", true);
343
- this.label = label;
344
- this.parentPath = parentPath;
345
- this.options = options;
346
- this.createPath();
347
- if (isDebug) {
348
- // States in debug mode are just 1, 2, 3, ...
349
- this.totalState = basicBlocks.size + 1;
350
- } else {
351
- this.totalState = stateIntGen.generate();
352
- }
353
-
354
- // Correct state values
355
- // Start with random numbers
356
- this.stateValues = stateVars.map(function () {
357
- return (0, _randomUtils.getRandomInteger)(-250, 250);
358
567
  });
568
+ }
569
+ currentBasicBlock = oldBasicBlock;
570
+ return 0; // continue
571
+ }
359
572
 
360
- // Try to re-use old state values to make diffs smaller
361
- if (basicBlocks.size > 1) {
362
- var lastBlock = _toConsumableArray(basicBlocks.values()).at(-1);
363
- this.stateValues = lastBlock.stateValues.map(function (oldValue, i) {
364
- return (0, _randomUtils.choice)([oldValue, _this.stateValues[i]]);
573
+ // Convert IF statements into Basic Blocks
574
+ if (statement.isIfStatement() && flattenIfStatements) {
575
+ var test = statement.get("test");
576
+ var consequent = statement.get("consequent");
577
+ var alternate = statement.get("alternate");
578
+
579
+ // Both consequent and alternate are blocks
580
+ if (consequent.isBlockStatement() && (!alternate.node || alternate.isBlockStatement())) {
581
+ me.changeData.ifStatements++;
582
+ var consequentLabel = me.getPlaceholder();
583
+ var alternateLabel = alternate.node ? me.getPlaceholder() : null;
584
+ var afterPath = me.getPlaceholder();
585
+ currentBasicBlock.insertAfter(t.ifStatement(test.node, GotoControlStatement(consequentLabel), alternateLabel ? GotoControlStatement(alternateLabel) : GotoControlStatement(afterPath)));
586
+ var _oldBasicBlock = currentBasicBlock;
587
+ endCurrentBasicBlock({
588
+ jumpToNext: false,
589
+ nextLabel: consequentLabel,
590
+ nextBlockPath: consequent
591
+ });
592
+ flattenIntoBasicBlocks(consequent);
593
+ currentBasicBlock.initializedScope = _oldBasicBlock.scopeManager;
594
+ if (alternate.isBlockStatement()) {
595
+ endCurrentBasicBlock({
596
+ prevJumpTo: afterPath,
597
+ nextLabel: alternateLabel,
598
+ nextBlockPath: alternate
365
599
  });
600
+ flattenIntoBasicBlocks(alternate);
366
601
  }
602
+ endCurrentBasicBlock({
603
+ prevJumpTo: afterPath,
604
+ nextLabel: afterPath,
605
+ nextBlockPath: _oldBasicBlock.parentPath
606
+ });
607
+ return 0; // continue
608
+ }
609
+ }
610
+ if (Number(index) === body.length - 1 && statement.isExpressionStatement() && statement.findParent(function (p) {
611
+ return p.isBlock();
612
+ }) === blockPath && programPath // <- ONLY APPLY TO TOP-LEVEL NEVER FUNCTIONS!
613
+ ) {
614
+ // Return the result of the last expression for eval() purposes
615
+ currentBasicBlock.insertAfter(t.returnStatement(statement.get("expression").node));
616
+ return 0; // continue
617
+ }
367
618
 
368
- // Correct one of the values so that the accumulated sum is equal to the state
369
- var correctIndex = (0, _randomUtils.getRandomInteger)(0, this.stateValues.length);
370
- var getCurrentState = function getCurrentState() {
371
- return _this.stateValues.reduce(function (a, b) {
372
- return a + b;
373
- }, 0);
374
- };
619
+ // 3 or more statements should be split more
620
+ if (currentBasicBlock.body.length > 1 && (0, _randomUtils.chance)(50 + currentBasicBlock.body.length)) {
621
+ endCurrentBasicBlock({
622
+ nextBlockPath: nextBlockPath
623
+ });
624
+ }
375
625
 
376
- // Correct the value
377
- this.stateValues[correctIndex] = this.totalState - (getCurrentState() - this.stateValues[correctIndex]);
378
- (0, _assert.ok)(getCurrentState() === this.totalState);
626
+ // console.log(currentBasicBlock.thisPath.type);
627
+ // console.log(currentBasicBlock.body);
628
+ currentBasicBlock.body.push(statement.node);
629
+ },
630
+ _ret;
631
+ for (var index in body) {
632
+ _ret = _loop();
633
+ if (_ret === 0) continue;
634
+ }
635
+ }
379
636
 
380
- // Store basic block
381
- basicBlocks.set(label, this);
637
+ // Convert our code into Basic Blocks
638
+ flattenIntoBasicBlocks(blockPath.get("body"));
639
+
640
+ // Ensure always jumped to the Program end
641
+ endCurrentBasicBlock({
642
+ jumpToNext: true,
643
+ nextLabel: endLabel,
644
+ nextBlockPath: defaultBlockPath
645
+ });
646
+ if (!isDebug && addDeadCode) {
647
+ // DEAD CODE 1/3: Add fake chunks that are never reached
648
+ var fakeChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
649
+ for (var i = 0; i < fakeChunkCount; i++) {
650
+ // These chunks just jump somewhere random, they are never executed
651
+ // so it could contain any code
652
+ var fakeBlock = new BasicBlock(me.getPlaceholder(), blockPath, {
653
+ impossible: true
654
+ });
655
+ var fakeJump = void 0;
656
+ do {
657
+ fakeJump = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
658
+ } while (fakeJump === fakeBlock.label);
659
+ fakeBlock.insertAfter(GotoControlStatement(fakeJump));
660
+ me.changeData.deadCode++;
661
+ }
382
662
 
383
- // Create a new scope manager if it doesn't exist
384
- if (!scopeToScopeManager.has(this.scope)) {
385
- scopeToScopeManager.set(this.scope, new ScopeManager(this.scope, this));
386
- }
387
- this.initializedScope = this.scopeManager;
388
- }
389
- return _createClass(BasicBlock, [{
390
- key: "withDiscriminant",
391
- get: function get() {
392
- if (!this.allowWithDiscriminant) return;
393
- return this.bestWithDiscriminant;
394
- }
395
- }, {
396
- key: "createPath",
397
- value: function createPath() {
398
- var newPath = _traverse.NodePath.get({
399
- hub: this.parentPath.hub,
400
- parentPath: this.parentPath,
401
- parent: this.parentPath.node,
402
- container: this.parentPath.node.body,
403
- listKey: "body",
404
- // Set the correct list key
405
- key: "virtual" // Set the index of the new node
663
+ // DEAD CODE 2/3: Add fake jumps to really mess with deobfuscators
664
+ // "irreducible control flow"
665
+ basicBlocks.forEach(function (basicBlock) {
666
+ if ((0, _randomUtils.chance)(30 - basicBlocks.size)) {
667
+ var randomLabel = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
668
+ basicBlock.insertAfter(new _template["default"]("\n if({predicate}){\n {goto}\n }\n ").single({
669
+ "goto": GotoControlStatement(randomLabel),
670
+ predicate: basicBlock.createFalsePredicate()
671
+ }));
672
+ me.changeData.deadCode++;
673
+ }
674
+ });
675
+ // DEAD CODE 3/3: Clone chunks but these chunks are never ran
676
+ var cloneChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
677
+ var _loop2 = function _loop2() {
678
+ var randomChunk = (0, _randomUtils.choice)(Array.from(basicBlocks.values()));
679
+
680
+ // Don't double define functions
681
+ var hasDeclaration = randomChunk.body.find(function (stmt) {
682
+ return t.isDeclaration(stmt);
683
+ });
684
+ if (!hasDeclaration) {
685
+ var clonedChunk = new BasicBlock(me.getPlaceholder(), randomChunk.parentPath, {
686
+ impossible: true
687
+ });
688
+ randomChunk.thisNode.body.map(function (x) {
689
+ return (0, _node.deepClone)(x);
690
+ }).forEach(function (node) {
691
+ if (node.type === "EmptyStatement") return;
692
+ clonedChunk.insertAfter(node);
693
+ });
694
+ me.changeData.deadCode++;
695
+ }
696
+ };
697
+ for (var _i = 0; _i < cloneChunkCount; _i++) {
698
+ _loop2();
699
+ }
700
+ }
701
+
702
+ /**
703
+ * Stage 2: Transform 'goto' statements into valid JavaScript
704
+ *
705
+ * - 'goto' is replaced with equivalent state updates and break statements
706
+ * - Original identifiers are converted into member expressions
707
+ */
708
+
709
+ // Remap 'GotoStatement' to actual state assignments and Break statements
710
+ var _iterator = _createForOfIteratorHelper(basicBlocks.values()),
711
+ _step;
712
+ try {
713
+ var _loop4 = function _loop4() {
714
+ var basicBlock = _step.value;
715
+ var currentStateValues = basicBlock.stateValues;
716
+ // Wrap the statement in a Babel path to allow traversal
717
+
718
+ var outerFn = (0, _astUtils.getParentFunctionOrProgram)(basicBlock.parentPath);
719
+ function isWithinSameFunction(path) {
720
+ var fn = (0, _astUtils.getParentFunctionOrProgram)(path);
721
+ return fn.node === outerFn.node;
722
+ }
723
+ var visitor = {
724
+ SequenceExpression: {
725
+ enter: function enter(path) {
726
+ var _path$parentPath;
727
+ var hasComment = function hasComment(node) {
728
+ var _node$leadingComments;
729
+ return node === null || node === void 0 || (_node$leadingComments = node.leadingComments) === null || _node$leadingComments === void 0 ? void 0 : _node$leadingComments.some(function (p) {
730
+ return p.value.includes("@js-confuser-assert");
406
731
  });
407
- newPath.scope = this.parentPath.scope;
408
- newPath.parentPath = this.parentPath;
409
- newPath.node = t.blockStatement([]);
410
- this.thisPath = newPath;
411
- this.thisNode = newPath.node;
412
- }
413
- }, {
414
- key: "insertAfter",
415
- value: function insertAfter(newNode) {
416
- this.body.push(newNode);
417
- }
418
- }, {
419
- key: "scope",
420
- get: function get() {
421
- return this.parentPath.scope;
422
- }
423
- }, {
424
- key: "scopeManager",
425
- get: function get() {
426
- return scopeToScopeManager.get(this.scope);
427
- }
428
- }, {
429
- key: "body",
430
- get: function get() {
431
- return this.thisPath.node.body;
432
- }
433
- }, {
434
- key: "createFalsePredicate",
435
- value: function createFalsePredicate() {
436
- var predicate = this.createPredicate();
437
- if (predicate.value) {
438
- // Make predicate false
439
- return t.unaryExpression("!", predicate.node);
440
- }
441
- return predicate.node;
732
+ };
733
+ var asserted = hasComment(path.node) || hasComment((_path$parentPath = path.parentPath) === null || _path$parentPath === void 0 ? void 0 : _path$parentPath.node);
734
+ if (!asserted) {
735
+ return;
442
736
  }
443
- }, {
444
- key: "createTruePredicate",
445
- value: function createTruePredicate() {
446
- var predicate = this.createPredicate();
447
- if (!predicate.value) {
448
- // Make predicate true
449
- return t.unaryExpression("!", predicate.node);
450
- }
451
- return predicate.node;
737
+ var exprs = path.get("expressions");
738
+ if (exprs.length !== 2) {
739
+ throw new Error("Asserted sequence expressions must have exactly 2 expressions");
452
740
  }
453
- }, {
454
- key: "createPredicate",
455
- value: function createPredicate() {
456
- var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
457
- var stateValue = this.stateValues[stateVarIndex];
458
- var compareValue = (0, _randomUtils.choice)([stateValue, (0, _randomUtils.getRandomInteger)(-250, 250)]);
459
- var operator = (0, _randomUtils.choice)(["==", "!=", "<", ">"]);
460
- var compareResult;
461
- switch (operator) {
462
- case "==":
463
- compareResult = stateValue === compareValue;
464
- break;
465
- case "!=":
466
- compareResult = stateValue !== compareValue;
467
- break;
468
- case "<":
469
- compareResult = stateValue < compareValue;
470
- break;
471
- case ">":
472
- compareResult = stateValue > compareValue;
473
- break;
474
- }
475
- return {
476
- node: t.binaryExpression(operator, (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(compareValue)),
477
- value: compareResult
478
- };
479
- }
480
- }, {
481
- key: "identifier",
482
- value: function identifier(identifierName, scopeManager) {
483
- if (this.withDiscriminant && this.withDiscriminant === scopeManager) {
484
- var id = t.identifier(identifierName);
485
- id[_constants.NO_RENAME] = cffIndex;
486
- id[_constants.WITH_STATEMENT] = true;
487
- return id;
488
- }
489
- return scopeManager.getMemberExpression(identifierName);
741
+ var assertedValue = exprs[1].evaluate();
742
+ if (!assertedValue.confident) {
743
+ throw new Error("Asserted sequence expression must be statically evaluable");
490
744
  }
491
- }]);
492
- }();
493
- /**
494
- * Stage 1: Flatten the code into Basic Blocks
495
- *
496
- * This involves transforming the Control Flow / Scopes into blocks with 'goto' statements
497
- *
498
- * - A block is simply a sequence of statements
499
- * - A block can have a 'goto' statement to another block
500
- * - A block original scope is preserved
501
- *
502
- * 'goto' & Scopes are transformed in Stage 2
503
- */
504
- var switchLabel = me.getPlaceholder();
505
- var breakStatement = function breakStatement() {
506
- return t.breakStatement(t.identifier(switchLabel));
507
- };
508
- var startLabel = me.getPlaceholder();
509
- var endLabel = me.getPlaceholder();
510
- var currentBasicBlock = new BasicBlock(startLabel, blockPath);
511
- currentBasicBlock.allowWithDiscriminant = false;
512
- var gotoFunctionName = "GOTO__" + me.getPlaceholder() + "__IF_YOU_CAN_READ_THIS_THERE_IS_A_BUG";
513
- function GotoControlStatement(label) {
514
- return new _template["default"]("\n ".concat(gotoFunctionName, "(\"").concat(label, "\");\n ")).single();
515
- }
516
-
517
- // Ends the current block and starts a new one
518
- function endCurrentBasicBlock() {
519
- var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
520
- _ref2$jumpToNext = _ref2.jumpToNext,
521
- jumpToNext = _ref2$jumpToNext === void 0 ? true : _ref2$jumpToNext,
522
- _ref2$nextLabel = _ref2.nextLabel,
523
- nextLabel = _ref2$nextLabel === void 0 ? me.getPlaceholder() : _ref2$nextLabel,
524
- _ref2$prevJumpTo = _ref2.prevJumpTo,
525
- prevJumpTo = _ref2$prevJumpTo === void 0 ? null : _ref2$prevJumpTo,
526
- _ref2$nextBlockPath = _ref2.nextBlockPath,
527
- nextBlockPath = _ref2$nextBlockPath === void 0 ? null : _ref2$nextBlockPath;
528
- (0, _assert.ok)(nextBlockPath);
529
- if (prevJumpTo) {
530
- currentBasicBlock.insertAfter(GotoControlStatement(prevJumpTo));
531
- } else if (jumpToNext) {
532
- currentBasicBlock.insertAfter(GotoControlStatement(nextLabel));
745
+ (0, _assert.ok)(typeof assertedValue.value === "number", "Asserted sequence expression must evaluate to a number");
746
+ path.replaceWith(t.assignmentExpression("=", (0, _node.deepClone)(runtimeVar), exprs[0].node));
533
747
  }
534
- currentBasicBlock = new BasicBlock(nextLabel, nextBlockPath);
535
- }
536
- var prependNodes = [];
537
- var functionExpressions = [];
538
- function flattenIntoBasicBlocks(bodyIn) {
539
- // if (!Array.isArray(bodyIn) && bodyIn.isBlock()) {
540
- // currentBasicBlock.parentPath = bodyIn;
541
- // }
542
- var body = Array.isArray(bodyIn) ? bodyIn : bodyIn.get("body");
543
- var nextBlockPath = Array.isArray(bodyIn) ? currentBasicBlock.parentPath : bodyIn;
544
- var _loop = function _loop() {
545
- var statement = body[index];
546
-
547
- // Keep Imports before everything else
548
- if (statement.isImportDeclaration()) {
549
- prependNodes.push(statement.node);
550
- return 0; // continue
551
- }
552
- if (statement.isFunctionDeclaration()) {
553
- var fnName = statement.node.id.name;
554
- var isIllegal = false;
555
- if (!flattenFunctionDeclarations || statement.node.async || statement.node.generator || statement.node[_constants.UNSAFE] || statement.node[CFF_UNSAFE] || (0, _astUtils.isStrictMode)(statement)) {
556
- isIllegal = true;
557
- }
558
- var oldBasicBlock = currentBasicBlock;
559
- var _fnLabel = me.getPlaceholder();
560
- var sm = currentBasicBlock.scopeManager;
561
- var rename = sm.getNewName(fnName);
562
- sm.scope.bindings[fnName].kind = "var";
563
- var hoistedBasicBlock = Array.from(basicBlocks.values()).find(function (block) {
564
- return block.parentPath === currentBasicBlock.parentPath;
565
- });
566
- if (isIllegal) {
567
- hoistedBasicBlock.body.unshift(statement.node);
568
- return 0; // continue
569
- }
570
- me.changeData.functions++;
571
- var functionExpression = t.functionExpression(null, [], t.blockStatement([]));
572
- functionExpressions.push([fnName, _fnLabel, currentBasicBlock, functionExpression]);
573
-
574
- // Change the function declaration to a variable declaration
575
- hoistedBasicBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(fnName), functionExpression)]));
576
- var blockStatement = statement.get("body");
577
- endCurrentBasicBlock({
578
- nextLabel: _fnLabel,
579
- nextBlockPath: blockStatement,
580
- jumpToNext: false
581
- });
582
- var fnTopBlock = currentBasicBlock;
583
-
584
- // Implicit return
585
- blockStatement.node.body.push(t.returnStatement(t.identifier("undefined")));
586
- flattenIntoBasicBlocks(blockStatement);
587
- scopeToScopeManager.get(statement.scope).requiresInitializing = false;
588
- basicBlocks.get(_fnLabel).allowWithDiscriminant = false;
589
-
590
- // Debug label
591
- if (isDebug) {
592
- fnTopBlock.body.unshift(t.expressionStatement(t.stringLiteral("Function " + statement.node.id.name + " -> Renamed to " + rename)));
593
- }
594
-
595
- // Unpack parameters from the parameter 'argVar'
596
- if (statement.node.params.length > 0) {
597
- usedArgVar = true;
598
- fnTopBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(statement.node.params), (0, _node.deepClone)(argVar))]));
599
-
600
- // Change bindings from 'param' to 'var'
601
- statement.get("params").forEach(function (param) {
602
- var ids = param.getBindingIdentifierPaths();
603
- // Loop over the record of binding identifiers
604
- for (var identifierName in ids) {
605
- var identifierPath = ids[identifierName];
606
- if (identifierPath.getFunctionParent() === statement) {
607
- var binding = statement.scope.getBinding(identifierName);
608
- if (binding) {
609
- binding.kind = "var";
610
- }
611
- }
612
- }
613
- });
614
- }
615
- currentBasicBlock = oldBasicBlock;
616
- return 0; // continue
617
- }
618
-
619
- // Convert IF statements into Basic Blocks
620
- if (statement.isIfStatement() && flattenIfStatements) {
621
- var test = statement.get("test");
622
- var consequent = statement.get("consequent");
623
- var alternate = statement.get("alternate");
624
-
625
- // Both consequent and alternate are blocks
626
- if (consequent.isBlockStatement() && (!alternate.node || alternate.isBlockStatement())) {
627
- me.changeData.ifStatements++;
628
- var consequentLabel = me.getPlaceholder();
629
- var alternateLabel = alternate.node ? me.getPlaceholder() : null;
630
- var afterPath = me.getPlaceholder();
631
- currentBasicBlock.insertAfter(t.ifStatement(test.node, GotoControlStatement(consequentLabel), alternateLabel ? GotoControlStatement(alternateLabel) : GotoControlStatement(afterPath)));
632
- var _oldBasicBlock = currentBasicBlock;
633
- endCurrentBasicBlock({
634
- jumpToNext: false,
635
- nextLabel: consequentLabel,
636
- nextBlockPath: consequent
637
- });
638
- flattenIntoBasicBlocks(consequent);
639
- currentBasicBlock.initializedScope = _oldBasicBlock.scopeManager;
640
- if (alternate.isBlockStatement()) {
641
- endCurrentBasicBlock({
642
- prevJumpTo: afterPath,
643
- nextLabel: alternateLabel,
644
- nextBlockPath: alternate
645
- });
646
- flattenIntoBasicBlocks(alternate);
647
- }
648
- endCurrentBasicBlock({
649
- prevJumpTo: afterPath,
650
- nextLabel: afterPath,
651
- nextBlockPath: _oldBasicBlock.parentPath
652
- });
653
- return 0; // continue
654
- }
655
- }
656
- if (Number(index) === body.length - 1 && statement.isExpressionStatement() && statement.findParent(function (p) {
657
- return p.isBlock();
658
- }) === blockPath && programPath // <- ONLY APPLY TO TOP-LEVEL NEVER FUNCTIONS!
659
- ) {
660
- // Return the result of the last expression for eval() purposes
661
- currentBasicBlock.insertAfter(t.returnStatement(statement.get("expression").node));
662
- return 0; // continue
663
- }
664
-
665
- // 3 or more statements should be split more
666
- if (currentBasicBlock.body.length > 1 && (0, _randomUtils.chance)(50 + currentBasicBlock.body.length)) {
667
- endCurrentBasicBlock({
668
- nextBlockPath: nextBlockPath
669
- });
670
- }
671
-
672
- // console.log(currentBasicBlock.thisPath.type);
673
- // console.log(currentBasicBlock.body);
674
- currentBasicBlock.body.push(statement.node);
675
- },
676
- _ret;
677
- for (var index in body) {
678
- _ret = _loop();
679
- if (_ret === 0) continue;
748
+ },
749
+ BooleanLiteral: {
750
+ exit: function exit(boolPath) {
751
+ // Don't mangle booleans in debug mode
752
+ if (isDebug || !mangleBooleanLiterals || me.isSkipped(boolPath)) return;
753
+ if (!isWithinSameFunction(boolPath)) return;
754
+ mangledLiteralsCreated++;
755
+ var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
756
+ var stateVar = stateVars[index];
757
+ var stateVarValue = currentStateValues[index];
758
+ var compareValue = (0, _randomUtils.choice)([(0, _randomUtils.getRandomInteger)(-250, 250), stateVarValue]);
759
+ var compareResult = stateVarValue === compareValue;
760
+ var newExpression = t.binaryExpression(boolPath.node.value === compareResult ? "==" : "!=", (0, _node.deepClone)(stateVar), (0, _node.numericLiteral)(compareValue));
761
+ (0, _astUtils.ensureComputedExpression)(boolPath);
762
+ boolPath.replaceWith(newExpression);
680
763
  }
681
- }
764
+ },
765
+ // Mangle numbers with the state values
766
+ NumericLiteral: {
767
+ exit: function exit(numPath) {
768
+ // Don't mangle numbers in debug mode
769
+ if (isDebug || !mangleNumericalLiterals || me.isSkipped(numPath)) return;
770
+ var num = numPath.node.value;
771
+ if (Math.floor(num) !== num || Math.abs(num) > 100000 || !Number.isFinite(num) || Number.isNaN(num)) return;
772
+ if (!isWithinSameFunction(numPath)) return;
773
+ if ((0, _randomUtils.chance)(50)) return;
774
+ mangledLiteralsCreated++;
775
+ var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
776
+ var stateVar = stateVars[index];
777
+
778
+ // num = 50
779
+ // stateVar = 30
780
+ // stateVar + 30
781
+
782
+ var diff = t.binaryExpression("+", (0, _node.deepClone)(stateVar), me.skip((0, _node.numericLiteral)(num - currentStateValues[index])));
783
+ (0, _astUtils.ensureComputedExpression)(numPath);
784
+ numPath.replaceWith(diff);
785
+ numPath.skip();
786
+ }
787
+ },
788
+ // Xor encrypt string literals
789
+ StringLiteral: {
790
+ exit: function exit(strPath) {
791
+ // Don't mangle strings in debug mode
792
+ if (isDebug || !mangleStringLiterals || me.isSkipped(strPath)) return;
793
+
794
+ // Don't accidentally break goto call expressions
795
+ var p = strPath.parentPath;
796
+ if (p !== null && p !== void 0 && p.isCallExpression() && t.isIdentifier(p.node.callee) && p.node.callee.name === gotoFunctionName) {
797
+ return;
798
+ }
799
+ var str = strPath.node.value;
800
+ if (str.length > 5000) return;
801
+ if (!isWithinSameFunction(strPath)) return;
802
+ if ((0, _randomUtils.chance)(50)) return;
803
+ var num = (0, _randomUtils.getRandomInteger)(-255, 255); // XOR string encrypt key
682
804
 
683
- // Convert our code into Basic Blocks
684
- flattenIntoBasicBlocks(blockPath.get("body"));
805
+ var encryptedStr = (0, _xorStringTemplate.xorEncodeString)(str, num);
806
+ var decryptedStr = (0, _xorStringTemplate.xorDecodeString)(encryptedStr, num);
685
807
 
686
- // Ensure always jumped to the Program end
687
- endCurrentBasicBlock({
688
- jumpToNext: true,
689
- nextLabel: endLabel,
690
- nextBlockPath: defaultBlockPath
691
- });
692
- basicBlocks.get(endLabel).allowWithDiscriminant = false;
693
- if (!isDebug && addDeadCode) {
694
- // DEAD CODE 1/3: Add fake chunks that are never reached
695
- var fakeChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
696
- for (var i = 0; i < fakeChunkCount; i++) {
697
- // These chunks just jump somewhere random, they are never executed
698
- // so it could contain any code
699
- var fakeBlock = new BasicBlock(me.getPlaceholder(), blockPath, {
700
- impossible: true
701
- });
702
- var fakeJump = void 0;
703
- do {
704
- fakeJump = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
705
- } while (fakeJump === fakeBlock.label);
706
- fakeBlock.insertAfter(GotoControlStatement(fakeJump));
707
- me.changeData.deadCode++;
808
+ // If decryption fails for some reason, skip
809
+ if (decryptedStr !== str) {
810
+ return;
811
+ }
812
+ mangledLiteralsCreated++;
813
+ var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
814
+ var stateVar = stateVars[index];
815
+
816
+ // Equals 'num' but entangled with current state var
817
+ var diff = t.binaryExpression("+", (0, _node.deepClone)(stateVar), me.skip((0, _node.numericLiteral)(num - currentStateValues[index])));
818
+ var newExpr = t.callExpression(t.identifier(xorFnName), [t.stringLiteral(encryptedStr), diff]);
819
+ (0, _astUtils.ensureComputedExpression)(strPath);
820
+ strPath.replaceWith(newExpr);
821
+ strPath.skip();
708
822
  }
709
-
710
- // DEAD CODE 2/3: Add fake jumps to really mess with deobfuscators
711
- // "irreducible control flow"
712
- basicBlocks.forEach(function (basicBlock) {
713
- if ((0, _randomUtils.chance)(30 - basicBlocks.size)) {
714
- var randomLabel = (0, _randomUtils.choice)(Array.from(basicBlocks.keys()));
715
-
716
- // The `false` literal will be mangled
717
- basicBlock.insertAfter(new _template["default"]("\n if({predicate}){\n {goto}\n }\n ").single({
718
- "goto": GotoControlStatement(randomLabel),
719
- predicate: basicBlock.createFalsePredicate()
720
- }));
721
- me.changeData.deadCode++;
823
+ },
824
+ Identifier: {
825
+ exit: function exit(path) {
826
+ if (!(0, _astUtils.isVariableIdentifier)(path)) return;
827
+ if (me.isSkipped(path)) return;
828
+ if (path.node[_constants.NO_RENAME] === cffIndex) return;
829
+ // For identifiers using implicit with discriminant, skip
830
+ if (path.node[_constants.WITH_STATEMENT]) return;
831
+ var identifierName = path.node.name;
832
+ if (identifierName === gotoFunctionName) return;
833
+ var binding = path.scope.getBinding(identifierName);
834
+ if (!binding) {
835
+ return;
722
836
  }
723
- });
724
- // DEAD CODE 3/3: Clone chunks but these chunks are never ran
725
- var cloneChunkCount = (0, _randomUtils.getRandomInteger)(1, 5);
726
- var _loop2 = function _loop2() {
727
- var randomChunk = (0, _randomUtils.choice)(Array.from(basicBlocks.values()));
728
-
729
- // Don't double define functions
730
- var hasDeclaration = randomChunk.body.find(function (stmt) {
731
- return t.isDeclaration(stmt);
732
- });
733
- if (!hasDeclaration) {
734
- var clonedChunk = new BasicBlock(me.getPlaceholder(), randomChunk.parentPath, {
735
- impossible: true
736
- });
737
- randomChunk.thisNode.body.map(function (x) {
738
- return (0, _node.deepClone)(x);
739
- }).forEach(function (node) {
740
- if (node.type === "EmptyStatement") return;
741
- clonedChunk.insertAfter(node);
742
- });
743
- me.changeData.deadCode++;
837
+ if (binding.kind === "var" || binding.kind === "let" || binding.kind === "const") {} else {
838
+ return;
744
839
  }
745
- };
746
- for (var _i2 = 0; _i2 < cloneChunkCount; _i2++) {
747
- _loop2();
748
- }
749
- }
750
-
751
- // Select scope managers for the with statement
752
- var _iterator = _createForOfIteratorHelper(basicBlocks.values()),
753
- _step;
754
- try {
755
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
756
- var _basicBlock2$initiali;
757
- var _basicBlock2 = _step.value;
758
- _basicBlock2.bestWithDiscriminant = (_basicBlock2$initiali = _basicBlock2.initializedScope) === null || _basicBlock2$initiali === void 0 ? void 0 : _basicBlock2$initiali.findBestWithDiscriminant(_basicBlock2);
759
- if (isDebug && _basicBlock2.withDiscriminant) {
760
- _basicBlock2.body.unshift(t.expressionStatement(t.stringLiteral("With " + _basicBlock2.withDiscriminant.propertyName)));
840
+ var scopeManager = scopeToScopeManager.get(binding.scope);
841
+ if (!scopeManager) return;
842
+ var newName = scopeManager.getNewName(identifierName, path.node);
843
+ var memberExpression = scopeManager.getMemberExpression(newName);
844
+ scopeManager.isNotUsed = false;
845
+ if ((0, _astUtils.isDefiningIdentifier)(path)) {
846
+ (0, _astUtils.replaceDefiningIdentifierToMemberExpression)(path, memberExpression);
847
+ return;
761
848
  }
762
- }
763
-
764
- /**
765
- * Stage 2: Transform 'goto' statements into valid JavaScript
766
- *
767
- * - 'goto' is replaced with equivalent state updates and break statements
768
- * - Original identifiers are converted into member expressions
769
- */
770
-
771
- // Remap 'GotoStatement' to actual state assignments and Break statements
772
- } catch (err) {
773
- _iterator.e(err);
774
- } finally {
775
- _iterator.f();
776
- }
777
- var _iterator2 = _createForOfIteratorHelper(basicBlocks.values()),
778
- _step2;
779
- try {
780
- var _loop4 = function _loop4() {
781
- var basicBlock = _step2.value;
782
- var currentStateValues = basicBlock.stateValues;
783
- // Wrap the statement in a Babel path to allow traversal
784
-
785
- var outerFn = (0, _astUtils.getParentFunctionOrProgram)(basicBlock.parentPath);
786
- function isWithinSameFunction(path) {
787
- var fn = (0, _astUtils.getParentFunctionOrProgram)(path);
788
- return fn.node === outerFn.node;
849
+ if (!path.container) return;
850
+ me.skip(memberExpression);
851
+ path.replaceWith(memberExpression);
852
+ path.skip();
853
+
854
+ // Preserve proper 'this' context when directly calling functions
855
+ // X.Y.Z() -> (1, X.Y.Z)()
856
+ if (path.parentPath.isCallExpression() && path.key === "callee") {
857
+ path.replaceWith(t.sequenceExpression([t.numericLiteral(1), path.node]));
789
858
  }
790
- var visitor = {
791
- BooleanLiteral: {
792
- exit: function exit(boolPath) {
793
- // Don't mangle booleans in debug mode
794
- if (isDebug || !mangleBooleanLiterals || me.isSkipped(boolPath)) return;
795
- if (!isWithinSameFunction(boolPath)) return;
796
- if ((0, _randomUtils.chance)(50 + mangledLiteralsCreated)) return;
797
- mangledLiteralsCreated++;
798
- var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
799
- var stateVar = stateVars[index];
800
- var stateVarValue = currentStateValues[index];
801
- var compareValue = (0, _randomUtils.choice)([(0, _randomUtils.getRandomInteger)(-250, 250), stateVarValue]);
802
- var compareResult = stateVarValue === compareValue;
803
- var newExpression = t.binaryExpression(boolPath.node.value === compareResult ? "==" : "!=", (0, _node.deepClone)(stateVar), (0, _node.numericLiteral)(compareValue));
804
- (0, _astUtils.ensureComputedExpression)(boolPath);
805
- boolPath.replaceWith(newExpression);
806
- }
807
- },
808
- // Mangle numbers with the state values
809
- NumericLiteral: {
810
- exit: function exit(numPath) {
811
- // Don't mangle numbers in debug mode
812
- if (isDebug || !mangleNumericalLiterals || me.isSkipped(numPath)) return;
813
- var num = numPath.node.value;
814
- if (Math.floor(num) !== num || Math.abs(num) > 100000 || !Number.isFinite(num) || Number.isNaN(num)) return;
815
- if (!isWithinSameFunction(numPath)) return;
816
- if ((0, _randomUtils.chance)(50 + mangledLiteralsCreated)) return;
817
- mangledLiteralsCreated++;
818
- var index = (0, _randomUtils.getRandomInteger)(0, stateVars.length - 1);
819
- var stateVar = stateVars[index];
820
-
821
- // num = 50
822
- // stateVar = 30
823
- // stateVar + 30
824
-
825
- var diff = t.binaryExpression("+", (0, _node.deepClone)(stateVar), me.skip((0, _node.numericLiteral)(num - currentStateValues[index])));
826
- (0, _astUtils.ensureComputedExpression)(numPath);
827
- numPath.replaceWith(diff);
828
- numPath.skip();
829
- }
830
- },
831
- Identifier: {
832
- exit: function exit(path) {
833
- if (!(0, _astUtils.isVariableIdentifier)(path)) return;
834
- if (me.isSkipped(path)) return;
835
- if (path.node[_constants.NO_RENAME] === cffIndex) return;
836
- // For identifiers using implicit with discriminant, skip
837
- if (path.node[_constants.WITH_STATEMENT]) return;
838
- var identifierName = path.node.name;
839
- if (identifierName === gotoFunctionName) return;
840
- var binding = path.scope.getBinding(identifierName);
841
- if (!binding) {
842
- return;
843
- }
844
- if (binding.kind === "var" || binding.kind === "let" || binding.kind === "const") {} else {
845
- return;
846
- }
847
- var scopeManager = scopeToScopeManager.get(binding.scope);
848
- if (!scopeManager) return;
849
- var newName = scopeManager.getNewName(identifierName, path.node);
850
- var memberExpression = scopeManager.getMemberExpression(newName);
851
- scopeManager.isNotUsed = false;
852
-
853
- // Scope object as with discriminant? Use identifier
854
- if (typeof basicBlock.withDiscriminant === "undefined") {
855
- var id = t.identifier(scopeManager.propertyName);
856
- id[_constants.WITH_STATEMENT] = true;
857
- id[_constants.NO_RENAME] = cffIndex;
858
- memberExpression = scopeManager.getMemberExpression(newName, id);
859
- }
860
- if ((0, _astUtils.isDefiningIdentifier)(path)) {
861
- (0, _astUtils.replaceDefiningIdentifierToMemberExpression)(path, memberExpression);
862
- return;
863
- }
864
- if (!path.container) return;
865
- if (basicBlock.withDiscriminant && basicBlock.withDiscriminant === scopeManager && basicBlock.withDiscriminant.hasOwnName(identifierName)) {
866
- // The defining mode must directly append to the scope object
867
- // Subsequent uses can use the identifier
868
- var isDefiningNode = path.node === binding.identifier;
869
- if (!isDefiningNode) {
870
- memberExpression = basicBlock.identifier(newName, scopeManager);
871
- }
872
- }
873
- me.skip(memberExpression);
874
- path.replaceWith(memberExpression);
875
- path.skip();
876
-
877
- // Preserve proper 'this' context when directly calling functions
878
- // X.Y.Z() -> (1, X.Y.Z)()
879
- if (path.parentPath.isCallExpression() && path.key === "callee") {
880
- path.replaceWith(t.sequenceExpression([t.numericLiteral(1), path.node]));
881
- }
882
- }
883
- },
884
- // Top-level returns set additional flag to indicate that the function has returned
885
- ReturnStatement: {
886
- exit: function exit(path) {
887
- var functionParent = path.getFunctionParent();
888
- if (!functionParent || functionParent.get("body") !== blockPath) return;
889
- var returnArgument = path.node.argument || t.identifier("undefined");
890
- path.node.argument = new _template["default"]("\n ({didReturnVar} = true, {returnArgument})\n ").expression({
891
- returnArgument: returnArgument,
892
- didReturnVar: (0, _node.deepClone)(_didReturnVar)
893
- });
894
- }
895
- },
896
- // goto() calls are replaced with state updates and break statements
897
- CallExpression: {
898
- exit: function exit(path) {
899
- if (t.isIdentifier(path.node.callee) && path.node.callee.name === gotoFunctionName) {
900
- var _path$node$arguments = _slicedToArray(path.node.arguments, 1),
901
- labelNode = _path$node$arguments[0];
902
- (0, _assert.ok)(t.isStringLiteral(labelNode));
903
- var _label = labelNode.value;
904
- var jumpBlock = basicBlocks.get(_label);
905
- (0, _assert.ok)(jumpBlock, "Label not found: " + _label);
906
- var newStateValues = jumpBlock.stateValues,
907
- newTotalState = jumpBlock.totalState;
908
- var assignments = [];
909
- if (jumpBlock.withDiscriminant) {
910
- assignments.push(t.assignmentExpression("=", (0, _node.deepClone)(withMemberExpression), jumpBlock.withDiscriminant.getScopeObject()));
911
- } else if (basicBlock.withDiscriminant) {
912
- // Reset the with discriminant to undefined using fake property
913
- // scope["fake"] -> undefined
914
-
915
- var fakeProperty = scopeNameGen.generate();
916
- assignments.push(t.assignmentExpression("=", (0, _node.deepClone)(withMemberExpression), t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(fakeProperty), true)));
917
- }
918
- var mutatingStateValues = _toConsumableArray(currentStateValues);
919
- var _loop5 = function _loop5(_i9) {
920
- var oldValue = currentStateValues[_i9];
921
- var newValue = newStateValues[_i9];
922
-
923
- // console.log(oldValue, newValue);
924
- if (oldValue === newValue) return 1; // continue
925
- // No diff needed if the value doesn't change
926
-
927
- var assignment = t.assignmentExpression("=", (0, _node.deepClone)(stateVars[_i9]), (0, _node.numericLiteral)(newValue));
928
- if (!isDebug && addRelativeAssignments) {
929
- // Use two level of diffs to create confusing code:
930
- // stateVar += anotherStateVar - diff
931
- // has the effect of:
932
- // stateVar = newState
933
- var mangledNumericLiteral = function mangledNumericLiteral(value) {
934
- var stateVarIndex;
935
- do {
936
- stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
937
- } while (stateVarIndex === _i9 && stateVars.length > 1);
938
- var stateVarId = stateVars[stateVarIndex];
939
- var stateVarValue = mutatingStateValues[stateVarIndex];
940
- var diff = stateVarValue - value;
941
- return t.binaryExpression("-", (0, _node.deepClone)(stateVarId), (0, _node.numericLiteral)(diff));
942
- };
943
- assignment = t.assignmentExpression("+=", (0, _node.deepClone)(stateVars[_i9]), mangledNumericLiteral(newValue - oldValue));
944
- }
945
- mutatingStateValues[_i9] = newValue;
946
- assignments.push(assignment);
947
- };
948
- for (var _i9 = 0; _i9 < stateVars.length; _i9++) {
949
- if (_loop5(_i9)) continue;
950
- }
951
-
952
- // Add debug label
953
- if (isDebug) {
954
- assignments.unshift(t.stringLiteral("Goto " + newTotalState));
955
- }
956
- path.parentPath.replaceWith(t.expressionStatement(t.sequenceExpression(assignments)))[0].skip();
957
-
958
- // Add break after updating state variables
959
- path.insertAfter(breakStatement());
960
- }
961
- }
962
- }
963
- };
964
- basicBlock.thisPath.traverse(visitor);
965
- };
966
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
967
- _loop4();
968
859
  }
969
-
970
- /**
971
- * Stage 3: Create a switch statement to handle the control flow
972
- *
973
- * - Add fake / impossible blocks
974
- * - Add fake / predicates to the switch cases tests
975
- */
976
-
977
- // Create global numbers for predicates
978
- } catch (err) {
979
- _iterator2.e(err);
980
- } finally {
981
- _iterator2.f();
982
- }
983
- var mainScope = basicBlocks.get(startLabel).scopeManager;
984
- var predicateNumbers = new Map();
985
- var predicateNumberCount = isDebug || !addPredicateTests ? 0 : (0, _randomUtils.getRandomInteger)(1, 4);
986
- for (var _i3 = 0; _i3 < predicateNumberCount; _i3++) {
987
- var name = mainScope.getNewName(me.getPlaceholder("predicate_" + _i3));
988
- var number = (0, _randomUtils.getRandomInteger)(-250, 250);
989
- predicateNumbers.set(name, number);
990
- }
991
- var predicateSymbol = Symbol("predicate");
992
- var createAssignment = function createAssignment(values) {
993
- var exprStmt = new _template["default"]("\n ({predicateVariables} = {values})\n ").single({
994
- predicateVariables: t.arrayPattern(Array.from(predicateNumbers.keys()).map(function (name) {
995
- return mainScope.getMemberExpression(name);
996
- })),
997
- values: t.arrayExpression(values.map(function (value) {
998
- return (0, _node.numericLiteral)(value);
999
- }))
1000
- });
1001
- exprStmt[predicateSymbol] = true;
1002
- return exprStmt;
1003
- };
1004
- basicBlocks.get(startLabel).body.unshift(createAssignment(Array.from(predicateNumbers.values())));
1005
-
1006
- // Add random assignments to impossible blocks
1007
- var fakeAssignmentCount = (0, _randomUtils.getRandomInteger)(1, 3);
1008
- for (var _i4 = 0; _i4 < fakeAssignmentCount; _i4++) {
1009
- var impossibleBlock = (0, _randomUtils.choice)(getImpossibleBasicBlocks());
1010
- if (impossibleBlock) {
1011
- var _impossibleBlock$body;
1012
- if ((_impossibleBlock$body = impossibleBlock.body[0]) !== null && _impossibleBlock$body !== void 0 && _impossibleBlock$body[predicateSymbol]) continue;
1013
- var fakeValues = new Array(predicateNumberCount).fill(0).map(function () {
1014
- return (0, _randomUtils.getRandomInteger)(-250, 250);
860
+ },
861
+ // Top-level returns set additional flag to indicate that the function has returned
862
+ ReturnStatement: {
863
+ exit: function exit(path) {
864
+ var functionParent = path.getFunctionParent();
865
+ if (!functionParent || functionParent.get("body") !== blockPath) return;
866
+ var returnArgument = path.node.argument || t.identifier("undefined");
867
+ path.node.argument = new _template["default"]("\n ({didReturnVar} = true, {returnArgument})\n ").expression({
868
+ returnArgument: returnArgument,
869
+ didReturnVar: (0, _node.deepClone)(_didReturnVar)
1015
870
  });
1016
- impossibleBlock.body.unshift(createAssignment(fakeValues));
1017
- }
1018
- }
1019
-
1020
- // Add scope initializations: scope["_0"] = {identity: "_0"}
1021
- var _iterator3 = _createForOfIteratorHelper(scopeToScopeManager.values()),
1022
- _step3;
1023
- try {
1024
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
1025
- var _scopeManager = _step3.value;
1026
- if (_scopeManager.isNotUsed) continue;
1027
- if (!_scopeManager.requiresInitializing) continue;
1028
- if (_scopeManager.initializingBasicBlock.label === startLabel) continue;
1029
- _scopeManager.initializingBasicBlock.body.unshift(_scopeManager.getInitializingStatement());
1030
- }
1031
- } catch (err) {
1032
- _iterator3.e(err);
1033
- } finally {
1034
- _iterator3.f();
1035
- }
1036
- var switchCases = [];
1037
- var blocks = Array.from(basicBlocks.values());
1038
- if (!isDebug && addFakeTests) {
1039
- (0, _randomUtils.shuffle)(blocks);
1040
- }
1041
- var _loop3 = function _loop3() {
1042
- var block = _blocks[_i5];
1043
- if (block.label === endLabel) {
1044
- // ok(block.body.length === 0);
1045
- return 1; // continue
1046
- }
1047
- var test = (0, _node.numericLiteral)(block.totalState);
1048
-
1049
- // Predicate tests cannot apply to the start label
1050
- // As that's when the numbers are initialized
1051
- if (!isDebug && addPredicateTests && block.label !== startLabel && (0, _randomUtils.chance)(50)) {
1052
- var predicateName = (0, _randomUtils.choice)(Array.from(predicateNumbers.keys()));
1053
- if (predicateName) {
1054
- var _number = predicateNumbers.get(predicateName);
1055
- var diff = block.totalState - _number;
1056
- test = t.binaryExpression("+", mainScope.getMemberExpression(predicateName), (0, _node.numericLiteral)(diff));
1057
- }
1058
871
  }
1059
-
1060
- // Add complex tests
1061
- if (!isDebug && addComplexTests && (0, _randomUtils.chance)(50)) {
1062
- // Create complex test expressions for each switch case
1063
-
1064
- // case STATE+X:
1065
- var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
1066
- var stateValues = block.stateValues;
1067
- var difference = stateValues[stateVarIndex] - block.totalState;
1068
- var conditionNodes = [];
1069
- var alreadyConditionedItems = new Set();
1070
-
1071
- // This code finds clash conditions and adds them to 'conditionNodes' array
1072
- Array.from(basicBlocks.keys()).forEach(function (label) {
1073
- if (label !== block.label) {
1074
- var labelStates = basicBlocks.get(label).stateValues;
1075
- var totalState = labelStates.reduce(function (a, b) {
1076
- return a + b;
1077
- }, 0);
1078
- if (totalState === labelStates[stateVarIndex] - difference) {
1079
- var differentIndex = labelStates.findIndex(function (v, i) {
1080
- return v !== stateValues[i];
1081
- });
1082
- if (differentIndex !== -1) {
1083
- var expressionAsString = stateVars[differentIndex].name + "!=" + labelStates[differentIndex];
1084
- if (!alreadyConditionedItems.has(expressionAsString)) {
1085
- alreadyConditionedItems.add(expressionAsString);
1086
- conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(stateVars[differentIndex]), (0, _node.numericLiteral)(labelStates[differentIndex])));
1087
- }
1088
- } else {
1089
- conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(totalState)));
1090
- }
872
+ },
873
+ // goto() calls are replaced with state updates and break statements
874
+ CallExpression: {
875
+ exit: function exit(path) {
876
+ if (t.isIdentifier(path.node.callee) && path.node.callee.name === gotoFunctionName) {
877
+ var _path$node$arguments = _slicedToArray(path.node.arguments, 1),
878
+ labelNode = _path$node$arguments[0];
879
+ (0, _assert.ok)(t.isStringLiteral(labelNode));
880
+ var _label = labelNode.value;
881
+ var jumpBlock = basicBlocks.get(_label);
882
+ (0, _assert.ok)(jumpBlock, "Label not found: " + _label);
883
+ var newStateValues = jumpBlock.stateValues,
884
+ newTotalState = jumpBlock.totalState;
885
+ var assignments = [];
886
+ var mutatingStateValues = _toConsumableArray(currentStateValues);
887
+ var _loop5 = function _loop5(_i8) {
888
+ var oldValue = currentStateValues[_i8];
889
+ var newValue = newStateValues[_i8];
890
+
891
+ // console.log(oldValue, newValue);
892
+ if (oldValue === newValue) return 1; // continue
893
+ // No diff needed if the value doesn't change
894
+
895
+ var assignment = t.assignmentExpression("=", (0, _node.deepClone)(stateVars[_i8]), (0, _node.numericLiteral)(newValue));
896
+ if (!isDebug && addRelativeAssignments) {
897
+ // Use two level of diffs to create confusing code:
898
+ // stateVar += anotherStateVar - diff
899
+ // has the effect of:
900
+ // stateVar = newState
901
+ var mangledNumericLiteral = function mangledNumericLiteral(value) {
902
+ var stateVarIndex;
903
+ do {
904
+ stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
905
+ } while (stateVarIndex === _i8 && stateVars.length > 1);
906
+ var stateVarId = stateVars[stateVarIndex];
907
+ var stateVarValue = mutatingStateValues[stateVarIndex];
908
+ var diff = stateVarValue - value;
909
+ return t.binaryExpression("-", (0, _node.deepClone)(stateVarId), (0, _node.numericLiteral)(diff));
910
+ };
911
+ assignment = t.assignmentExpression("+=", (0, _node.deepClone)(stateVars[_i8]), mangledNumericLiteral(newValue - oldValue));
1091
912
  }
913
+ mutatingStateValues[_i8] = newValue;
914
+ assignments.push(assignment);
915
+ };
916
+ for (var _i8 = 0; _i8 < stateVars.length; _i8++) {
917
+ if (_loop5(_i8)) continue;
1092
918
  }
1093
- });
1094
919
 
1095
- // case STATE!=Y && STATE+X
1096
- test = t.binaryExpression("-", (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(difference));
920
+ // Add debug label
921
+ if (isDebug) {
922
+ assignments.unshift(t.stringLiteral("Goto " + newTotalState));
923
+ }
924
+ path.parentPath.replaceWith(t.expressionStatement(t.sequenceExpression(assignments)))[0].skip();
1097
925
 
1098
- // Use the 'conditionNodes' to not cause state clashing issues
1099
- conditionNodes.forEach(function (conditionNode) {
1100
- test = t.logicalExpression("&&", conditionNode, test);
1101
- });
1102
- }
1103
- var tests = [test];
1104
- if (!isDebug && addFakeTests && (0, _randomUtils.chance)(50)) {
1105
- // Add fake tests
1106
- var fakeTestCount = (0, _randomUtils.getRandomInteger)(1, 3);
1107
- for (var _i6 = 0; _i6 < fakeTestCount; _i6++) {
1108
- tests.push((0, _node.numericLiteral)(stateIntGen.generate()));
926
+ // Add break after updating state variables
927
+ path.insertAfter(breakStatement());
1109
928
  }
1110
- (0, _randomUtils.shuffle)(tests);
1111
929
  }
1112
- var lastTest = tests.pop();
1113
- for (var _i7 = 0, _tests = tests; _i7 < _tests.length; _i7++) {
1114
- var _test = _tests[_i7];
1115
- switchCases.push(t.switchCase(_test, []));
1116
- }
1117
- switchCases.push(t.switchCase(lastTest, block.thisPath.node.body));
1118
- };
1119
- for (var _i5 = 0, _blocks = blocks; _i5 < _blocks.length; _i5++) {
1120
- if (_loop3()) continue;
1121
930
  }
1122
- if (!isDebug && addFakeTests) {
1123
- // A random test can be 'default'
1124
- (0, _randomUtils.choice)(switchCases).test = null;
1125
- }
1126
- var discriminant = new _template["default"]("\n ".concat(stateVars.map(function (x) {
1127
- return x.name;
1128
- }).join(" + "), "\n ")).expression();
1129
- (0, _traverse["default"])(t.program([t.expressionStatement(discriminant)]), {
1130
- Identifier: function Identifier(path) {
1131
- path.node[_constants.NO_RENAME] = cffIndex;
1132
- }
1133
- });
931
+ };
932
+ basicBlock.thisPath.traverse(visitor);
933
+ };
934
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
935
+ _loop4();
936
+ }
1134
937
 
1135
- // Create a new SwitchStatement
1136
- var switchStatement = t.labeledStatement(t.identifier(switchLabel), t.switchStatement(discriminant, switchCases));
1137
- var startStateValues = basicBlocks.get(startLabel).stateValues;
1138
- var endTotalState = basicBlocks.get(endLabel).totalState;
1139
- var whileStatement = t.whileStatement(t.binaryExpression("!==", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(endTotalState)), t.blockStatement([t.withStatement(new _template["default"]("{withDiscriminant} || {scopeVar}").expression({
1140
- withDiscriminant: (0, _node.deepClone)(withMemberExpression),
1141
- scopeVar: (0, _node.deepClone)(scopeVar)
1142
- }), t.blockStatement([switchStatement]))]));
1143
- var parameters = [].concat(_toConsumableArray(stateVars), [scopeVar, argVar]).map(function (id) {
1144
- return (0, _node.deepClone)(id);
1145
- });
1146
- var parametersNames = parameters.map(function (id) {
1147
- return id.name;
1148
- });
1149
- for (var _i8 = 0, _functionExpressions = functionExpressions; _i8 < _functionExpressions.length; _i8++) {
1150
- var _functionExpressions$ = _slicedToArray(_functionExpressions[_i8], 4),
1151
- originalFnName = _functionExpressions$[0],
1152
- fnLabel = _functionExpressions$[1],
1153
- basicBlock = _functionExpressions$[2],
1154
- fn = _functionExpressions$[3];
1155
- var _basicBlock = basicBlock,
1156
- scopeManager = _basicBlock.scopeManager;
1157
- var _basicBlocks$get = basicBlocks.get(fnLabel),
1158
- stateValues = _basicBlocks$get.stateValues;
1159
- var argumentsRestName = me.getPlaceholder();
1160
- var argumentsNodes = [];
1161
- var _iterator4 = _createForOfIteratorHelper(parametersNames),
1162
- _step4;
1163
- try {
1164
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
1165
- var parameterName = _step4.value;
1166
- var stateIndex = stateVars.map(function (x) {
1167
- return x.name;
1168
- }).indexOf(parameterName);
1169
- if (stateIndex !== -1) {
1170
- argumentsNodes.push((0, _node.numericLiteral)(stateValues[stateIndex]));
1171
- } else if (parameterName === argVar.name) {
1172
- argumentsNodes.push(t.identifier(argumentsRestName));
1173
- } else if (parameterName === scopeVar.name) {
1174
- argumentsNodes.push(scopeManager.getObjectExpression(fnLabel));
1175
- } else {
1176
- (0, _assert.ok)(false);
938
+ /**
939
+ * Stage 3: Create a switch statement to handle the control flow
940
+ *
941
+ * - Add fake / impossible blocks
942
+ * - Add fake / predicates to the switch cases tests
943
+ */
944
+
945
+ // Create global numbers for predicates
946
+ } catch (err) {
947
+ _iterator.e(err);
948
+ } finally {
949
+ _iterator.f();
950
+ }
951
+ var mainScope = basicBlocks.get(startLabel).scopeManager;
952
+ var predicateNumbers = new Map();
953
+ var predicateNumberCount = isDebug || !addPredicateTests ? 0 : (0, _randomUtils.getRandomInteger)(1, 4);
954
+ for (var _i2 = 0; _i2 < predicateNumberCount; _i2++) {
955
+ var name = mainScope.getNewName(me.getPlaceholder("predicate_" + _i2));
956
+ var number = (0, _randomUtils.getRandomInteger)(-250, 250);
957
+ predicateNumbers.set(name, number);
958
+ }
959
+ var predicateSymbol = Symbol("predicate");
960
+ var createAssignment = function createAssignment(values) {
961
+ var exprStmt = new _template["default"]("\n ({predicateVariables} = {values})\n ").single({
962
+ predicateVariables: t.arrayPattern(Array.from(predicateNumbers.keys()).map(function (name) {
963
+ return mainScope.getMemberExpression(name);
964
+ })),
965
+ values: t.arrayExpression(values.map(function (value) {
966
+ return (0, _node.numericLiteral)(value);
967
+ }))
968
+ });
969
+ exprStmt[predicateSymbol] = true;
970
+ return exprStmt;
971
+ };
972
+ basicBlocks.get(startLabel).body.unshift(createAssignment(Array.from(predicateNumbers.values())));
973
+
974
+ // Add random assignments to impossible blocks
975
+ var fakeAssignmentCount = (0, _randomUtils.getRandomInteger)(1, 3);
976
+ for (var _i3 = 0; _i3 < fakeAssignmentCount; _i3++) {
977
+ var impossibleBlock = (0, _randomUtils.choice)(getImpossibleBasicBlocks());
978
+ if (impossibleBlock) {
979
+ var _impossibleBlock$body;
980
+ if ((_impossibleBlock$body = impossibleBlock.body[0]) !== null && _impossibleBlock$body !== void 0 && _impossibleBlock$body[predicateSymbol]) continue;
981
+ var fakeValues = new Array(predicateNumberCount).fill(0).map(function () {
982
+ return (0, _randomUtils.getRandomInteger)(-250, 250);
983
+ });
984
+ impossibleBlock.body.unshift(createAssignment(fakeValues));
985
+ }
986
+ }
987
+
988
+ // Add scope initializations: scope["_0"] = {identity: "_0"}
989
+ var _iterator2 = _createForOfIteratorHelper(scopeToScopeManager.values()),
990
+ _step2;
991
+ try {
992
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
993
+ var _scopeManager = _step2.value;
994
+ if (_scopeManager.isNotUsed) continue;
995
+ if (!_scopeManager.requiresInitializing) continue;
996
+ if (_scopeManager.initializingBasicBlock.label === startLabel) continue;
997
+ _scopeManager.initializingBasicBlock.body.unshift(_scopeManager.getInitializingStatement());
998
+ }
999
+ } catch (err) {
1000
+ _iterator2.e(err);
1001
+ } finally {
1002
+ _iterator2.f();
1003
+ }
1004
+ var switchCases = [];
1005
+ var blocks = Array.from(basicBlocks.values());
1006
+ if (!isDebug && addFakeTests) {
1007
+ (0, _randomUtils.shuffle)(blocks);
1008
+ }
1009
+ var _loop3 = function _loop3() {
1010
+ var block = _blocks[_i4];
1011
+ if (block.label === endLabel) {
1012
+ // ok(block.body.length === 0);
1013
+ return 1; // continue
1014
+ }
1015
+ var test = (0, _node.numericLiteral)(block.totalState);
1016
+
1017
+ // Add complex tests
1018
+ if (!isDebug && addComplexTests && (0, _randomUtils.chance)(50)) {
1019
+ // Create complex test expressions for each switch case
1020
+
1021
+ // case STATE+X:
1022
+ var stateVarIndex = (0, _randomUtils.getRandomInteger)(0, stateVars.length);
1023
+ var stateValues = block.stateValues;
1024
+ var difference = stateValues[stateVarIndex] - block.totalState;
1025
+ var conditionNodes = [];
1026
+ var alreadyConditionedItems = new Set();
1027
+
1028
+ // This code finds clash conditions and adds them to 'conditionNodes' array
1029
+ Array.from(basicBlocks.keys()).forEach(function (label) {
1030
+ if (label !== block.label) {
1031
+ var labelStates = basicBlocks.get(label).stateValues;
1032
+ var totalState = labelStates.reduce(function (a, b) {
1033
+ return a + b;
1034
+ }, 0);
1035
+ if (totalState === labelStates[stateVarIndex] - difference) {
1036
+ var differentIndex = labelStates.findIndex(function (v, i) {
1037
+ return v !== stateValues[i];
1038
+ });
1039
+ if (differentIndex !== -1) {
1040
+ var expressionAsString = differentIndex + "!=" + labelStates[differentIndex];
1041
+ if (!alreadyConditionedItems.has(expressionAsString)) {
1042
+ alreadyConditionedItems.add(expressionAsString);
1043
+ conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(stateVars[differentIndex]), (0, _node.numericLiteral)(labelStates[differentIndex])));
1177
1044
  }
1045
+ } else {
1046
+ conditionNodes.push(t.binaryExpression("!=", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(totalState)));
1178
1047
  }
1179
-
1180
- // Ensure parameter is added (No effect if not added in this case)
1181
- } catch (err) {
1182
- _iterator4.e(err);
1183
- } finally {
1184
- _iterator4.f();
1185
1048
  }
1186
- usedArgVar = true;
1187
- Object.assign(fn, new _template["default"]("\n (function (...".concat(argumentsRestName, "){\n ").concat(isDebug ? "\"Calling ".concat(originalFnName, ", Label: ").concat(fnLabel, "\";") : "", "\n return {callExpression}\n })\n \n ")).expression({
1188
- callExpression: createCallExpression(argumentsNodes)
1189
- }));
1190
- }
1191
- var startProgramObjectExpression = basicBlocks.get(startLabel).scopeManager.getObjectExpression(startLabel);
1192
- var mainParameters = parameters;
1193
-
1194
- // First state values use the default parameter for initialization
1195
- // function main(..., scope = { mainScope: {} }, ...){...}
1196
- mainParameters.splice(mainParameters.findIndex(function (p) {
1197
- return p.name === scopeVar.name;
1198
- }), 1, t.assignmentPattern((0, _node.deepClone)(scopeVar), startProgramObjectExpression));
1199
-
1200
- // Remove parameter 'argVar' if never used (No function calls obfuscated)
1201
- if (!usedArgVar) {
1202
- mainParameters.pop();
1203
1049
  }
1204
- var mainFnDeclaration = t.functionDeclaration((0, _node.deepClone)(mainFnName), parameters, t.blockStatement([whileStatement]), addGeneratorFunction);
1205
-
1206
- // The main function is always called with same number of arguments
1207
- mainFnDeclaration[_constants.PREDICTABLE] = true;
1208
- function createCallExpression(argumentNodes) {
1209
- var callExpression = t.callExpression((0, _node.deepClone)(mainFnName), argumentNodes);
1210
- if (!addGeneratorFunction) {
1211
- return callExpression;
1212
- }
1213
- return new _template["default"]("\n ({callExpression})[\"next\"]()[\"value\"];\n ").expression({
1214
- callExpression: callExpression
1215
- });
1050
+ });
1051
+
1052
+ // case STATE!=Y && STATE+X
1053
+ test = t.binaryExpression("-", (0, _node.deepClone)(stateVars[stateVarIndex]), (0, _node.numericLiteral)(difference));
1054
+
1055
+ // Use the 'conditionNodes' to not cause state clashing issues
1056
+ conditionNodes.forEach(function (conditionNode) {
1057
+ test = t.logicalExpression("&&", conditionNode, test);
1058
+ });
1059
+ }
1060
+ var tests = [test];
1061
+ if (!isDebug && addFakeTests && (0, _randomUtils.chance)(50)) {
1062
+ // Add fake tests
1063
+ var fakeTestCount = (0, _randomUtils.getRandomInteger)(1, 3);
1064
+ for (var _i5 = 0; _i5 < fakeTestCount; _i5++) {
1065
+ tests.push((0, _node.numericLiteral)(stateIntGen.generate()));
1066
+ }
1067
+ (0, _randomUtils.shuffle)(tests);
1068
+ }
1069
+ var lastTest = tests.pop();
1070
+ for (var _i6 = 0, _tests = tests; _i6 < _tests.length; _i6++) {
1071
+ var _test = _tests[_i6];
1072
+ switchCases.push(t.switchCase(_test, []));
1073
+ }
1074
+
1075
+ // Preserve source location for source maps
1076
+ var sup = t.switchCase(lastTest, block.thisPath.node.body);
1077
+ var firstNode = block.thisPath.node.body[0];
1078
+ if (firstNode) {
1079
+ t.inherits(sup, firstNode);
1080
+ }
1081
+ switchCases.push(sup);
1082
+ };
1083
+ for (var _i4 = 0, _blocks = blocks; _i4 < _blocks.length; _i4++) {
1084
+ if (_loop3()) continue;
1085
+ }
1086
+ if (!isDebug && addFakeTests) {
1087
+ // A random test can be 'default'
1088
+ // choice(switchCases).test = null;
1089
+ }
1090
+ needsSumFunction = true;
1091
+ var discriminant = new _template["default"]("\n ".concat(sumFnName, "({statesVar})\n ")).expression({
1092
+ statesVar: (0, _node.deepClone)(statesVar)
1093
+ });
1094
+ (0, _traverse["default"])(t.program([t.expressionStatement(discriminant)]), {
1095
+ Identifier: function Identifier(path) {
1096
+ path.node[_constants.NO_RENAME] = cffIndex;
1097
+ }
1098
+ });
1099
+
1100
+ // Create a new SwitchStatement
1101
+ var switchStatement = t.inherits(t.labeledStatement(t.identifier(switchLabel), t.inherits(t.switchStatement((0, _node.deepClone)(discriminant), switchCases), programOrFunctionPath.node)), programOrFunctionPath.node);
1102
+ var startStateValues = basicBlocks.get(startLabel).stateValues;
1103
+ var endTotalState = basicBlocks.get(endLabel).totalState;
1104
+ var whileStatement = t.whileStatement(t.binaryExpression("!==", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(endTotalState)), t.inherits(t.blockStatement([switchStatement]), programOrFunctionPath.node));
1105
+ t.inherits(whileStatement, programOrFunctionPath.node);
1106
+ var parameters = [statesVar, scopeVar, runtimeVar, argVar].map(function (id) {
1107
+ return (0, _node.deepClone)(id);
1108
+ });
1109
+ var parametersNames = parameters.map(function (id) {
1110
+ return id.name;
1111
+ });
1112
+ for (var _i7 = 0, _functionExpressions = functionExpressions; _i7 < _functionExpressions.length; _i7++) {
1113
+ var _functionExpressions$ = _slicedToArray(_functionExpressions[_i7], 4),
1114
+ originalFnName = _functionExpressions$[0],
1115
+ fnLabel = _functionExpressions$[1],
1116
+ basicBlock = _functionExpressions$[2],
1117
+ fn = _functionExpressions$[3];
1118
+ var _basicBlock = basicBlock,
1119
+ scopeManager = _basicBlock.scopeManager;
1120
+ var _basicBlocks$get = basicBlocks.get(fnLabel),
1121
+ stateValues = _basicBlocks$get.stateValues;
1122
+ var argumentsRestName = me.getPlaceholder();
1123
+ var argumentsNodes = [];
1124
+ var _iterator3 = _createForOfIteratorHelper(parametersNames),
1125
+ _step3;
1126
+ try {
1127
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
1128
+ var parameterName = _step3.value;
1129
+ if (parameterName === statesVar.name) {
1130
+ continue;
1131
+ } else if (parameterName === runtimeVar.name) {
1132
+ argumentsNodes.push((0, _node.deepClone)(runtimeVar));
1133
+ } else if (parameterName === argVar.name) {
1134
+ argumentsNodes.push(t.identifier(argumentsRestName));
1135
+ } else if (parameterName === scopeVar.name) {
1136
+ argumentsNodes.push(scopeManager.getObjectExpression(fnLabel));
1137
+ } else {
1138
+ (0, _assert.ok)(false);
1216
1139
  }
1217
- var startProgramExpression = createCallExpression(startStateValues.map(function (stateValue) {
1218
- return (0, _node.numericLiteral)(stateValue);
1219
- }));
1220
- var _resultVar = withIdentifier("result");
1221
- var isTopLevel = blockPath.isProgram();
1222
- var allowReturns = !isTopLevel && blockPath.find(function (p) {
1223
- return p.isFunction();
1224
- });
1225
- var startPrefix = allowReturns ? "var {resultVar} = " : "";
1226
- var startProgramStatements = new _template["default"]("\n ".concat(allowReturns ? "var {didReturnVar};" : "", "\n ").concat(startPrefix, "{startProgramExpression};\n ").concat(allowReturns ? "\n if({didReturnVar}){\n return {resultVar};\n }" : "", "\n ")).compile({
1227
- startProgramExpression: startProgramExpression,
1228
- didReturnVar: function didReturnVar() {
1229
- return (0, _node.deepClone)(_didReturnVar);
1230
- },
1231
- resultVar: function resultVar() {
1232
- return (0, _node.deepClone)(_resultVar);
1233
- }
1140
+ }
1141
+
1142
+ // Ensure parameter is added (No effect if not added in this case)
1143
+ } catch (err) {
1144
+ _iterator3.e(err);
1145
+ } finally {
1146
+ _iterator3.f();
1147
+ }
1148
+ usedArgVar = true;
1149
+ Object.assign(fn, new _template["default"]("\n (function (...".concat(argumentsRestName, "){\n ").concat(isDebug ? "\"Calling ".concat(originalFnName, ", Label: ").concat(fnLabel, "\";") : "", "\n return {callExpression}\n })\n \n ")).expression({
1150
+ callExpression: createCallExpression(stateValues, argumentsNodes)
1151
+ }));
1152
+ }
1153
+ var startProgramObjectExpression = basicBlocks.get(startLabel).scopeManager.getObjectExpression(startLabel);
1154
+ var mainParameters = parameters;
1155
+
1156
+ // First state values use the default parameter for initialization
1157
+ // function main(..., scope = { mainScope: {} }, ...){...}
1158
+ mainParameters.splice(mainParameters.findIndex(function (p) {
1159
+ return p.name === scopeVar.name;
1160
+ }), 1, t.assignmentPattern((0, _node.deepClone)(scopeVar), startProgramObjectExpression));
1161
+
1162
+ // Remove parameter 'argVar' if never used (No function calls obfuscated)
1163
+ if (!usedArgVar) {
1164
+ mainParameters.pop();
1165
+ }
1166
+ var mainFnDeclaration = t.functionDeclaration((0, _node.deepClone)(mainFnName), parameters, t.inherits(t.blockStatement([whileStatement]), programOrFunctionPath.node), false);
1167
+ t.inherits(mainFnDeclaration, programOrFunctionPath.node);
1168
+
1169
+ // The main function is always called with same number of arguments
1170
+ mainFnDeclaration[_constants.PREDICTABLE] = true;
1171
+ function createCallExpression(stateValues) {
1172
+ var argumentNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
1173
+ var callExpression = t.callExpression((0, _node.deepClone)(mainFnName), [t.arrayExpression(stateValues.map(function (value) {
1174
+ return (0, _node.numericLiteral)(value);
1175
+ }))].concat(_toConsumableArray(argumentNodes)));
1176
+ return callExpression;
1177
+ }
1178
+ var startProgramExpression = createCallExpression(startStateValues);
1179
+ var _resultVar = identifier("result");
1180
+ var isTopLevel = blockPath.isProgram();
1181
+ var allowReturns = !isTopLevel && blockPath.find(function (p) {
1182
+ return p.isFunction();
1183
+ });
1184
+ var startPrefix = allowReturns ? "var {resultVar} = " : "";
1185
+ var startProgramStatements = new _template["default"]("\n ".concat(allowReturns ? "var {didReturnVar};" : "", "\n ").concat(startPrefix, "{startProgramExpression};\n ").concat(allowReturns ? "\n if({didReturnVar}){\n return {resultVar};\n }" : "", "\n ")).compile({
1186
+ startProgramExpression: startProgramExpression,
1187
+ didReturnVar: function didReturnVar() {
1188
+ return (0, _node.deepClone)(_didReturnVar);
1189
+ },
1190
+ resultVar: function resultVar() {
1191
+ return (0, _node.deepClone)(_resultVar);
1192
+ }
1193
+ });
1194
+ blockPath.node.body = [].concat(prependNodes, [mainFnDeclaration], _toConsumableArray(startProgramStatements));
1195
+ functionsModified.push(programOrFunctionPath.node);
1196
+
1197
+ // Reset all bindings here
1198
+ blockPath.scope.bindings = Object.create(null);
1199
+
1200
+ // Register new declarations
1201
+ var _iterator4 = _createForOfIteratorHelper(blockPath.get("body")),
1202
+ _step4;
1203
+ try {
1204
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
1205
+ var node = _step4.value;
1206
+ blockPath.scope.registerDeclaration(node);
1207
+ }
1208
+ } catch (err) {
1209
+ _iterator4.e(err);
1210
+ } finally {
1211
+ _iterator4.f();
1212
+ }
1213
+ }
1214
+ return {
1215
+ post: function post() {
1216
+ for (var _i9 = 0, _functionsModified = functionsModified; _i9 < _functionsModified.length; _i9++) {
1217
+ var node = _functionsModified[_i9];
1218
+ node[_constants.UNSAFE] = true;
1219
+ }
1220
+ },
1221
+ visitor: {
1222
+ // Unsafe detection
1223
+ ThisExpression: function ThisExpression(path) {
1224
+ flagFunctionToAvoid(path, "this");
1225
+ },
1226
+ VariableDeclaration: function VariableDeclaration(path) {
1227
+ if (path.node.declarations.length !== 1) {
1228
+ path.getAncestry().forEach(function (p) {
1229
+ p.node[CFF_UNSAFE] = "multipleDeclarations";
1234
1230
  });
1235
- blockPath.node.body = [].concat(prependNodes, [mainFnDeclaration], _toConsumableArray(startProgramStatements));
1236
- functionsModified.push(programOrFunctionPath.node);
1237
-
1238
- // Reset all bindings here
1239
- blockPath.scope.bindings = Object.create(null);
1240
-
1241
- // Register new declarations
1242
- var _iterator5 = _createForOfIteratorHelper(blockPath.get("body")),
1243
- _step5;
1244
- try {
1245
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
1246
- var node = _step5.value;
1247
- blockPath.scope.registerDeclaration(node);
1231
+ }
1232
+ },
1233
+ Identifier: function Identifier(path) {
1234
+ if (path.node.name === _constants.variableFunctionName || path.node.name === "arguments") {
1235
+ flagFunctionToAvoid(path, "arguments");
1236
+ }
1237
+ },
1238
+ "Super|MetaProperty|AwaitExpression|YieldExpression": function SuperMetaPropertyAwaitExpressionYieldExpression(path) {
1239
+ flagFunctionToAvoid(path, "functionSpecific");
1240
+ },
1241
+ // Main CFF transformation
1242
+ "Program|Function": {
1243
+ exit: function exit(_path) {
1244
+ cffMain(_path);
1245
+ if (_path.isProgram()) {
1246
+ _path.stop(); // This is necessary because we insert a new function and it will get traversed again here
1247
+
1248
+ if (needsSumFunction) {
1249
+ (0, _astUtils.prepend)(_path, new _template["default"]("\n function ".concat(sumFnName, "(array){\n for (var sum = 0, i = 0; i < array[\"length\"]; i++) {\n sum += array[i]\n }\n return sum;\n }\n ")).compile());
1250
+ (0, _astUtils.prepend)(_path, _xorStringTemplate.xorDecodeStringTemplate.compile({
1251
+ fnName: xorFnName
1252
+ }));
1248
1253
  }
1249
- } catch (err) {
1250
- _iterator5.e(err);
1251
- } finally {
1252
- _iterator5.f();
1253
1254
  }
1254
1255
  }
1255
1256
  }