js-confuser 1.7.3 → 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
- package/CHANGELOG.md +70 -0
- package/Migration.md +57 -0
- package/README.md +23 -929
- package/dist/constants.js +65 -14
- package/dist/index.js +108 -160
- package/dist/obfuscator.js +316 -118
- package/dist/options.js +1 -119
- package/dist/order.js +30 -30
- package/dist/presets.js +47 -45
- package/dist/probability.js +25 -32
- package/dist/templates/bufferToStringTemplate.js +9 -0
- package/dist/templates/deadCodeTemplates.js +9 -0
- package/dist/templates/getGlobalTemplate.js +19 -0
- package/dist/templates/integrityTemplate.js +30 -0
- package/dist/templates/setFunctionLengthTemplate.js +9 -0
- package/dist/templates/stringCompressionTemplate.js +10 -0
- package/dist/templates/tamperProtectionTemplates.js +21 -0
- package/dist/templates/template.js +199 -184
- package/dist/transforms/astScrambler.js +100 -0
- package/dist/transforms/calculator.js +70 -127
- package/dist/transforms/controlFlowFlattening.js +1182 -0
- package/dist/transforms/deadCode.js +62 -587
- package/dist/transforms/dispatcher.js +300 -313
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
- package/dist/transforms/extraction/objectExtraction.js +131 -215
- package/dist/transforms/finalizer.js +56 -59
- package/dist/transforms/flatten.js +275 -276
- package/dist/transforms/functionOutlining.js +230 -0
- package/dist/transforms/identifier/globalConcealing.js +214 -135
- package/dist/transforms/identifier/movedDeclarations.js +167 -91
- package/dist/transforms/identifier/renameVariables.js +239 -193
- package/dist/transforms/lock/integrity.js +61 -184
- package/dist/transforms/lock/lock.js +261 -387
- package/dist/transforms/minify.js +431 -436
- package/dist/transforms/opaquePredicates.js +65 -118
- package/dist/transforms/pack.js +160 -0
- package/dist/transforms/plugin.js +179 -0
- package/dist/transforms/preparation.js +261 -173
- package/dist/transforms/renameLabels.js +132 -56
- package/dist/transforms/rgf.js +140 -267
- package/dist/transforms/shuffle.js +52 -145
- package/dist/transforms/string/encoding.js +44 -175
- package/dist/transforms/string/stringCompression.js +79 -155
- package/dist/transforms/string/stringConcealing.js +189 -225
- package/dist/transforms/string/stringEncoding.js +32 -40
- package/dist/transforms/string/stringSplitting.js +54 -55
- package/dist/transforms/variableMasking.js +232 -0
- package/dist/utils/ControlObject.js +125 -0
- package/dist/utils/IntGen.js +46 -0
- package/dist/utils/NameGen.js +106 -0
- package/dist/utils/ast-utils.js +560 -0
- package/dist/utils/function-utils.js +56 -0
- package/dist/utils/gen-utils.js +48 -0
- package/dist/utils/node.js +77 -0
- package/dist/utils/object-utils.js +21 -0
- package/dist/utils/random-utils.js +91 -0
- package/dist/utils/static-utils.js +64 -0
- package/dist/validateOptions.js +122 -0
- package/index.d.ts +1 -17
- package/package.json +27 -22
- package/src/constants.ts +139 -82
- package/src/index.ts +70 -165
- package/src/obfuscationResult.ts +43 -0
- package/src/obfuscator.ts +328 -135
- package/src/options.ts +149 -658
- package/src/order.ts +14 -14
- package/src/presets.ts +39 -34
- package/src/probability.ts +21 -36
- package/src/templates/bufferToStringTemplate.ts +57 -0
- package/src/templates/deadCodeTemplates.ts +1185 -0
- package/src/templates/getGlobalTemplate.ts +72 -0
- package/src/templates/integrityTemplate.ts +69 -0
- package/src/templates/setFunctionLengthTemplate.ts +11 -0
- package/src/templates/stringCompressionTemplate.ts +42 -0
- package/src/templates/tamperProtectionTemplates.ts +116 -0
- package/src/templates/template.ts +149 -157
- package/src/transforms/astScrambler.ts +99 -0
- package/src/transforms/calculator.ts +96 -226
- package/src/transforms/controlFlowFlattening.ts +1594 -0
- package/src/transforms/deadCode.ts +85 -676
- package/src/transforms/dispatcher.ts +431 -640
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -295
- package/src/transforms/extraction/objectExtraction.ts +160 -333
- package/src/transforms/finalizer.ts +63 -64
- package/src/transforms/flatten.ts +439 -557
- package/src/transforms/functionOutlining.ts +225 -0
- package/src/transforms/identifier/globalConcealing.ts +255 -266
- package/src/transforms/identifier/movedDeclarations.ts +228 -142
- package/src/transforms/identifier/renameVariables.ts +250 -271
- package/src/transforms/lock/integrity.ts +85 -263
- package/src/transforms/lock/lock.ts +338 -579
- package/src/transforms/minify.ts +523 -663
- package/src/transforms/opaquePredicates.ts +90 -229
- package/src/transforms/pack.ts +195 -0
- package/src/transforms/plugin.ts +185 -0
- package/src/transforms/preparation.ts +337 -231
- package/src/transforms/renameLabels.ts +176 -77
- package/src/transforms/rgf.ts +293 -424
- package/src/transforms/shuffle.ts +80 -254
- package/src/transforms/string/encoding.ts +20 -126
- package/src/transforms/string/stringCompression.ts +117 -307
- package/src/transforms/string/stringConcealing.ts +254 -342
- package/src/transforms/string/stringEncoding.ts +28 -47
- package/src/transforms/string/stringSplitting.ts +61 -75
- package/src/transforms/variableMasking.ts +257 -0
- package/src/utils/ControlObject.ts +141 -0
- package/src/utils/IntGen.ts +33 -0
- package/src/utils/NameGen.ts +106 -0
- package/src/utils/ast-utils.ts +667 -0
- package/src/utils/function-utils.ts +50 -0
- package/src/utils/gen-utils.ts +48 -0
- package/src/utils/node.ts +78 -0
- package/src/utils/object-utils.ts +21 -0
- package/src/utils/random-utils.ts +79 -0
- package/src/utils/static-utils.ts +66 -0
- package/src/validateOptions.ts +256 -0
- package/tsconfig.json +13 -8
- package/babel.config.js +0 -12
- package/dev.js +0 -8
- package/dist/compiler.js +0 -34
- package/dist/parser.js +0 -59
- package/dist/precedence.js +0 -66
- package/dist/templates/bufferToString.js +0 -129
- package/dist/templates/core.js +0 -35
- package/dist/templates/crash.js +0 -28
- package/dist/templates/es5.js +0 -137
- package/dist/templates/functionLength.js +0 -34
- package/dist/templates/globals.js +0 -9
- package/dist/transforms/antiTooling.js +0 -88
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +0 -1287
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +0 -131
- package/dist/transforms/es5/antiClass.js +0 -164
- package/dist/transforms/es5/antiDestructuring.js +0 -193
- package/dist/transforms/es5/antiES6Object.js +0 -185
- package/dist/transforms/es5/antiSpreadOperator.js +0 -35
- package/dist/transforms/es5/antiTemplate.js +0 -66
- package/dist/transforms/es5/es5.js +0 -123
- package/dist/transforms/extraction/classExtraction.js +0 -83
- package/dist/transforms/identifier/globalAnalysis.js +0 -83
- package/dist/transforms/identifier/variableAnalysis.js +0 -104
- package/dist/transforms/lock/antiDebug.js +0 -76
- package/dist/transforms/stack.js +0 -349
- package/dist/transforms/transform.js +0 -372
- package/dist/traverse.js +0 -110
- package/dist/util/compare.js +0 -145
- package/dist/util/gen.js +0 -564
- package/dist/util/guard.js +0 -14
- package/dist/util/identifiers.js +0 -355
- package/dist/util/insert.js +0 -362
- package/dist/util/math.js +0 -19
- package/dist/util/object.js +0 -40
- package/dist/util/random.js +0 -156
- package/dist/util/scope.js +0 -20
- package/docs/ControlFlowFlattening.md +0 -595
- package/docs/Countermeasures.md +0 -70
- package/docs/ES5.md +0 -197
- package/docs/Integrity.md +0 -82
- package/docs/RGF.md +0 -424
- package/docs/RenameVariables.md +0 -116
- package/docs/TamperProtection.md +0 -100
- package/docs/Template.md +0 -117
- package/samples/example.js +0 -15
- package/samples/high.js +0 -1
- package/samples/input.js +0 -3
- package/samples/javascriptobfuscator.com.js +0 -8
- package/samples/jscrambler_advanced.js +0 -1894
- package/samples/jscrambler_light.js +0 -1134
- package/samples/low.js +0 -1
- package/samples/medium.js +0 -1
- package/samples/obfuscator.io.js +0 -1686
- package/samples/preemptive.com.js +0 -16
- package/src/compiler.ts +0 -35
- package/src/parser.ts +0 -49
- package/src/precedence.ts +0 -61
- package/src/templates/bufferToString.ts +0 -136
- package/src/templates/core.ts +0 -29
- package/src/templates/crash.ts +0 -23
- package/src/templates/es5.ts +0 -131
- package/src/templates/functionLength.ts +0 -32
- package/src/templates/globals.ts +0 -3
- package/src/transforms/antiTooling.ts +0 -102
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2153
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
- package/src/transforms/es5/antiClass.ts +0 -276
- package/src/transforms/es5/antiDestructuring.ts +0 -294
- package/src/transforms/es5/antiES6Object.ts +0 -267
- package/src/transforms/es5/antiSpreadOperator.ts +0 -56
- package/src/transforms/es5/antiTemplate.ts +0 -98
- package/src/transforms/es5/es5.ts +0 -149
- package/src/transforms/extraction/classExtraction.ts +0 -168
- package/src/transforms/identifier/globalAnalysis.ts +0 -102
- package/src/transforms/identifier/variableAnalysis.ts +0 -118
- package/src/transforms/lock/antiDebug.ts +0 -112
- package/src/transforms/stack.ts +0 -557
- package/src/transforms/transform.ts +0 -441
- package/src/traverse.ts +0 -120
- package/src/types.ts +0 -133
- package/src/util/compare.ts +0 -181
- package/src/util/gen.ts +0 -651
- package/src/util/guard.ts +0 -17
- package/src/util/identifiers.ts +0 -494
- package/src/util/insert.ts +0 -419
- package/src/util/math.ts +0 -15
- package/src/util/object.ts +0 -39
- package/src/util/random.ts +0 -221
- package/src/util/scope.ts +0 -21
- package/test/code/Cash.src.js +0 -1011
- package/test/code/Cash.test.ts +0 -132
- package/test/code/Dynamic.src.js +0 -118
- package/test/code/Dynamic.test.ts +0 -49
- package/test/code/ES6.src.js +0 -235
- package/test/code/ES6.test.ts +0 -42
- package/test/code/NewFeatures.test.ts +0 -19
- package/test/code/StrictMode.src.js +0 -65
- package/test/code/StrictMode.test.js +0 -37
- package/test/compare.test.ts +0 -104
- package/test/index.test.ts +0 -249
- package/test/options.test.ts +0 -150
- package/test/presets.test.ts +0 -22
- package/test/probability.test.ts +0 -44
- package/test/templates/template.test.ts +0 -224
- package/test/transforms/antiTooling.test.ts +0 -52
- package/test/transforms/calculator.test.ts +0 -78
- package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
- package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
- package/test/transforms/deadCode.test.ts +0 -85
- package/test/transforms/dispatcher.test.ts +0 -457
- package/test/transforms/es5/antiClass.test.ts +0 -427
- package/test/transforms/es5/antiDestructuring.test.ts +0 -157
- package/test/transforms/es5/antiES6Object.test.ts +0 -245
- package/test/transforms/es5/antiTemplate.test.ts +0 -116
- package/test/transforms/es5/es5.test.ts +0 -110
- package/test/transforms/extraction/classExtraction.test.ts +0 -86
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
- package/test/transforms/extraction/objectExtraction.test.ts +0 -491
- package/test/transforms/flatten.test.ts +0 -721
- package/test/transforms/hexadecimalNumbers.test.ts +0 -62
- package/test/transforms/identifier/globalConcealing.test.ts +0 -142
- package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
- package/test/transforms/identifier/renameVariables.test.ts +0 -695
- package/test/transforms/lock/antiDebug.test.ts +0 -66
- package/test/transforms/lock/browserLock.test.ts +0 -129
- package/test/transforms/lock/countermeasures.test.ts +0 -100
- package/test/transforms/lock/integrity.test.ts +0 -161
- package/test/transforms/lock/lock.test.ts +0 -204
- package/test/transforms/lock/osLock.test.ts +0 -312
- package/test/transforms/lock/selfDefending.test.ts +0 -68
- package/test/transforms/lock/tamperProtection.test.ts +0 -336
- package/test/transforms/minify.test.ts +0 -575
- package/test/transforms/opaquePredicates.test.ts +0 -43
- package/test/transforms/preparation.test.ts +0 -157
- package/test/transforms/renameLabels.test.ts +0 -95
- package/test/transforms/rgf.test.ts +0 -378
- package/test/transforms/shuffle.test.ts +0 -135
- package/test/transforms/stack.test.ts +0 -573
- package/test/transforms/string/stringCompression.test.ts +0 -120
- package/test/transforms/string/stringConcealing.test.ts +0 -299
- package/test/transforms/string/stringEncoding.test.ts +0 -95
- package/test/transforms/string/stringSplitting.test.ts +0 -135
- package/test/transforms/transform.test.ts +0 -66
- package/test/traverse.test.ts +0 -139
- package/test/util/compare.test.ts +0 -34
- package/test/util/gen.test.ts +0 -121
- package/test/util/identifiers.test.ts +0 -253
- package/test/util/insert.test.ts +0 -142
- package/test/util/math.test.ts +0 -5
- package/test/util/random.test.ts +0 -71
- /package/dist/{types.js → obfuscationResult.js} +0 -0
|
@@ -1,1287 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
var _assert = require("assert");
|
|
8
|
-
var _order = require("../../order");
|
|
9
|
-
var _probability = require("../../probability");
|
|
10
|
-
var _template = _interopRequireDefault(require("../../templates/template"));
|
|
11
|
-
var _traverse = require("../../traverse");
|
|
12
|
-
var _gen = require("../../util/gen");
|
|
13
|
-
var _identifiers = require("../../util/identifiers");
|
|
14
|
-
var _insert = require("../../util/insert");
|
|
15
|
-
var _random = require("../../util/random");
|
|
16
|
-
var _transform = _interopRequireDefault(require("../transform"));
|
|
17
|
-
var _expressionObfuscation = _interopRequireDefault(require("./expressionObfuscation"));
|
|
18
|
-
var _constants = require("../../constants");
|
|
19
|
-
var _compare = require("../../util/compare");
|
|
20
|
-
var _guard = require("../../util/guard");
|
|
21
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
|
-
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
23
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
24
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
25
|
-
const flattenStructures = new Set(["IfStatement", "ForStatement", "WhileStatement", "DoWhileStatement"]);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* A chunk represents a small segment of code
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Breaks functions into DAGs (Directed Acyclic Graphs)
|
|
33
|
-
*
|
|
34
|
-
* - 1. Break functions into chunks
|
|
35
|
-
* - 2. Shuffle chunks but remember their original position
|
|
36
|
-
* - 3. Create a Switch statement inside a While loop, each case is a chunk, and the while loops exits on the last transition.
|
|
37
|
-
*
|
|
38
|
-
* The Switch statement:
|
|
39
|
-
*
|
|
40
|
-
* - 1. The state variable controls which case will run next
|
|
41
|
-
* - 2. At the end of each case, the state variable is updated to the next block of code.
|
|
42
|
-
* - 3. The while loop continues until the the state variable is the end state.
|
|
43
|
-
*/
|
|
44
|
-
class ControlFlowFlattening extends _transform.default {
|
|
45
|
-
constructor(o) {
|
|
46
|
-
super(o, _order.ObfuscateOrder.ControlFlowFlattening);
|
|
47
|
-
// in Debug mode, the output is much easier to read
|
|
48
|
-
_defineProperty(this, "isDebug", false);
|
|
49
|
-
_defineProperty(this, "flattenControlStructures", true);
|
|
50
|
-
// Flatten if-statements, for-loops, etc
|
|
51
|
-
_defineProperty(this, "addToControlObject", true);
|
|
52
|
-
// var control = { str1, num1 }
|
|
53
|
-
_defineProperty(this, "mangleNumberLiterals", true);
|
|
54
|
-
// 50 => state + X
|
|
55
|
-
_defineProperty(this, "mangleBooleanLiterals", true);
|
|
56
|
-
// true => state == X
|
|
57
|
-
_defineProperty(this, "mangleIdentifiers", true);
|
|
58
|
-
// console => (state == X ? console : _)
|
|
59
|
-
_defineProperty(this, "outlineStatements", true);
|
|
60
|
-
// Tries to outline entire chunks
|
|
61
|
-
_defineProperty(this, "outlineExpressions", true);
|
|
62
|
-
// Tries to outline expressions found in chunks
|
|
63
|
-
_defineProperty(this, "addComplexTest", true);
|
|
64
|
-
// case s != 49 && s - 10:
|
|
65
|
-
_defineProperty(this, "addFakeTest", true);
|
|
66
|
-
// case 100: case 490: case 510: ...
|
|
67
|
-
_defineProperty(this, "addDeadCode", true);
|
|
68
|
-
// add fakes chunks of code
|
|
69
|
-
_defineProperty(this, "addOpaquePredicates", true);
|
|
70
|
-
// predicate ? REAL : FAKE
|
|
71
|
-
_defineProperty(this, "addFlaggedLabels", true);
|
|
72
|
-
// s=NEXT_STATE,flag=true,break
|
|
73
|
-
// Limit amount of mangling
|
|
74
|
-
_defineProperty(this, "mangledExpressionsMade", 0);
|
|
75
|
-
// Amount of blocks changed by Control Flow Flattening
|
|
76
|
-
_defineProperty(this, "cffCount", 0);
|
|
77
|
-
if (!this.isDebug) {
|
|
78
|
-
this.before.push(new _expressionObfuscation.default(o));
|
|
79
|
-
} else {
|
|
80
|
-
console.warn("Debug mode enabled");
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
match(object, parents) {
|
|
84
|
-
return (0, _traverse.isBlock)(object) && (!parents[0] || !flattenStructures.has(parents[0].type)) && (!parents[1] || !flattenStructures.has(parents[1].type));
|
|
85
|
-
}
|
|
86
|
-
transform(object, parents) {
|
|
87
|
-
var _this = this;
|
|
88
|
-
// Must be at least 3 statements or more
|
|
89
|
-
if (object.body.length < 3) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// No 'let'/'const' allowed (These won't work in Switch cases!)
|
|
93
|
-
if ((0, _identifiers.containsLexicallyBoundVariables)(object, parents)) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
// Check user's threshold setting
|
|
97
|
-
if (!(0, _probability.ComputeProbabilityMap)(this.options.controlFlowFlattening, x => x)) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
var objectBody = (0, _insert.getBlockBody)(object.body);
|
|
101
|
-
if (!objectBody.length) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Purely for naming purposes
|
|
106
|
-
var cffIndex = this.cffCount++;
|
|
107
|
-
|
|
108
|
-
// The controlVar is an object containing:
|
|
109
|
-
// - Strings found in chunks
|
|
110
|
-
// - Numbers found in chunks
|
|
111
|
-
// - Helper functions to adjust the state
|
|
112
|
-
// - Outlined expressions changed into functions
|
|
113
|
-
var controlVar = this.getPlaceholder() + `_c${cffIndex}_CONTROL`;
|
|
114
|
-
var controlProperties = [];
|
|
115
|
-
var controlConstantMap = new Map();
|
|
116
|
-
var controlGen = this.getGenerator("mangled");
|
|
117
|
-
var controlTestKey = controlGen.generate();
|
|
118
|
-
|
|
119
|
-
// This 'controlVar' can be accessed by child-nodes
|
|
120
|
-
object.$controlVar = controlVar;
|
|
121
|
-
object.$controlConstantMap = controlConstantMap;
|
|
122
|
-
object.$controlProperties = controlProperties;
|
|
123
|
-
object.$controlGen = controlGen;
|
|
124
|
-
return () => {
|
|
125
|
-
(0, _assert.ok)(Array.isArray(objectBody));
|
|
126
|
-
|
|
127
|
-
// The state variable names (and quantity)
|
|
128
|
-
var stateVars = Array(this.isDebug ? 1 : (0, _random.getRandomInteger)(2, 5)).fill(0).map((_, i) => this.getPlaceholder() + `_c${cffIndex}_S${i}`);
|
|
129
|
-
|
|
130
|
-
// How often should chunks be split up?
|
|
131
|
-
// Percentage between 10% and 90% based on block size
|
|
132
|
-
var splitPercent = Math.max(10, 90 - objectBody.length * 5);
|
|
133
|
-
|
|
134
|
-
// Find functions and import declarations
|
|
135
|
-
var importDeclarations = [];
|
|
136
|
-
var functionDeclarationNames = new Set();
|
|
137
|
-
var functionDeclarationValues = new Map();
|
|
138
|
-
|
|
139
|
-
// Find all parent control-nodes
|
|
140
|
-
const allControlNodes = [object];
|
|
141
|
-
parents.filter(x => x.$controlVar).forEach(node => allControlNodes.push(node));
|
|
142
|
-
const addControlMapConstant = literalValue => {
|
|
143
|
-
var _selectedControlConst;
|
|
144
|
-
// Choose a random control node to add to
|
|
145
|
-
var controlNode = (0, _random.choice)(allControlNodes);
|
|
146
|
-
var selectedControlVar = controlNode.$controlVar;
|
|
147
|
-
var selectedControlConstantMap = controlNode.$controlConstantMap;
|
|
148
|
-
var selectedControlProperties = controlNode.$controlProperties;
|
|
149
|
-
var key = (_selectedControlConst = selectedControlConstantMap.get(literalValue)) === null || _selectedControlConst === void 0 ? void 0 : _selectedControlConst.key;
|
|
150
|
-
|
|
151
|
-
// Not found, create
|
|
152
|
-
if (!key) {
|
|
153
|
-
key = controlNode.$controlGen.generate();
|
|
154
|
-
selectedControlConstantMap.set(literalValue, {
|
|
155
|
-
key: key
|
|
156
|
-
});
|
|
157
|
-
selectedControlProperties.push((0, _gen.Property)((0, _gen.Literal)(key), (0, _gen.Literal)(literalValue), false));
|
|
158
|
-
}
|
|
159
|
-
return getControlMember(key, selectedControlVar);
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Helper function to easily make control object accessors
|
|
163
|
-
const getControlMember = function (key) {
|
|
164
|
-
let objectName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : controlVar;
|
|
165
|
-
return (0, _gen.MemberExpression)((0, _gen.Identifier)(objectName), (0, _gen.Literal)(key), true);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// This function recursively calls itself to flatten and split up code into 'chunks'
|
|
169
|
-
const flattenBody = (body, startingLabel) => {
|
|
170
|
-
var chunks = [];
|
|
171
|
-
var currentBody = [];
|
|
172
|
-
var currentLabel = startingLabel;
|
|
173
|
-
|
|
174
|
-
// This function ends the current chunk being created ('currentBody')
|
|
175
|
-
const finishCurrentChunk = function (pointingLabel, newLabel) {
|
|
176
|
-
let addGotoStatement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
177
|
-
if (!newLabel) {
|
|
178
|
-
newLabel = _this.getPlaceholder();
|
|
179
|
-
}
|
|
180
|
-
if (!pointingLabel) {
|
|
181
|
-
pointingLabel = newLabel;
|
|
182
|
-
}
|
|
183
|
-
if (addGotoStatement) {
|
|
184
|
-
currentBody.push({
|
|
185
|
-
type: "GotoStatement",
|
|
186
|
-
label: pointingLabel
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
chunks.push({
|
|
190
|
-
label: currentLabel,
|
|
191
|
-
body: [...currentBody]
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Random chance of this chunk being flagged (First label cannot be flagged)
|
|
195
|
-
if (!_this.isDebug && _this.addFlaggedLabels && currentLabel !== startLabel && (0, _random.chance)(25)) {
|
|
196
|
-
flaggedLabels[currentLabel] = {
|
|
197
|
-
flagKey: controlGen.generate(),
|
|
198
|
-
flagValue: (0, _random.choice)([true, false])
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
(0, _traverse.walk)(currentBody, [], (o, p) => {
|
|
202
|
-
if (o.type === "Literal" && !_this.isDebug) {
|
|
203
|
-
// Add strings to the control object
|
|
204
|
-
if (_this.addToControlObject && typeof o.value === "string" && o.value.length >= 3 && o.value.length <= 100 && !(0, _compare.isModuleSource)(o, p) && !(0, _compare.isDirective)(o, p) && !o.regex && (0, _random.chance)(50 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
|
|
205
|
-
return () => {
|
|
206
|
-
_this.replaceIdentifierOrLiteral(o, addControlMapConstant(o.value), p);
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Add numbers to the control object
|
|
211
|
-
if (_this.addToControlObject && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100_000 && (0, _random.chance)(50 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
|
|
212
|
-
return () => {
|
|
213
|
-
_this.replaceIdentifierOrLiteral(o, addControlMapConstant(o.value), p);
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
currentLabel = newLabel;
|
|
219
|
-
currentBody = [];
|
|
220
|
-
};
|
|
221
|
-
if (body !== objectBody) {
|
|
222
|
-
// This code is nested. Move function declarations up
|
|
223
|
-
|
|
224
|
-
var newBody = [];
|
|
225
|
-
for (var stmt of body) {
|
|
226
|
-
if (stmt.type === "FunctionDeclaration") {
|
|
227
|
-
newBody.unshift(stmt);
|
|
228
|
-
} else {
|
|
229
|
-
newBody.push(stmt);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
body = newBody;
|
|
233
|
-
}
|
|
234
|
-
body.forEach((stmt, i) => {
|
|
235
|
-
if (stmt.type === "ImportDeclaration") {
|
|
236
|
-
// The 'importDeclarations' hold statements that are required to be left untouched at the top of the block
|
|
237
|
-
importDeclarations.push(stmt);
|
|
238
|
-
return;
|
|
239
|
-
} else if (stmt.type === "FunctionDeclaration") {
|
|
240
|
-
var functionName = stmt.id.name;
|
|
241
|
-
stmt.type = "FunctionExpression";
|
|
242
|
-
stmt.id = null;
|
|
243
|
-
functionDeclarationNames.add(functionName);
|
|
244
|
-
if (objectBody === body) {
|
|
245
|
-
functionDeclarationValues.set(functionName, stmt);
|
|
246
|
-
return;
|
|
247
|
-
} else {
|
|
248
|
-
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(functionName), stmt)));
|
|
249
|
-
}
|
|
250
|
-
return;
|
|
251
|
-
} else if (stmt.directive) {
|
|
252
|
-
if (objectBody === body) {
|
|
253
|
-
importDeclarations.push(stmt);
|
|
254
|
-
} else {
|
|
255
|
-
this.error(new Error("Unimplemented directive support."));
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
if (stmt.type == "GotoStatement" && i !== body.length - 1) {
|
|
260
|
-
finishCurrentChunk(stmt.label);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// The Preparation transform adds labels to every Control-Flow node
|
|
265
|
-
if (this.flattenControlStructures && stmt.type == "LabeledStatement") {
|
|
266
|
-
var lbl = stmt.label.name;
|
|
267
|
-
var control = stmt.body;
|
|
268
|
-
var isSwitchStatement = control.type === "SwitchStatement";
|
|
269
|
-
if (isSwitchStatement || (control.type == "ForStatement" || control.type == "WhileStatement" || control.type == "DoWhileStatement") && control.body.type == "BlockStatement") {
|
|
270
|
-
if (isSwitchStatement) {
|
|
271
|
-
if (control.cases.length == 0) {
|
|
272
|
-
currentBody.push(stmt);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
var isLoop = !isSwitchStatement;
|
|
277
|
-
var supportContinueStatement = isLoop;
|
|
278
|
-
var testPath = this.getPlaceholder();
|
|
279
|
-
var updatePath = this.getPlaceholder();
|
|
280
|
-
var bodyPath = this.getPlaceholder();
|
|
281
|
-
var afterPath = this.getPlaceholder();
|
|
282
|
-
var possible = true;
|
|
283
|
-
var toReplace = [];
|
|
284
|
-
|
|
285
|
-
// Find all break; and continue; statements and change them into 'GotoStatement's
|
|
286
|
-
(0, _traverse.walk)(control.body || control.cases, [], (o, p) => {
|
|
287
|
-
if (o.type === "BreakStatement" || o.type === "ContinueStatement") {
|
|
288
|
-
var allowedLabels = new Set(p.filter(x => x.type === "LabeledStatement" && x.body.type === "SwitchStatement").map(x => x.label.name));
|
|
289
|
-
var isUnsupportedContinue = !supportContinueStatement && o.type === "ContinueStatement";
|
|
290
|
-
var isInvalidLabel = !o.label || o.label.name !== lbl && !allowedLabels.has(o.label.name);
|
|
291
|
-
|
|
292
|
-
// This seems like the best solution:
|
|
293
|
-
if (isUnsupportedContinue || isInvalidLabel) {
|
|
294
|
-
possible = false;
|
|
295
|
-
return "EXIT";
|
|
296
|
-
}
|
|
297
|
-
if (o.label.name === lbl) {
|
|
298
|
-
return () => {
|
|
299
|
-
toReplace.push([o, {
|
|
300
|
-
type: "GotoStatement",
|
|
301
|
-
label: o.type == "BreakStatement" ? afterPath : updatePath
|
|
302
|
-
}]);
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
if (!possible) {
|
|
308
|
-
currentBody.push(stmt);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
toReplace.forEach(v => this.replace(v[0], v[1]));
|
|
312
|
-
if (isSwitchStatement) {
|
|
313
|
-
var switchDiscriminantName = this.getPlaceholder() + "_switchD"; // Stores the value of the discriminant
|
|
314
|
-
var switchTestName = this.getPlaceholder() + "_switchT"; // Set to true when a Switch case is matched
|
|
315
|
-
|
|
316
|
-
currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchDiscriminantName, control.discriminant)));
|
|
317
|
-
currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchTestName, (0, _gen.Literal)(false))));
|
|
318
|
-
|
|
319
|
-
// case labels are:
|
|
320
|
-
// `${caseLabelPrefix}_test_${index}`
|
|
321
|
-
// `${caseLabelPrefix}_entry_${index}`
|
|
322
|
-
var caseLabelPrefix = this.getPlaceholder();
|
|
323
|
-
var defaultCaseIndex = control.cases.findIndex(x => x.test === null);
|
|
324
|
-
control.cases.forEach((switchCase, i) => {
|
|
325
|
-
var testPath = caseLabelPrefix + "_test_" + i;
|
|
326
|
-
var entryPath = caseLabelPrefix + "_entry_" + i;
|
|
327
|
-
var nextEntryPath = i === control.cases.length - 1 // Last path goes to afterPath
|
|
328
|
-
? afterPath // Else go to next entry path (fall-through behavior)
|
|
329
|
-
: caseLabelPrefix + "_entry_" + (i + 1);
|
|
330
|
-
var nextTestPath = i === control.cases.length - 1 ? afterPath : caseLabelPrefix + "_test_" + (i + 1);
|
|
331
|
-
finishCurrentChunk(testPath, testPath, i == 0);
|
|
332
|
-
if (switchCase.test) {
|
|
333
|
-
// Check the case condition and goto statement
|
|
334
|
-
currentBody.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("===", (0, _gen.Identifier)(switchDiscriminantName), switchCase.test), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(switchTestName), (0, _gen.Literal)(true))), {
|
|
335
|
-
type: "GotoStatement",
|
|
336
|
-
label: entryPath
|
|
337
|
-
}]));
|
|
338
|
-
} else {
|
|
339
|
-
// Default case: No test needed.
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// If default case, on last test, if no case was matched, goto default case
|
|
343
|
-
if (i === control.cases.length - 1 && defaultCaseIndex !== -1) {
|
|
344
|
-
currentBody.push((0, _gen.IfStatement)((0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(switchTestName)), [{
|
|
345
|
-
type: "GotoStatement",
|
|
346
|
-
label: caseLabelPrefix + "_entry_" + defaultCaseIndex
|
|
347
|
-
}]));
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Jump to next test
|
|
351
|
-
currentBody.push({
|
|
352
|
-
type: "GotoStatement",
|
|
353
|
-
label: nextTestPath
|
|
354
|
-
});
|
|
355
|
-
chunks.push(...flattenBody([...switchCase.consequent, {
|
|
356
|
-
type: "GotoStatement",
|
|
357
|
-
label: nextEntryPath
|
|
358
|
-
}], entryPath));
|
|
359
|
-
});
|
|
360
|
-
finishCurrentChunk(afterPath, afterPath, false);
|
|
361
|
-
return;
|
|
362
|
-
} else if (isLoop) {
|
|
363
|
-
var isPostTest = control.type == "DoWhileStatement";
|
|
364
|
-
|
|
365
|
-
// add initializing section to current chunk
|
|
366
|
-
if (control.init) {
|
|
367
|
-
if (control.init.type == "VariableDeclaration") {
|
|
368
|
-
currentBody.push(control.init);
|
|
369
|
-
} else {
|
|
370
|
-
currentBody.push((0, _gen.ExpressionStatement)(control.init));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// create new label called `testPath` and have current chunk point to it (goto testPath)
|
|
375
|
-
finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
|
|
376
|
-
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlTestKey), control.test || (0, _gen.Literal)(true))));
|
|
377
|
-
finishCurrentChunk();
|
|
378
|
-
currentBody.push((0, _gen.IfStatement)(getControlMember(controlTestKey), [{
|
|
379
|
-
type: "GotoStatement",
|
|
380
|
-
label: bodyPath
|
|
381
|
-
}]));
|
|
382
|
-
|
|
383
|
-
// create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
|
|
384
|
-
finishCurrentChunk(afterPath, bodyPath);
|
|
385
|
-
var innerBothPath = this.getPlaceholder();
|
|
386
|
-
chunks.push(...flattenBody([...control.body.body, {
|
|
387
|
-
type: "GotoStatement",
|
|
388
|
-
label: updatePath
|
|
389
|
-
}], innerBothPath));
|
|
390
|
-
finishCurrentChunk(innerBothPath, updatePath);
|
|
391
|
-
if (control.update) {
|
|
392
|
-
currentBody.push((0, _gen.ExpressionStatement)(control.update));
|
|
393
|
-
}
|
|
394
|
-
finishCurrentChunk(testPath, afterPath);
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (this.flattenControlStructures && stmt.type == "IfStatement" && stmt.consequent.type == "BlockStatement" && (!stmt.alternate || stmt.alternate.type == "BlockStatement")) {
|
|
400
|
-
finishCurrentChunk();
|
|
401
|
-
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlTestKey), stmt.test)));
|
|
402
|
-
finishCurrentChunk();
|
|
403
|
-
var hasAlternate = !!stmt.alternate;
|
|
404
|
-
(0, _assert.ok)(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
|
|
405
|
-
var yesPath = this.getPlaceholder();
|
|
406
|
-
var noPath = this.getPlaceholder();
|
|
407
|
-
var afterPath = this.getPlaceholder();
|
|
408
|
-
currentBody.push((0, _gen.IfStatement)(getControlMember(controlTestKey), [{
|
|
409
|
-
type: "GotoStatement",
|
|
410
|
-
label: yesPath
|
|
411
|
-
}]));
|
|
412
|
-
chunks.push(...flattenBody([...stmt.consequent.body, {
|
|
413
|
-
type: "GotoStatement",
|
|
414
|
-
label: afterPath
|
|
415
|
-
}], yesPath));
|
|
416
|
-
if (hasAlternate) {
|
|
417
|
-
chunks.push(...flattenBody([...stmt.alternate.body, {
|
|
418
|
-
type: "GotoStatement",
|
|
419
|
-
label: afterPath
|
|
420
|
-
}], noPath));
|
|
421
|
-
finishCurrentChunk(noPath, afterPath);
|
|
422
|
-
} else {
|
|
423
|
-
finishCurrentChunk(afterPath, afterPath);
|
|
424
|
-
}
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
if (!currentBody.length || !(0, _random.chance)(splitPercent)) {
|
|
428
|
-
currentBody.push(stmt);
|
|
429
|
-
} else {
|
|
430
|
-
// Start new chunk
|
|
431
|
-
finishCurrentChunk();
|
|
432
|
-
currentBody.push(stmt);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
finishCurrentChunk();
|
|
436
|
-
chunks[chunks.length - 1].body.pop();
|
|
437
|
-
return chunks;
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Executable code segments are broken down into `chunks` typically 1-3 statements each
|
|
442
|
-
*
|
|
443
|
-
* Chunked Code has a special `GotoStatement` node that get processed later on
|
|
444
|
-
* This allows more complex control structures like `IfStatement`s and `ForStatement`s to be converted into basic
|
|
445
|
-
* conditional jumps and flattened in the switch body
|
|
446
|
-
*
|
|
447
|
-
* IfStatement would be converted like this:
|
|
448
|
-
*
|
|
449
|
-
* MAIN:
|
|
450
|
-
* if ( TEST ) {
|
|
451
|
-
* GOTO consequent_label;
|
|
452
|
-
* } else? {
|
|
453
|
-
* GOTO alternate_label;
|
|
454
|
-
* }
|
|
455
|
-
* GOTO NEXT_CHUNK;
|
|
456
|
-
*/
|
|
457
|
-
const chunks = [];
|
|
458
|
-
|
|
459
|
-
// Flagged labels have addition code protecting the control state
|
|
460
|
-
const flaggedLabels = Object.create(null);
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* label: switch(a+b+c){...break label...}
|
|
464
|
-
*/
|
|
465
|
-
const switchLabel = this.getPlaceholder();
|
|
466
|
-
const startLabel = this.getPlaceholder();
|
|
467
|
-
chunks.push(...flattenBody(objectBody, startLabel));
|
|
468
|
-
chunks[chunks.length - 1].body.push({
|
|
469
|
-
type: "GotoStatement",
|
|
470
|
-
label: "END_LABEL"
|
|
471
|
-
});
|
|
472
|
-
chunks.push({
|
|
473
|
-
label: "END_LABEL",
|
|
474
|
-
body: []
|
|
475
|
-
});
|
|
476
|
-
const endLabel = chunks[Object.keys(chunks).length - 1].label;
|
|
477
|
-
if (!this.isDebug && this.addDeadCode) {
|
|
478
|
-
// DEAD CODE 1/3: Add fake chunks that are never reached
|
|
479
|
-
var fakeChunkCount = (0, _random.getRandomInteger)(1, 5);
|
|
480
|
-
for (var i = 0; i < fakeChunkCount; i++) {
|
|
481
|
-
// These chunks just jump somewhere random, they are never executed
|
|
482
|
-
// so it could contain any code
|
|
483
|
-
var fakeChunkBody = [
|
|
484
|
-
// This a fake assignment expression
|
|
485
|
-
(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Literal)((0, _random.getRandomInteger)(-150, 150)))), {
|
|
486
|
-
type: "GotoStatement",
|
|
487
|
-
label: (0, _random.choice)(chunks).label
|
|
488
|
-
}];
|
|
489
|
-
chunks.push({
|
|
490
|
-
label: this.getPlaceholder(),
|
|
491
|
-
body: fakeChunkBody,
|
|
492
|
-
impossible: true
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// DEAD CODE 2/3: Add fake jumps to really mess with deobfuscators
|
|
497
|
-
chunks.forEach(chunk => {
|
|
498
|
-
if ((0, _random.chance)(25)) {
|
|
499
|
-
var randomLabel = (0, _random.choice)(chunks).label;
|
|
500
|
-
|
|
501
|
-
// The `false` literal will be mangled
|
|
502
|
-
chunk.body.unshift((0, _gen.IfStatement)((0, _gen.Literal)(false), [{
|
|
503
|
-
type: "GotoStatement",
|
|
504
|
-
label: randomLabel,
|
|
505
|
-
impossible: true
|
|
506
|
-
}]));
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
// DEAD CODE 3/3: Clone chunks but these chunks are never ran
|
|
511
|
-
var cloneChunkCount = (0, _random.getRandomInteger)(1, 5);
|
|
512
|
-
for (var i = 0; i < cloneChunkCount; i++) {
|
|
513
|
-
var randomChunk = (0, _random.choice)(chunks);
|
|
514
|
-
var clonedChunk = {
|
|
515
|
-
body: (0, _insert.clone)(randomChunk.body),
|
|
516
|
-
label: this.getPlaceholder(),
|
|
517
|
-
impossible: true
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
// Don't double define functions
|
|
521
|
-
var hasDeclaration = clonedChunk.body.find(stmt => {
|
|
522
|
-
return stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration";
|
|
523
|
-
});
|
|
524
|
-
if (!hasDeclaration) {
|
|
525
|
-
chunks.unshift(clonedChunk);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Generate a unique 'state' number for each chunk
|
|
531
|
-
var caseSelection = new Set();
|
|
532
|
-
var uniqueStatesNeeded = chunks.length;
|
|
533
|
-
do {
|
|
534
|
-
var newState = (0, _random.getRandomInteger)(1, chunks.length * 15);
|
|
535
|
-
if (this.isDebug) {
|
|
536
|
-
newState = caseSelection.size;
|
|
537
|
-
}
|
|
538
|
-
caseSelection.add(newState);
|
|
539
|
-
} while (caseSelection.size !== uniqueStatesNeeded);
|
|
540
|
-
(0, _assert.ok)(caseSelection.size == uniqueStatesNeeded);
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* The accumulated state values
|
|
544
|
-
*
|
|
545
|
-
* index -> total state value
|
|
546
|
-
*/
|
|
547
|
-
var caseStates = Array.from(caseSelection);
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* The individual state values for each label
|
|
551
|
-
*
|
|
552
|
-
* labels right now are just chunk indexes (numbers)
|
|
553
|
-
*
|
|
554
|
-
* but will expand to if statements and functions when `goto statement` obfuscation is added
|
|
555
|
-
*/
|
|
556
|
-
var labelToStates = Object.create(null);
|
|
557
|
-
var lastLabel;
|
|
558
|
-
Object.values(chunks).forEach((chunk, i) => {
|
|
559
|
-
var state = caseStates[i];
|
|
560
|
-
var stateValues = Array(stateVars.length).fill(0).map((_, i) => lastLabel && (0, _random.chance)(95) // Try to make state changes not as drastic (If last label, re-use some of it's values)
|
|
561
|
-
? labelToStates[lastLabel][i] : (0, _random.getRandomInteger)(-500, 500));
|
|
562
|
-
const getCurrentState = () => {
|
|
563
|
-
return stateValues.reduce((a, b) => b + a, 0);
|
|
564
|
-
};
|
|
565
|
-
var correctIndex = (0, _random.getRandomInteger)(0, stateValues.length);
|
|
566
|
-
stateValues[correctIndex] = state - (getCurrentState() - stateValues[correctIndex]);
|
|
567
|
-
labelToStates[chunk.label] = stateValues;
|
|
568
|
-
lastLabel = chunk.label;
|
|
569
|
-
});
|
|
570
|
-
var initStateValues = [...labelToStates[startLabel]];
|
|
571
|
-
var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
|
|
572
|
-
|
|
573
|
-
// Creates a predicate based on the state-variables and control-object properties
|
|
574
|
-
const createPredicate = stateValues => {
|
|
575
|
-
this.mangledExpressionsMade++;
|
|
576
|
-
var index = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
577
|
-
var compareValue = (0, _random.choice)([stateValues[index], (0, _random.getRandomInteger)(-100, 100)]);
|
|
578
|
-
|
|
579
|
-
// 'state equality' test
|
|
580
|
-
var test = (0, _gen.BinaryExpression)("==", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(compareValue, stateValues));
|
|
581
|
-
var testValue = stateValues[index] === compareValue;
|
|
582
|
-
|
|
583
|
-
// 'control' equality test
|
|
584
|
-
if (controlConstantMap.size && (0, _random.chance)(50)) {
|
|
585
|
-
var _controlConstantMap$g;
|
|
586
|
-
// The controlMap maps LITERAL-values to STRING property names
|
|
587
|
-
var actualValue = (0, _random.choice)(Array.from(controlConstantMap.keys()));
|
|
588
|
-
var controlKey = (_controlConstantMap$g = controlConstantMap.get(actualValue)) === null || _controlConstantMap$g === void 0 ? void 0 : _controlConstantMap$g.key;
|
|
589
|
-
var controlCompareValue = (0, _random.choice)([actualValue, stateValues[index], (0, _random.getRandomInteger)(-100, 100), controlGen.generate()]);
|
|
590
|
-
|
|
591
|
-
// 'control equality' test
|
|
592
|
-
test = (0, _gen.BinaryExpression)("==", getControlMember(controlKey), (0, _gen.Literal)(controlCompareValue));
|
|
593
|
-
testValue = actualValue == controlCompareValue;
|
|
594
|
-
|
|
595
|
-
// 'control typeof' test
|
|
596
|
-
if ((0, _random.chance)(10)) {
|
|
597
|
-
var compareTypeofValue = (0, _random.choice)(["number", "string", "object", "function", "undefined"]);
|
|
598
|
-
test = (0, _gen.BinaryExpression)("==", (0, _gen.UnaryExpression)("typeof", getControlMember(controlKey)), (0, _gen.Literal)(compareTypeofValue));
|
|
599
|
-
testValue = typeof actualValue === compareTypeofValue;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// 'control hasOwnProperty' test
|
|
603
|
-
if ((0, _random.chance)(10)) {
|
|
604
|
-
var hasOwnProperty = (0, _random.choice)([controlKey, controlGen.generate()]);
|
|
605
|
-
test = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(controlVar), (0, _gen.Literal)("hasOwnProperty"), true), [(0, _gen.Literal)(hasOwnProperty)]);
|
|
606
|
-
testValue = hasOwnProperty === controlKey;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return {
|
|
610
|
-
test,
|
|
611
|
-
testValue
|
|
612
|
-
};
|
|
613
|
-
};
|
|
614
|
-
|
|
615
|
-
// A "state-less" number literal is a Number Literal that is mangled in with the Control properties.
|
|
616
|
-
// Example: X = CONTROL.Y + Z. These can be used anywhere because control properties are constant (unlike state variables)
|
|
617
|
-
const createStatelessNumberLiteral = function (num) {
|
|
618
|
-
var _selectedControlConst2;
|
|
619
|
-
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
620
|
-
if (!controlConstantMap.size || depth > 4 || (0, _random.chance)(75 + depth * 5 + _this.mangledExpressionsMade / 25)) {
|
|
621
|
-
// Add to control constant map?
|
|
622
|
-
if ((0, _random.chance)(25 - controlConstantMap.size - _this.mangledExpressionsMade / 100)) {
|
|
623
|
-
return addControlMapConstant(num);
|
|
624
|
-
}
|
|
625
|
-
return (0, _gen.Literal)(num);
|
|
626
|
-
}
|
|
627
|
-
_this.mangledExpressionsMade++;
|
|
628
|
-
if (controlConstantMap.has(num)) {
|
|
629
|
-
var _controlConstantMap$g2;
|
|
630
|
-
return getControlMember((_controlConstantMap$g2 = controlConstantMap.get(num)) === null || _controlConstantMap$g2 === void 0 ? void 0 : _controlConstantMap$g2.key);
|
|
631
|
-
}
|
|
632
|
-
var allControlNodes = [object];
|
|
633
|
-
parents.filter(x => x.$controlVar && x.$controlConstantMap.size > 0).forEach(node => allControlNodes.push(node));
|
|
634
|
-
var controlNode = (0, _random.choice)(allControlNodes);
|
|
635
|
-
var selectedControlConstantMap = controlNode.$controlConstantMap;
|
|
636
|
-
var selectedControlVar = controlNode.$controlVar;
|
|
637
|
-
var actualValue = (0, _random.choice)(Array.from(selectedControlConstantMap.keys()));
|
|
638
|
-
var controlKey = (_selectedControlConst2 = selectedControlConstantMap.get(actualValue)) === null || _selectedControlConst2 === void 0 ? void 0 : _selectedControlConst2.key;
|
|
639
|
-
if (typeof actualValue === "number") {
|
|
640
|
-
var difference = actualValue - num;
|
|
641
|
-
return (0, _gen.BinaryExpression)("-", getControlMember(controlKey, selectedControlVar), createStatelessNumberLiteral(difference, depth + 1));
|
|
642
|
-
} else if (typeof actualValue === "string") {
|
|
643
|
-
// 'control string length' test
|
|
644
|
-
var compareValue = (0, _random.choice)([actualValue.length, (0, _random.getRandomInteger)(0, 50)]);
|
|
645
|
-
var test = (0, _gen.BinaryExpression)("==", (0, _gen.MemberExpression)(getControlMember(controlKey, selectedControlVar), (0, _gen.Literal)("length"), true), createStatelessNumberLiteral(compareValue, depth + 1));
|
|
646
|
-
var testValue = actualValue.length == compareValue;
|
|
647
|
-
var consequent = createStatelessNumberLiteral(num, depth + 1);
|
|
648
|
-
var alternate = (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100));
|
|
649
|
-
return (0, _gen.ConditionalExpression)(test, testValue ? consequent : alternate, !testValue ? consequent : alternate);
|
|
650
|
-
} else {
|
|
651
|
-
throw new Error("Unknown: " + typeof actualValue);
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
// A "state-bound" number literal is a Number Literal that is mangled in with the current state variables
|
|
656
|
-
// Example: X = STATE + Y. This can only be used when the state-values are guaranteed to be known.
|
|
657
|
-
const createStateBoundNumberLiteral = function (num, stateValues) {
|
|
658
|
-
let depth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
659
|
-
(0, _assert.ok)(Array.isArray(stateValues));
|
|
660
|
-
|
|
661
|
-
// Base case: After 4 depth, OR random chance
|
|
662
|
-
if (depth > 4 || (0, _random.chance)(75 + depth * 5 + _this.mangledExpressionsMade / 25)) {
|
|
663
|
-
// Add this number to the control object?
|
|
664
|
-
// Add to control constant map?
|
|
665
|
-
if ((0, _random.chance)(25 - controlConstantMap.size)) {
|
|
666
|
-
return addControlMapConstant(num);
|
|
667
|
-
}
|
|
668
|
-
return (0, _gen.Literal)(num);
|
|
669
|
-
}
|
|
670
|
-
_this.mangledExpressionsMade++;
|
|
671
|
-
if ((0, _random.chance)(10)) {
|
|
672
|
-
return createStatelessNumberLiteral(num, depth + 1);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Terminated predicate
|
|
676
|
-
if ((0, _random.chance)(50)) {
|
|
677
|
-
var {
|
|
678
|
-
test,
|
|
679
|
-
testValue
|
|
680
|
-
} = createPredicate(stateValues);
|
|
681
|
-
var alternateNode = (0, _random.choice)([(0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100)), (0, _gen.Literal)(controlGen.generate()), getControlMember(controlGen.generate())]);
|
|
682
|
-
return (0, _gen.ConditionalExpression)(test, testValue ? (0, _gen.Literal)(num) : alternateNode, !testValue ? (0, _gen.Literal)(num) : alternateNode);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Recursive predicate
|
|
686
|
-
var opposing = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
687
|
-
if ((0, _random.chance)(10)) {
|
|
688
|
-
// state > compare ? real : fake
|
|
689
|
-
|
|
690
|
-
var compareValue = (0, _random.choice)([stateValues[opposing], (0, _random.getRandomInteger)(-150, 150)]);
|
|
691
|
-
var operator = (0, _random.choice)(["<", ">", "==", "!="]);
|
|
692
|
-
var answer = {
|
|
693
|
-
">": compareValue > stateValues[opposing],
|
|
694
|
-
"<": compareValue < stateValues[opposing],
|
|
695
|
-
"==": compareValue === stateValues[opposing],
|
|
696
|
-
"!=": compareValue !== stateValues[opposing]
|
|
697
|
-
}[operator];
|
|
698
|
-
var correct = createStateBoundNumberLiteral(num, stateValues, depth + 1);
|
|
699
|
-
var incorrect = createStateBoundNumberLiteral((0, _random.getRandomInteger)(-150, 150), stateValues, depth + 1);
|
|
700
|
-
return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, createStateBoundNumberLiteral(compareValue, stateValues, depth + 1), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// state + 10 = <REAL>
|
|
704
|
-
var difference = num - stateValues[opposing];
|
|
705
|
-
if (difference === 0) {
|
|
706
|
-
return (0, _gen.Identifier)(stateVars[opposing]);
|
|
707
|
-
}
|
|
708
|
-
return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), createStateBoundNumberLiteral(difference, stateValues, depth + 1));
|
|
709
|
-
};
|
|
710
|
-
var outlinesCreated = 0;
|
|
711
|
-
const isExpression = (object, parents) => {
|
|
712
|
-
var fnIndex = parents.findIndex(x => (0, _insert.isFunction)(x));
|
|
713
|
-
if (fnIndex != -1) {
|
|
714
|
-
// This does NOT mutate
|
|
715
|
-
parents = parents.slice(0, fnIndex);
|
|
716
|
-
}
|
|
717
|
-
var assignmentIndex = parents.findIndex(x => x.type === "AssignmentExpression");
|
|
718
|
-
|
|
719
|
-
// Left-hand assignment validation
|
|
720
|
-
if (assignmentIndex != -1) {
|
|
721
|
-
if (parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object)) {
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
// For in/of left validation
|
|
727
|
-
var forInOfIndex = parents.findIndex(x => x.type === "ForInStatement" || x.type === "ForOfStatement");
|
|
728
|
-
if (forInOfIndex != -1) {
|
|
729
|
-
if (parents[forInOfIndex].left === (parents[forInOfIndex - 1] || object)) {
|
|
730
|
-
return false;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Bound call-expression validation
|
|
735
|
-
var callExpressionIndex = parents.findIndex(x => x.type === "CallExpression");
|
|
736
|
-
if (callExpressionIndex != -1) {
|
|
737
|
-
if (parents[callExpressionIndex].callee == (parents[callExpressionIndex - 1] || object)) {
|
|
738
|
-
var callee = parents[callExpressionIndex].callee;
|
|
739
|
-
|
|
740
|
-
// Detected bound call expression. Not supported.
|
|
741
|
-
if (callee.type === "MemberExpression") {
|
|
742
|
-
return false;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
// Update-expression validation:
|
|
748
|
-
var updateExpressionIndex = parents.findIndex(x => x.type === "UpdateExpression");
|
|
749
|
-
if (updateExpressionIndex !== -1) return false;
|
|
750
|
-
return true;
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
// This function checks if the expression or statements is possible to be outlined
|
|
754
|
-
const canOutline = (object, parents) => {
|
|
755
|
-
var isIllegal = false;
|
|
756
|
-
var breakStatements = [];
|
|
757
|
-
var returnStatements = [];
|
|
758
|
-
if (!Array.isArray(object) && !isExpression(object, parents)) {
|
|
759
|
-
return {
|
|
760
|
-
isIllegal: true,
|
|
761
|
-
breakStatements: [],
|
|
762
|
-
returnStatements: []
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
(0, _traverse.walk)(object, parents, (o, p) => {
|
|
766
|
-
if (o.type === "ThisExpression" || o.type === "MetaProperty" || o.type === "Super") {
|
|
767
|
-
isIllegal = true;
|
|
768
|
-
return "EXIT";
|
|
769
|
-
}
|
|
770
|
-
if (o.type === "BreakStatement") {
|
|
771
|
-
// This can be safely outlined
|
|
772
|
-
if (o.label && o.label.name === switchLabel) {
|
|
773
|
-
breakStatements.push([o, p]);
|
|
774
|
-
} else {
|
|
775
|
-
isIllegal = true;
|
|
776
|
-
return "EXIT";
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
if ((o.type === "ContinueStatement" || o.type === "AwaitExpression" || o.type === "YieldExpression" || o.type === "ReturnStatement" || o.type === "VariableDeclaration" || o.type === "FunctionDeclaration" || o.type === "ClassDeclaration") && !p.find(x => (0, _insert.isVarContext)(x))) {
|
|
780
|
-
// This can be safely outlined
|
|
781
|
-
if (o.type === "ReturnStatement") {
|
|
782
|
-
returnStatements.push([o, p]);
|
|
783
|
-
} else {
|
|
784
|
-
isIllegal = true;
|
|
785
|
-
return "EXIT";
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
if (o.type === "Identifier") {
|
|
789
|
-
if (o.name === "arguments") {
|
|
790
|
-
isIllegal = true;
|
|
791
|
-
return "EXIT";
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
return {
|
|
796
|
-
isIllegal,
|
|
797
|
-
breakStatements,
|
|
798
|
-
returnStatements
|
|
799
|
-
};
|
|
800
|
-
};
|
|
801
|
-
const createOutlineFunction = (body, stateValues, label) => {
|
|
802
|
-
var key = controlGen.generate();
|
|
803
|
-
var functionExpression = (0, _gen.FunctionExpression)([], body);
|
|
804
|
-
if (!this.options.es5 && (0, _random.chance)(50)) {
|
|
805
|
-
functionExpression.type = "ArrowFunctionExpression";
|
|
806
|
-
}
|
|
807
|
-
controlProperties.push((0, _gen.Property)((0, _gen.Literal)(key), functionExpression, false));
|
|
808
|
-
|
|
809
|
-
// Add dead code to function
|
|
810
|
-
if (!this.isDebug && (0, _random.chance)(25)) {
|
|
811
|
-
var {
|
|
812
|
-
test,
|
|
813
|
-
testValue
|
|
814
|
-
} = createPredicate(stateValues);
|
|
815
|
-
var deadCodeVar = this.getPlaceholder();
|
|
816
|
-
functionExpression.params.push((0, _gen.AssignmentPattern)((0, _gen.Identifier)(deadCodeVar), test));
|
|
817
|
-
var alternate = [(0, _gen.ReturnStatement)((0, _random.choice)([(0, _gen.BinaryExpression)("==", (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100))), (0, _gen.Literal)(controlGen.generate()), (0, _gen.Identifier)("arguments"), (0, _gen.Identifier)((0, _random.choice)(stateVars)), (0, _gen.Identifier)(controlVar), (0, _gen.CallExpression)(getControlMember(controlGen.generate()), [])]))];
|
|
818
|
-
functionExpression.body.body.unshift((0, _gen.IfStatement)(testValue ? (0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(deadCodeVar)) : (0, _gen.Identifier)(deadCodeVar), alternate));
|
|
819
|
-
}
|
|
820
|
-
outlinesCreated++;
|
|
821
|
-
return key;
|
|
822
|
-
};
|
|
823
|
-
const attemptOutlineStatements = (statements, parentBlock, stateValues, label) => {
|
|
824
|
-
if (this.isDebug || !this.outlineStatements || (0, _random.chance)(75 + outlinesCreated - this.mangledExpressionsMade / 25)) {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
var index = parentBlock.indexOf(statements[0]);
|
|
828
|
-
if (index === -1) return;
|
|
829
|
-
var outlineInfo = canOutline(statements, parentBlock);
|
|
830
|
-
if (outlineInfo.isIllegal) return;
|
|
831
|
-
var breakFlag = controlGen.generate();
|
|
832
|
-
outlineInfo.breakStatements.forEach(_ref => {
|
|
833
|
-
let [breakStatement, p] = _ref;
|
|
834
|
-
this.replace(breakStatement, (0, _gen.ReturnStatement)((0, _gen.Literal)(breakFlag)));
|
|
835
|
-
});
|
|
836
|
-
var returnFlag = controlGen.generate();
|
|
837
|
-
outlineInfo.returnStatements.forEach(_ref2 => {
|
|
838
|
-
let [returnStatement, p] = _ref2;
|
|
839
|
-
var argument = returnStatement.argument || (0, _gen.Identifier)("undefined");
|
|
840
|
-
this.replace(returnStatement, (0, _gen.ReturnStatement)((0, _gen.ObjectExpression)([(0, _gen.Property)((0, _gen.Literal)(returnFlag), argument, false)])));
|
|
841
|
-
});
|
|
842
|
-
|
|
843
|
-
// Outline these statements!
|
|
844
|
-
var key = createOutlineFunction((0, _insert.clone)(statements), stateValues, label);
|
|
845
|
-
var callExpression = (0, _gen.CallExpression)(getControlMember(key), []);
|
|
846
|
-
var newStatements = [];
|
|
847
|
-
if (outlineInfo.breakStatements.length === 0 && outlineInfo.returnStatements.length === 0) {
|
|
848
|
-
newStatements.push((0, _gen.ExpressionStatement)(callExpression));
|
|
849
|
-
} else if (outlineInfo.returnStatements.length === 0) {
|
|
850
|
-
newStatements.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("==", callExpression, (0, _gen.Literal)(breakFlag)), [(0, _gen.BreakStatement)(switchLabel)]));
|
|
851
|
-
} else {
|
|
852
|
-
var tempVar = this.getPlaceholder();
|
|
853
|
-
newStatements.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(tempVar, callExpression)));
|
|
854
|
-
const t = str => new _template.default(str).single().expression;
|
|
855
|
-
newStatements.push((0, _gen.IfStatement)(t(`${tempVar} === "${breakFlag}"`), [(0, _gen.BreakStatement)(switchLabel)], [(0, _gen.IfStatement)(t(`typeof ${tempVar} == "object"`), [(0, _gen.ReturnStatement)(t(`${tempVar}["${returnFlag}"]`))])]));
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// Remove the original statements from the block and replace it with the call expression
|
|
859
|
-
parentBlock.splice(index, statements.length, ...newStatements);
|
|
860
|
-
};
|
|
861
|
-
const attemptOutlineExpression = (expression, expressionParents, stateValues, label) => {
|
|
862
|
-
if (this.isDebug || !this.outlineExpressions || (0, _random.chance)(75 + outlinesCreated - this.mangledExpressionsMade / 25)) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
var outlineInfo = canOutline(expression, expressionParents);
|
|
866
|
-
if (outlineInfo.isIllegal || outlineInfo.breakStatements.length || outlineInfo.returnStatements.length) return;
|
|
867
|
-
|
|
868
|
-
// Outline this expression!
|
|
869
|
-
var key = createOutlineFunction([(0, _gen.ReturnStatement)((0, _insert.clone)(expression))], stateValues, label);
|
|
870
|
-
var callExpression = (0, _gen.CallExpression)(getControlMember(key), []);
|
|
871
|
-
this.replaceIdentifierOrLiteral(expression, callExpression, expressionParents);
|
|
872
|
-
};
|
|
873
|
-
const createTransitionExpression = (index, add, mutatingStateValues, label) => {
|
|
874
|
-
var beforeStateValues = [...mutatingStateValues];
|
|
875
|
-
var newValue = mutatingStateValues[index] + add;
|
|
876
|
-
var expr = null;
|
|
877
|
-
if (this.isDebug) {
|
|
878
|
-
// state = NEW_STATE
|
|
879
|
-
expr = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(stateVars[index]), (0, _gen.Literal)(newValue));
|
|
880
|
-
} else if ((0, _random.chance)(90)) {
|
|
881
|
-
// state += (NEW_STATE - CURRENT_STATE)
|
|
882
|
-
expr = (0, _gen.AssignmentExpression)("+=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(add, mutatingStateValues));
|
|
883
|
-
} else {
|
|
884
|
-
// state *= 2
|
|
885
|
-
// state -= DIFFERENCE
|
|
886
|
-
var double = mutatingStateValues[index] * 2;
|
|
887
|
-
var diff = double - newValue;
|
|
888
|
-
var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(2, mutatingStateValues));
|
|
889
|
-
mutatingStateValues[index] = double;
|
|
890
|
-
expr = (0, _gen.SequenceExpression)([first, (0, _gen.AssignmentExpression)("-=", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(diff, mutatingStateValues))]);
|
|
891
|
-
}
|
|
892
|
-
mutatingStateValues[index] = newValue;
|
|
893
|
-
|
|
894
|
-
// These are lower quality outlines vs. the entire transition outline
|
|
895
|
-
if ((0, _random.chance)(50)) {
|
|
896
|
-
attemptOutlineExpression(expr, [], [...beforeStateValues], label);
|
|
897
|
-
}
|
|
898
|
-
return expr;
|
|
899
|
-
};
|
|
900
|
-
var cases = [];
|
|
901
|
-
chunks.forEach((chunk, i) => {
|
|
902
|
-
// skip last case, its empty and never ran
|
|
903
|
-
if (chunk.label === endLabel) {
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
(0, _assert.ok)(labelToStates[chunk.label]);
|
|
907
|
-
var state = caseStates[i];
|
|
908
|
-
var staticStateValues = [...labelToStates[chunk.label]];
|
|
909
|
-
var potentialBranches = new Set();
|
|
910
|
-
[...chunk.body].forEach(stmt => {
|
|
911
|
-
(0, _traverse.walk)(stmt, [], (o, p) => {
|
|
912
|
-
// This mangles certain literals with the state variables
|
|
913
|
-
// Ex: A number literal (50) changed to a expression (stateVar + 40), when stateVar = 10
|
|
914
|
-
if (!this.isDebug && o.type === "Literal" && !p.find(x => (0, _insert.isVarContext)(x))) {
|
|
915
|
-
if (typeof o.value === "number" && Math.floor(o.value) === o.value &&
|
|
916
|
-
// Only whole numbers
|
|
917
|
-
Math.abs(o.value) < 100_000 &&
|
|
918
|
-
// Hard-coded limit
|
|
919
|
-
this.mangleNumberLiterals && (0, _random.chance)(50 - this.mangledExpressionsMade / 100)) {
|
|
920
|
-
// 50 -> state1 - 10, when state1 = 60. The result is still 50
|
|
921
|
-
|
|
922
|
-
return () => {
|
|
923
|
-
this.replaceIdentifierOrLiteral(o, createStateBoundNumberLiteral(o.value, staticStateValues), p);
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
if (typeof o.value === "boolean" && this.mangleBooleanLiterals && (0, _random.chance)(50 - this.mangledExpressionsMade / 100)) {
|
|
927
|
-
// true -> state1 == 10, when state1 = 10. The result is still true
|
|
928
|
-
|
|
929
|
-
// Choose a random state var to compare again
|
|
930
|
-
var index = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
931
|
-
var compareValue = staticStateValues[index];
|
|
932
|
-
|
|
933
|
-
// When false, always choose a different number, so the expression always equals false
|
|
934
|
-
while (!o.value && compareValue === staticStateValues[index]) {
|
|
935
|
-
compareValue = (0, _random.getRandomInteger)(-150, 150);
|
|
936
|
-
}
|
|
937
|
-
var mangledExpression = (0, _gen.BinaryExpression)("==", (0, _gen.Identifier)(stateVars[index]), createStateBoundNumberLiteral(compareValue, staticStateValues));
|
|
938
|
-
return () => {
|
|
939
|
-
this.replaceIdentifierOrLiteral(o, mangledExpression, p);
|
|
940
|
-
attemptOutlineExpression(o, p, staticStateValues, chunk.label);
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// Mangle certain referenced identifiers
|
|
946
|
-
// console.log("hi") -> (x ? console : window).log("hi"), when is x true. The result is the same
|
|
947
|
-
if (!this.isDebug && o.type === "Identifier" && this.mangleIdentifiers && !_constants.reservedIdentifiers.has(o.name) && (0, _random.chance)(50 - this.mangledExpressionsMade / 100) && !p.find(x => (0, _insert.isVarContext)(x))) {
|
|
948
|
-
// ONLY referenced identifiers (like actual variable names) can be changed
|
|
949
|
-
var info = (0, _identifiers.getIdentifierInfo)(o, p);
|
|
950
|
-
if (!info.spec.isReferenced || info.spec.isDefined || info.spec.isModified || info.spec.isExported) {
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Ignore __JS_CONFUSER_VAR__()
|
|
955
|
-
if ((0, _guard.isJSConfuserVar)(p)) {
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// TYPEOF expression check
|
|
960
|
-
if (p[0] && p[0].type === "UnaryExpression" && p[0].operator === "typeof" && p[0].argument === o) {
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Update expression check
|
|
965
|
-
if (p[0] && p[0].type === "UpdateExpression") {
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// FOR-in/of initializer check
|
|
970
|
-
if ((0, _insert.isForInitialize)(o, p) === "left-hand") {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
var {
|
|
974
|
-
test,
|
|
975
|
-
testValue
|
|
976
|
-
} = createPredicate(staticStateValues);
|
|
977
|
-
|
|
978
|
-
// test && real
|
|
979
|
-
var mangledExpression = (0, _gen.LogicalExpression)(testValue ? "&&" : "||", test, (0, _gen.Identifier)(o.name));
|
|
980
|
-
|
|
981
|
-
// control.fake = real
|
|
982
|
-
if ((0, _random.chance)(50)) {
|
|
983
|
-
mangledExpression = (0, _gen.AssignmentExpression)("=", getControlMember(controlGen.generate()), (0, _gen.Identifier)(o.name));
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// test ? real : fake
|
|
987
|
-
if ((0, _random.chance)(50)) {
|
|
988
|
-
var alternateName = (0, _random.choice)([controlVar, ...stateVars, ...this.options.globalVariables, ..._constants.reservedIdentifiers]);
|
|
989
|
-
|
|
990
|
-
// Don't use 'arguments'
|
|
991
|
-
if (alternateName === "arguments") alternateName = "undefined";
|
|
992
|
-
mangledExpression = (0, _gen.ConditionalExpression)(test, (0, _gen.Identifier)(testValue ? o.name : alternateName), (0, _gen.Identifier)(!testValue ? o.name : alternateName));
|
|
993
|
-
}
|
|
994
|
-
return () => {
|
|
995
|
-
this.replaceIdentifierOrLiteral(o, mangledExpression, p);
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
// Function outlining: bring out certain expressions
|
|
1000
|
-
if (!this.isDebug && o.type && ["BinaryExpression", "LogicalExpression", "CallExpression", "AssignmentExpression", "MemberExpression", "ObjectExpression", "ConditionalExpression"].includes(o.type) && !(0, _random.chance)(p.length * 5) &&
|
|
1001
|
-
// The further down the tree the lower quality of expression
|
|
1002
|
-
!p.find(x => (0, _insert.isContext)(x) || x.$outlining)) {
|
|
1003
|
-
o.$outlining = true;
|
|
1004
|
-
return () => {
|
|
1005
|
-
attemptOutlineExpression(o, p, staticStateValues, chunk.label);
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// Opaque predicates: If Statements, Conditional Statements, Switch Case test
|
|
1010
|
-
if (!this.isDebug && this.addOpaquePredicates && p[0] && (0, _random.chance)(50 - outlinesCreated - this.mangledExpressionsMade / 100)) {
|
|
1011
|
-
var isTestExpression = p[0].type == "IfStatement" && p[0].test === o || p[0].type === "ConditionalExpression" && p[0].test === o || p[0].type === "SwitchCase" && p[0].test === o;
|
|
1012
|
-
if (isTestExpression && !p.find(x => (0, _insert.isContext)(x))) {
|
|
1013
|
-
return () => {
|
|
1014
|
-
var {
|
|
1015
|
-
test,
|
|
1016
|
-
testValue
|
|
1017
|
-
} = createPredicate(staticStateValues);
|
|
1018
|
-
this.replace(o, (0, _gen.LogicalExpression)(testValue ? "&&" : "||", test, (0, _insert.clone)(o)));
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
if (o.type == "StateIdentifier") {
|
|
1023
|
-
return () => {
|
|
1024
|
-
(0, _assert.ok)(labelToStates[o.label]);
|
|
1025
|
-
this.replace(o, (0, _gen.ArrayExpression)(labelToStates[o.label].map(_gen.Literal)));
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
if (o.type == "GotoStatement") {
|
|
1029
|
-
return () => {
|
|
1030
|
-
var blockIndex = p.findIndex(node => (0, _traverse.isBlock)(node) || node.type === "SwitchCase");
|
|
1031
|
-
if (blockIndex === -1) {
|
|
1032
|
-
var index = chunk.body.indexOf(stmt);
|
|
1033
|
-
(0, _assert.ok)(index != -1);
|
|
1034
|
-
|
|
1035
|
-
// Top level: Insert break statement in the chunk body
|
|
1036
|
-
// This is OKAY because this forEach uses a cloned version of the body `[...chunk.body]`
|
|
1037
|
-
chunk.body.splice(index + 1, 0, (0, _gen.BreakStatement)(switchLabel));
|
|
1038
|
-
} else {
|
|
1039
|
-
var block = p[blockIndex];
|
|
1040
|
-
if (block.type === "SwitchCase") {
|
|
1041
|
-
// Handle switch case break placement (Important!)
|
|
1042
|
-
block.consequent.splice(block.consequent.indexOf(p[blockIndex - 2] || o) + 1, 0, (0, _gen.BreakStatement)(switchLabel));
|
|
1043
|
-
} else {
|
|
1044
|
-
// Standard block placement
|
|
1045
|
-
var child = p[blockIndex - 2] || o;
|
|
1046
|
-
var childIndex = block.body.indexOf(child);
|
|
1047
|
-
block.body.splice(childIndex + 1, 0, (0, _gen.BreakStatement)(switchLabel));
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
if (!o.impossible) {
|
|
1051
|
-
potentialBranches.add(o.label);
|
|
1052
|
-
}
|
|
1053
|
-
var mutatingStateValues = [...labelToStates[chunk.label]];
|
|
1054
|
-
var nextStateValues = labelToStates[o.label];
|
|
1055
|
-
(0, _assert.ok)(nextStateValues, o.label);
|
|
1056
|
-
var transitionExpressions = [];
|
|
1057
|
-
for (var stateValueIndex = 0; stateValueIndex < stateVars.length; stateValueIndex++) {
|
|
1058
|
-
var diff = nextStateValues[stateValueIndex] - mutatingStateValues[stateValueIndex];
|
|
1059
|
-
|
|
1060
|
-
// Only add if state value changed
|
|
1061
|
-
// If pointing to itself then always add to ensure SequenceExpression isn't empty
|
|
1062
|
-
if (diff !== 0 || o.label === chunk.label) {
|
|
1063
|
-
transitionExpressions.push(createTransitionExpression(stateValueIndex, diff, mutatingStateValues, chunk.label));
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
(0, _assert.ok)(transitionExpressions.length !== 0);
|
|
1067
|
-
var sequenceExpression = (0, _gen.SequenceExpression)(transitionExpressions);
|
|
1068
|
-
|
|
1069
|
-
// Check if flagged and additional code here
|
|
1070
|
-
if (typeof flaggedLabels[o.label] === "object") {
|
|
1071
|
-
var {
|
|
1072
|
-
flagKey,
|
|
1073
|
-
flagValue
|
|
1074
|
-
} = flaggedLabels[o.label];
|
|
1075
|
-
sequenceExpression.expressions.push((0, _gen.AssignmentExpression)("=", getControlMember(flagKey), (0, _gen.Literal)(flagValue)));
|
|
1076
|
-
}
|
|
1077
|
-
attemptOutlineExpression(sequenceExpression, [], staticStateValues, chunk.label);
|
|
1078
|
-
this.replace(o, (0, _gen.ExpressionStatement)(sequenceExpression));
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
});
|
|
1082
|
-
});
|
|
1083
|
-
attemptOutlineStatements(chunk.body, chunk.body, staticStateValues, chunk.label);
|
|
1084
|
-
if (!chunk.impossible) {
|
|
1085
|
-
// FUTURE OBFUSCATION IDEA: Update controlObject based on 'potentialBranches' code
|
|
1086
|
-
// This idea would require a lot of work but would make some seriously effective obfuscation
|
|
1087
|
-
// for protecting the data. In 'inactive' states the data could be overwritten to fake values
|
|
1088
|
-
// And in the 'active' state the data would brought back just in time. This would require the controlObject
|
|
1089
|
-
// state to be known in all chunks
|
|
1090
|
-
}
|
|
1091
|
-
var caseObject = {
|
|
1092
|
-
body: chunk.body,
|
|
1093
|
-
state: state,
|
|
1094
|
-
label: chunk.label
|
|
1095
|
-
};
|
|
1096
|
-
cases.push(caseObject);
|
|
1097
|
-
});
|
|
1098
|
-
if (!this.isDebug && this.addDeadCode) {
|
|
1099
|
-
// Add fake control object updates
|
|
1100
|
-
chunks.forEach(chunk => {
|
|
1101
|
-
if ((0, _random.chance)(10)) {
|
|
1102
|
-
// These deadCode variants can NOT break the state/control variables
|
|
1103
|
-
// They are executed!
|
|
1104
|
-
var deadCodeChoices = [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(controlGen.generate()), (0, _gen.Literal)(controlGen.generate()))), (0, _gen.ExpressionStatement)((0, _gen.UnaryExpression)("delete", getControlMember(controlGen.generate())))];
|
|
1105
|
-
|
|
1106
|
-
// These deadCode variants can make breaking changes
|
|
1107
|
-
// because they are never ran
|
|
1108
|
-
if (chunk.impossible) {
|
|
1109
|
-
var randomControlKey = (0, _random.choice)(controlProperties.map(prop => {
|
|
1110
|
-
var _prop$key;
|
|
1111
|
-
return (_prop$key = prop.key) === null || _prop$key === void 0 ? void 0 : _prop$key.value;
|
|
1112
|
-
}).filter(x => x && typeof x === "string")) || controlGen.generate();
|
|
1113
|
-
deadCodeChoices = deadCodeChoices.concat([(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(controlVar), (0, _gen.Literal)(false))), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(controlVar), (0, _gen.Identifier)("undefined"))), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", getControlMember(randomControlKey), (0, _gen.Identifier)("undefined"))), (0, _gen.ExpressionStatement)((0, _gen.UnaryExpression)("delete", getControlMember(randomControlKey)))]);
|
|
1114
|
-
}
|
|
1115
|
-
chunk.body.unshift((0, _random.choice)(deadCodeChoices));
|
|
1116
|
-
}
|
|
1117
|
-
});
|
|
1118
|
-
}
|
|
1119
|
-
if (!this.isDebug) {
|
|
1120
|
-
(0, _random.shuffle)(cases);
|
|
1121
|
-
(0, _random.shuffle)(controlProperties);
|
|
1122
|
-
}
|
|
1123
|
-
var discriminant = new _template.default(`${stateVars.join("+")}`).single().expression;
|
|
1124
|
-
objectBody.length = 0;
|
|
1125
|
-
// Perverse position of import declarations
|
|
1126
|
-
for (var importDeclaration of importDeclarations) {
|
|
1127
|
-
objectBody.push(importDeclaration);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// As well as functions are brought up
|
|
1131
|
-
for (var functionName of functionDeclarationNames) {
|
|
1132
|
-
objectBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(functionName, functionDeclarationValues.get(functionName))));
|
|
1133
|
-
}
|
|
1134
|
-
var defaultCaseIndex = (0, _random.getRandomInteger)(0, cases.length);
|
|
1135
|
-
var switchCases = [];
|
|
1136
|
-
cases.forEach((caseObject, i) => {
|
|
1137
|
-
var _caseObject$body$0$la;
|
|
1138
|
-
// Empty case OR single break statement is skipped
|
|
1139
|
-
if (caseObject.body.length === 0 || caseObject.body.length === 1 && caseObject.body[0].type === "BreakStatement" && ((_caseObject$body$0$la = caseObject.body[0].label) === null || _caseObject$body$0$la === void 0 ? void 0 : _caseObject$body$0$la.name) === switchLabel) return;
|
|
1140
|
-
var test = (0, _gen.Literal)(caseObject.state);
|
|
1141
|
-
var isEligibleForOutlining = false;
|
|
1142
|
-
|
|
1143
|
-
// Check if Control Map has this value
|
|
1144
|
-
if (!this.isDebug && controlConstantMap.has(caseObject.state)) {
|
|
1145
|
-
var _controlConstantMap$g3;
|
|
1146
|
-
test = getControlMember((_controlConstantMap$g3 = controlConstantMap.get(caseObject.state)) === null || _controlConstantMap$g3 === void 0 ? void 0 : _controlConstantMap$g3.key);
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// Create complex test expressions for each switch case
|
|
1150
|
-
if (!this.isDebug && this.addComplexTest && (0, _random.chance)(25)) {
|
|
1151
|
-
isEligibleForOutlining = true;
|
|
1152
|
-
|
|
1153
|
-
// case STATE+X:
|
|
1154
|
-
var stateVarIndex = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
1155
|
-
var stateValues = labelToStates[caseObject.label];
|
|
1156
|
-
var difference = stateValues[stateVarIndex] - caseObject.state;
|
|
1157
|
-
var conditionNodes = [];
|
|
1158
|
-
var alreadyConditionedItems = new Set();
|
|
1159
|
-
|
|
1160
|
-
// This code finds clash conditions and adds them to 'conditionNodes' array
|
|
1161
|
-
Object.keys(labelToStates).forEach(label => {
|
|
1162
|
-
if (label !== caseObject.label) {
|
|
1163
|
-
var labelStates = labelToStates[label];
|
|
1164
|
-
var totalState = labelStates.reduce((a, b) => a + b, 0);
|
|
1165
|
-
if (totalState === labelStates[stateVarIndex] - difference) {
|
|
1166
|
-
var differentIndex = labelStates.findIndex((v, i) => v !== stateValues[i]);
|
|
1167
|
-
if (differentIndex !== -1) {
|
|
1168
|
-
var expressionAsString = stateVars[differentIndex] + "!=" + labelStates[differentIndex];
|
|
1169
|
-
if (!alreadyConditionedItems.has(expressionAsString)) {
|
|
1170
|
-
alreadyConditionedItems.add(expressionAsString);
|
|
1171
|
-
conditionNodes.push((0, _gen.BinaryExpression)("!=", (0, _gen.Identifier)(stateVars[differentIndex]), (0, _gen.Literal)(labelStates[differentIndex])));
|
|
1172
|
-
}
|
|
1173
|
-
} else {
|
|
1174
|
-
conditionNodes.push((0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(totalState)));
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
});
|
|
1179
|
-
|
|
1180
|
-
// case STATE!=Y && STATE+X
|
|
1181
|
-
test = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)(stateVars[stateVarIndex]), (0, _gen.Literal)(difference));
|
|
1182
|
-
|
|
1183
|
-
// Use the 'conditionNodes' to not cause state clashing issues
|
|
1184
|
-
conditionNodes.forEach(conditionNode => {
|
|
1185
|
-
test = (0, _gen.LogicalExpression)("&&", conditionNode, test);
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
// A 'flagged' label has addition 'flagKey' that gets switched before jumped to
|
|
1190
|
-
if (flaggedLabels[caseObject.label]) {
|
|
1191
|
-
isEligibleForOutlining = true;
|
|
1192
|
-
var {
|
|
1193
|
-
flagKey,
|
|
1194
|
-
flagValue
|
|
1195
|
-
} = flaggedLabels[caseObject.label];
|
|
1196
|
-
var alternateNum;
|
|
1197
|
-
do {
|
|
1198
|
-
alternateNum = (0, _random.getRandomInteger)(-1000, 1000 + chunks.length);
|
|
1199
|
-
} while (caseSelection.has(alternateNum));
|
|
1200
|
-
var alternate = (0, _gen.Literal)(alternateNum);
|
|
1201
|
-
|
|
1202
|
-
// case FLAG ? <REAL> : <FAKE>:
|
|
1203
|
-
test = (0, _gen.ConditionalExpression)(getControlMember(flagKey), flagValue ? test : alternate, !flagValue ? test : alternate);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// Outline this switch case test
|
|
1207
|
-
if (!this.isDebug && this.outlineExpressions && isEligibleForOutlining && (0, _random.chance)(75 - outlinesCreated - this.mangledExpressionsMade / 25)) {
|
|
1208
|
-
this.mangledExpressionsMade++;
|
|
1209
|
-
|
|
1210
|
-
// Selected a random parent node (or this node) to insert this function in
|
|
1211
|
-
var selectedControlNode = (0, _random.choice)(allControlNodes);
|
|
1212
|
-
var selectedControlProperties = selectedControlNode.$controlProperties;
|
|
1213
|
-
var selectedControlVar = selectedControlNode.$controlVar;
|
|
1214
|
-
var selectedControlGen = selectedControlNode.$controlGen;
|
|
1215
|
-
var fnKey = selectedControlGen.generate();
|
|
1216
|
-
|
|
1217
|
-
// Pass in the:
|
|
1218
|
-
// - controlVar for 'flagged labels' code check
|
|
1219
|
-
// - stateVars for 'complex test expressions'
|
|
1220
|
-
// (Check which identifiers are actually needed)
|
|
1221
|
-
var argumentList = [],
|
|
1222
|
-
watchingFor = new Set([controlVar, ...stateVars]);
|
|
1223
|
-
(0, _traverse.walk)(test, [], (o, p) => {
|
|
1224
|
-
if (o.type === "Identifier" && watchingFor.has(o.name)) {
|
|
1225
|
-
watchingFor.delete(o.name);
|
|
1226
|
-
argumentList.push((0, _gen.Identifier)(o.name));
|
|
1227
|
-
}
|
|
1228
|
-
});
|
|
1229
|
-
selectedControlProperties.push((0, _gen.Property)((0, _gen.Literal)(fnKey), (0, _gen.FunctionExpression)(argumentList, [(0, _gen.ReturnStatement)(test)]), true));
|
|
1230
|
-
|
|
1231
|
-
// case control.a(control, s1, s2):
|
|
1232
|
-
test = (0, _gen.CallExpression)(getControlMember(fnKey, selectedControlVar), (0, _insert.clone)(argumentList));
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
// One random case gets to be default
|
|
1236
|
-
if (!this.isDebug && i === defaultCaseIndex) test = null;
|
|
1237
|
-
var testArray = [test];
|
|
1238
|
-
if (!this.isDebug && this.addFakeTest && (0, _random.chance)(50)) {
|
|
1239
|
-
// Add fake test
|
|
1240
|
-
// case <FAKE>:
|
|
1241
|
-
// case <REAL>:
|
|
1242
|
-
// case <FAKE>:
|
|
1243
|
-
var fakeTestCount = (0, _random.getRandomInteger)(1, 4);
|
|
1244
|
-
for (var i = 0; i < fakeTestCount; i++) {
|
|
1245
|
-
// Create a fake test number that doesn't interfere with the actual states
|
|
1246
|
-
var fakeTestNum;
|
|
1247
|
-
do {
|
|
1248
|
-
fakeTestNum = (0, _random.getRandomInteger)(1, 1000 + caseSelection.size);
|
|
1249
|
-
} while (caseSelection.has(fakeTestNum));
|
|
1250
|
-
|
|
1251
|
-
// Add this fake test
|
|
1252
|
-
testArray.push((0, _gen.Literal)(fakeTestNum));
|
|
1253
|
-
}
|
|
1254
|
-
(0, _random.shuffle)(testArray);
|
|
1255
|
-
}
|
|
1256
|
-
testArray.forEach((test, i) => {
|
|
1257
|
-
var body = i === testArray.length - 1 ? caseObject.body : [];
|
|
1258
|
-
switchCases.push((0, _gen.SwitchCase)(test, body));
|
|
1259
|
-
});
|
|
1260
|
-
});
|
|
1261
|
-
|
|
1262
|
-
// switch(state) { case ... }
|
|
1263
|
-
var switchStatement = (0, _gen.SwitchStatement)(discriminant, switchCases);
|
|
1264
|
-
var declarations = [];
|
|
1265
|
-
|
|
1266
|
-
// var state = START_STATE
|
|
1267
|
-
declarations.push(...stateVars.map((stateVar, i) => {
|
|
1268
|
-
return (0, _gen.VariableDeclarator)(stateVar, (0, _gen.Literal)(initStateValues[i]));
|
|
1269
|
-
}));
|
|
1270
|
-
|
|
1271
|
-
// var control = { strings, numbers, outlined functions, etc... }
|
|
1272
|
-
var objectExpression = (0, _gen.ObjectExpression)(controlProperties);
|
|
1273
|
-
declarations.push((0, _gen.VariableDeclarator)(controlVar, objectExpression));
|
|
1274
|
-
objectBody.push(
|
|
1275
|
-
// Use individual variable declarations instead so Stack can apply
|
|
1276
|
-
...declarations.map(declaration => (0, _gen.VariableDeclaration)(declaration, "var")));
|
|
1277
|
-
|
|
1278
|
-
// while (state != END_STATE) {...}
|
|
1279
|
-
var whileTest = (0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(endState));
|
|
1280
|
-
objectBody.push((0, _gen.WhileStatement)(whileTest, [(0, _gen.LabeledStatement)(switchLabel, switchStatement)]));
|
|
1281
|
-
|
|
1282
|
-
// mark this object for switch case obfuscation
|
|
1283
|
-
switchStatement.$controlFlowFlattening = true;
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
exports.default = ControlFlowFlattening;
|