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