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