js-confuser 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/dist/index.js +2 -1
- package/dist/obfuscator.js +21 -30
- package/dist/order.js +0 -2
- package/dist/presets.js +0 -3
- package/dist/templates/xorStringTemplate.js +33 -0
- package/dist/transforms/controlFlowFlattening.js +1108 -1107
- package/dist/transforms/dispatcher.js +3 -0
- package/dist/transforms/flatten.js +13 -2
- package/dist/transforms/identifier/renameVariables.js +0 -1
- package/dist/transforms/lock/integrity.js +2 -1
- package/dist/transforms/pack.js +4 -3
- package/dist/transforms/preparation.js +10 -1
- package/dist/transforms/rgf.js +2 -1
- package/dist/transforms/string/encoding.js +0 -6
- package/dist/transforms/string/stringConcealing.js +1 -7
- package/dist/utils/ast-utils.js +3 -1
- package/dist/utils/random-utils.js +10 -3
- package/dist/validateOptions.js +1 -1
- package/package.json +2 -1
- package/src/templates/xorStringTemplate.ts +1 -1
- package/src/transforms/controlFlowFlattening.ts +1 -1
|
@@ -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");
|
|
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
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
146
|
-
|
|
338
|
+
// Correct the value
|
|
339
|
+
this.stateValues[correctIndex] = this.totalState - (getCurrentState() - this.stateValues[correctIndex]);
|
|
340
|
+
(0, _assert.ok)(getCurrentState() === this.totalState);
|
|
147
341
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
381
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
445
|
-
|
|
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
|
-
|
|
455
|
-
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
684
|
-
|
|
805
|
+
var encryptedStr = (0, _xorStringTemplate.xorEncodeString)(str, num);
|
|
806
|
+
var decryptedStr = (0, _xorStringTemplate.xorDecodeString)(encryptedStr, num);
|
|
685
807
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
var
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
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
|
-
|
|
1099
|
-
|
|
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
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
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
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
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
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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
|
}
|