js-confuser 1.2.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/README.md +7 -6
- package/dist/options.js +5 -1
- package/dist/parser.js +1 -2
- package/dist/presets.js +2 -2
- package/dist/transforms/calculator.js +48 -60
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +482 -95
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -0
- package/dist/transforms/controlFlowFlattening/{switchCaseObfucation.js → switchCaseObfuscation.js} +2 -2
- package/dist/transforms/deadCode.js +1 -1
- package/dist/transforms/dispatcher.js +14 -13
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +5 -10
- package/dist/transforms/flatten.js +5 -1
- package/dist/transforms/hideInitializingCode.js +17 -2
- package/dist/transforms/identifier/globalConcealing.js +46 -25
- package/dist/transforms/identifier/movedDeclarations.js +69 -68
- package/dist/transforms/identifier/renameVariables.js +22 -98
- package/dist/transforms/identifier/variableAnalysis.js +133 -0
- package/dist/transforms/label.js +11 -2
- package/dist/transforms/lock/antiDebug.js +32 -13
- package/dist/transforms/lock/lock.js +13 -2
- package/dist/transforms/minify.js +117 -120
- package/dist/transforms/opaquePredicates.js +4 -2
- package/dist/transforms/preparation/preparation.js +8 -0
- package/dist/transforms/renameLabels.js +17 -3
- package/dist/transforms/rgf.js +8 -3
- package/dist/transforms/shuffle.js +25 -9
- package/dist/transforms/stack.js +5 -9
- package/dist/transforms/string/encoding.js +209 -0
- package/dist/transforms/string/stringCompression.js +10 -10
- package/dist/transforms/string/stringConcealing.js +94 -65
- package/dist/transforms/string/stringSplitting.js +7 -7
- package/dist/transforms/transform.js +10 -0
- package/dist/traverse.js +1 -35
- package/dist/util/gen.js +3 -1
- package/dist/util/identifiers.js +9 -19
- package/dist/util/insert.js +6 -40
- package/dist/util/scope.js +17 -0
- package/package.json +2 -2
- package/src/options.ts +19 -3
- package/src/parser.ts +1 -2
- package/src/presets.ts +2 -2
- package/src/transforms/calculator.ts +87 -91
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +742 -142
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +6 -0
- package/src/transforms/controlFlowFlattening/{switchCaseObfucation.ts → switchCaseObfuscation.ts} +6 -2
- package/src/transforms/deadCode.ts +8 -0
- package/src/transforms/dispatcher.ts +29 -14
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +43 -19
- package/src/transforms/flatten.ts +15 -2
- package/src/transforms/hideInitializingCode.ts +432 -406
- package/src/transforms/identifier/globalConcealing.ts +148 -46
- package/src/transforms/identifier/movedDeclarations.ts +78 -101
- package/src/transforms/identifier/renameVariables.ts +21 -96
- package/src/transforms/identifier/variableAnalysis.ts +124 -0
- package/src/transforms/label.ts +20 -2
- package/src/transforms/lock/antiDebug.ts +69 -26
- package/src/transforms/lock/lock.ts +37 -3
- package/src/transforms/minify.ts +154 -130
- package/src/transforms/opaquePredicates.ts +25 -3
- package/src/transforms/preparation/preparation.ts +8 -1
- package/src/transforms/renameLabels.ts +26 -3
- package/src/transforms/rgf.ts +6 -1
- package/src/transforms/shuffle.ts +87 -29
- package/src/transforms/stack.ts +6 -8
- package/src/transforms/string/encoding.ts +310 -0
- package/src/transforms/string/stringCompression.ts +37 -24
- package/src/transforms/string/stringConcealing.ts +157 -160
- package/src/transforms/string/stringSplitting.ts +12 -8
- package/src/transforms/transform.ts +15 -2
- package/src/traverse.ts +1 -31
- package/src/util/gen.ts +5 -3
- package/src/util/identifiers.ts +20 -20
- package/src/util/insert.ts +12 -78
- package/src/util/scope.ts +9 -0
- package/test/{transforms/compare.test.ts → compare.test.ts} +2 -2
- package/test/index.test.ts +109 -1
- package/test/templates/template.test.ts +14 -0
- package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +392 -10
- package/test/transforms/dispatcher.test.ts +30 -0
- package/test/transforms/flatten.test.ts +28 -0
- package/test/transforms/hideInitializingCode.test.ts +336 -336
- package/test/transforms/identifier/globalConcealing.test.ts +1 -2
- package/test/transforms/identifier/movedDeclarations.test.ts +137 -112
- package/test/transforms/identifier/renameVariables.test.ts +124 -13
- package/test/transforms/lock/antiDebug.test.ts +43 -0
- package/test/transforms/lock/selfDefending.test.ts +68 -0
- package/test/transforms/minify.test.ts +137 -0
- package/test/transforms/renameLabels.test.ts +33 -0
- package/test/transforms/rgf.test.ts +29 -0
- package/test/transforms/string/stringSplitting.test.ts +33 -0
- package/test/util/identifiers.test.ts +105 -17
- package/dist/util/expr.js +0 -60
- package/src/util/expr.ts +0 -56
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { ok } from "assert";
|
|
2
|
+
import { compileJsSync } from "../../compiler";
|
|
2
3
|
import { ObfuscateOrder } from "../../order";
|
|
3
4
|
import { ComputeProbabilityMap } from "../../probability";
|
|
4
5
|
import Template from "../../templates/template";
|
|
5
6
|
import { getBlock, isBlock, walk } from "../../traverse";
|
|
6
7
|
import {
|
|
8
|
+
ArrayExpression,
|
|
9
|
+
ArrayPattern,
|
|
7
10
|
AssignmentExpression,
|
|
8
11
|
BinaryExpression,
|
|
9
12
|
BreakStatement,
|
|
13
|
+
CallExpression,
|
|
10
14
|
ConditionalExpression,
|
|
11
15
|
ExpressionStatement,
|
|
16
|
+
FunctionDeclaration,
|
|
12
17
|
Identifier,
|
|
18
|
+
IfStatement,
|
|
19
|
+
LabeledStatement,
|
|
13
20
|
Literal,
|
|
14
21
|
Node,
|
|
22
|
+
ReturnStatement,
|
|
15
23
|
SequenceExpression,
|
|
16
24
|
SwitchCase,
|
|
17
25
|
SwitchStatement,
|
|
@@ -26,6 +34,7 @@ import {
|
|
|
26
34
|
import {
|
|
27
35
|
clone,
|
|
28
36
|
getBlockBody,
|
|
37
|
+
getFunction,
|
|
29
38
|
getVarContext,
|
|
30
39
|
isVarContext,
|
|
31
40
|
} from "../../util/insert";
|
|
@@ -34,7 +43,14 @@ import Transform from "../transform";
|
|
|
34
43
|
import ChoiceFlowObfuscation from "./choiceFlowObfuscation";
|
|
35
44
|
import ControlFlowObfuscation from "./controlFlowObfuscation";
|
|
36
45
|
import ExpressionObfuscation from "./expressionObfuscation";
|
|
37
|
-
import SwitchCaseObfuscation from "./
|
|
46
|
+
import SwitchCaseObfuscation from "./switchCaseObfuscation";
|
|
47
|
+
|
|
48
|
+
var flattenStructures = new Set([
|
|
49
|
+
"IfStatement",
|
|
50
|
+
"ForStatement",
|
|
51
|
+
"WhileStatement",
|
|
52
|
+
"DoWhileStatement",
|
|
53
|
+
]);
|
|
38
54
|
|
|
39
55
|
/**
|
|
40
56
|
* Breaks functions into DAGs (Directed Acyclic Graphs)
|
|
@@ -50,23 +66,36 @@ import SwitchCaseObfuscation from "./switchCaseObfucation";
|
|
|
50
66
|
* - 3. The while loop continues until the the state variable is the end state.
|
|
51
67
|
*/
|
|
52
68
|
export default class ControlFlowFlattening extends Transform {
|
|
69
|
+
isDebug = false;
|
|
70
|
+
|
|
53
71
|
constructor(o) {
|
|
54
72
|
super(o, ObfuscateOrder.ControlFlowFlattening);
|
|
55
73
|
|
|
56
|
-
this.
|
|
74
|
+
if (!this.isDebug) {
|
|
75
|
+
this.before.push(new ExpressionObfuscation(o));
|
|
57
76
|
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
this.after.push(new ControlFlowObfuscation(o));
|
|
78
|
+
this.after.push(new SwitchCaseObfuscation(o));
|
|
79
|
+
} else {
|
|
80
|
+
console.warn("Debug mode enabled");
|
|
81
|
+
}
|
|
60
82
|
|
|
61
83
|
// this.after.push(new ChoiceFlowObfuscation(o));
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
match(object, parents) {
|
|
65
|
-
return
|
|
87
|
+
return (
|
|
88
|
+
isBlock(object) &&
|
|
89
|
+
(!parents[1] || !flattenStructures.has(parents[1].type)) &&
|
|
90
|
+
(!parents[2] || !flattenStructures.has(parents[2].type))
|
|
91
|
+
);
|
|
66
92
|
}
|
|
67
93
|
|
|
68
94
|
transform(object, parents) {
|
|
69
95
|
return () => {
|
|
96
|
+
if (object.body.length < 3) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
70
99
|
if (containsLexicallyBoundVariables(object, parents)) {
|
|
71
100
|
return;
|
|
72
101
|
}
|
|
@@ -86,16 +115,42 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
86
115
|
|
|
87
116
|
var functionDeclarations: Set<Node> = new Set();
|
|
88
117
|
var fnNames: Set<string> = new Set();
|
|
118
|
+
var illegalFnNames = new Set<string>();
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The variable names
|
|
122
|
+
*
|
|
123
|
+
* index -> var name
|
|
124
|
+
*/
|
|
125
|
+
var stateVars = Array(this.isDebug ? 1 : getRandomInteger(2, 5))
|
|
126
|
+
.fill(0)
|
|
127
|
+
.map(() => this.getPlaceholder());
|
|
89
128
|
|
|
90
129
|
body.forEach((stmt, i) => {
|
|
91
130
|
if (stmt.type == "FunctionDeclaration") {
|
|
92
131
|
functionDeclarations.add(stmt);
|
|
93
|
-
|
|
132
|
+
var name = stmt.id && stmt.id.name;
|
|
133
|
+
fnNames.add(name);
|
|
134
|
+
if (stmt.body.type !== "BlockStatement") {
|
|
135
|
+
illegalFnNames.add(name);
|
|
136
|
+
} else {
|
|
137
|
+
walk(stmt, [body, object, ...parents], (o, p) => {
|
|
138
|
+
if (
|
|
139
|
+
o.type == "ThisExpression" ||
|
|
140
|
+
o.type == "SuperExpression" ||
|
|
141
|
+
(o.type == "Identifier" &&
|
|
142
|
+
(o.name == "arguments" || o.name == "eval"))
|
|
143
|
+
) {
|
|
144
|
+
illegalFnNames.add(name);
|
|
145
|
+
return "EXIT";
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
94
149
|
}
|
|
95
150
|
});
|
|
96
151
|
|
|
97
152
|
walk(object, parents, (o, p) => {
|
|
98
|
-
if (o.type == "Identifier") {
|
|
153
|
+
if (o.type == "Identifier" && fnNames.has(o.name)) {
|
|
99
154
|
var info = getIdentifierInfo(o, p);
|
|
100
155
|
if (!info.spec.isReferenced) {
|
|
101
156
|
return;
|
|
@@ -112,6 +167,50 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
112
167
|
fnNames.delete(o.name);
|
|
113
168
|
}
|
|
114
169
|
}
|
|
170
|
+
|
|
171
|
+
if (!info.spec.isDefined) {
|
|
172
|
+
var b = getBlock(o, p);
|
|
173
|
+
if (b !== object || !p[0] || p[0].type !== "CallExpression") {
|
|
174
|
+
illegalFnNames.add(o.name);
|
|
175
|
+
} else {
|
|
176
|
+
var isExtractable = false;
|
|
177
|
+
if (p[1]) {
|
|
178
|
+
if (
|
|
179
|
+
p[1].type == "ExpressionStatement" &&
|
|
180
|
+
p[1].expression == p[0] &&
|
|
181
|
+
p[2] == object.body
|
|
182
|
+
) {
|
|
183
|
+
isExtractable = true;
|
|
184
|
+
p[1].$callExpression = "ExpressionStatement";
|
|
185
|
+
p[1].$fnName = o.name;
|
|
186
|
+
} else if (
|
|
187
|
+
p[1].type == "VariableDeclarator" &&
|
|
188
|
+
p[1].init == p[0] &&
|
|
189
|
+
p[2].length === 1 &&
|
|
190
|
+
p[4] == object.body
|
|
191
|
+
) {
|
|
192
|
+
isExtractable = true;
|
|
193
|
+
p[3].$callExpression = "VariableDeclarator";
|
|
194
|
+
p[3].$fnName = o.name;
|
|
195
|
+
} else if (
|
|
196
|
+
p[1].type == "AssignmentExpression" &&
|
|
197
|
+
p[1].operator == "=" &&
|
|
198
|
+
p[1].right === p[0] &&
|
|
199
|
+
p[2] &&
|
|
200
|
+
p[2].type == "ExpressionStatement" &&
|
|
201
|
+
p[3] == object.body
|
|
202
|
+
) {
|
|
203
|
+
isExtractable = true;
|
|
204
|
+
p[2].$callExpression = "AssignmentExpression";
|
|
205
|
+
p[2].$fnName = o.name;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!isExtractable) {
|
|
210
|
+
illegalFnNames.add(o.name);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
115
214
|
}
|
|
116
215
|
});
|
|
117
216
|
|
|
@@ -120,7 +219,9 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
120
219
|
return;
|
|
121
220
|
}
|
|
122
221
|
|
|
123
|
-
|
|
222
|
+
illegalFnNames.forEach((illegal) => {
|
|
223
|
+
fnNames.delete(illegal);
|
|
224
|
+
});
|
|
124
225
|
|
|
125
226
|
var fraction = 0.9;
|
|
126
227
|
if (body.length > 20) {
|
|
@@ -131,74 +232,514 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
131
232
|
fraction = 0.5;
|
|
132
233
|
}
|
|
133
234
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
235
|
+
var resultVar = this.getPlaceholder();
|
|
236
|
+
var argVar = this.getPlaceholder();
|
|
237
|
+
var needsResultAndArgVar = false;
|
|
238
|
+
var fnToLabel: { [fnName: string]: string } = Object.create(null);
|
|
138
239
|
|
|
139
|
-
|
|
240
|
+
fnNames.forEach((fnName) => {
|
|
241
|
+
fnToLabel[fnName] = this.getPlaceholder();
|
|
242
|
+
});
|
|
140
243
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
244
|
+
const flattenBody = (
|
|
245
|
+
body: Node[],
|
|
246
|
+
startingLabel = this.getPlaceholder()
|
|
247
|
+
): { label: string; body: Node[] }[] => {
|
|
248
|
+
var chunks = [];
|
|
249
|
+
var currentBody = [];
|
|
250
|
+
var currentLabel = startingLabel;
|
|
251
|
+
const finishCurrentChunk = (
|
|
252
|
+
pointingLabel?: string,
|
|
253
|
+
newLabel?: string,
|
|
254
|
+
addGotoStatement = true
|
|
255
|
+
) => {
|
|
256
|
+
if (!newLabel) {
|
|
257
|
+
newLabel = this.getPlaceholder();
|
|
258
|
+
}
|
|
259
|
+
if (!pointingLabel) {
|
|
260
|
+
pointingLabel = newLabel;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (addGotoStatement) {
|
|
264
|
+
currentBody.push({ type: "GotoStatement", label: pointingLabel });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
chunks.push({
|
|
268
|
+
label: currentLabel,
|
|
269
|
+
body: [...currentBody],
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
currentLabel = newLabel;
|
|
273
|
+
currentBody = [];
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
body.forEach((stmt, i) => {
|
|
277
|
+
if (functionDeclarations.has(stmt)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (stmt.$exit) {
|
|
282
|
+
currentBody.push(stmt);
|
|
283
|
+
currentBody.push(BreakStatement(switchLabel));
|
|
284
|
+
finishCurrentChunk(null, null, false);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (stmt.$callExpression && fnToLabel[stmt.$fnName]) {
|
|
289
|
+
var afterPath = this.getPlaceholder();
|
|
290
|
+
var args = [];
|
|
291
|
+
|
|
292
|
+
switch (stmt.$callExpression) {
|
|
293
|
+
// var a = fn();
|
|
294
|
+
case "VariableDeclarator":
|
|
295
|
+
args = stmt.declarations[0].init.arguments;
|
|
296
|
+
stmt.declarations[0].init = Identifier(resultVar);
|
|
297
|
+
break;
|
|
298
|
+
|
|
299
|
+
// fn();
|
|
300
|
+
case "ExpressionStatement":
|
|
301
|
+
args = stmt.expression.arguments;
|
|
302
|
+
stmt.expression = Identifier("undefined");
|
|
303
|
+
break;
|
|
304
|
+
|
|
305
|
+
// a = fn();
|
|
306
|
+
case "AssignmentExpression":
|
|
307
|
+
args = stmt.expression.right.arguments;
|
|
308
|
+
stmt.expression.right = Identifier(resultVar);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
needsResultAndArgVar = true;
|
|
313
|
+
|
|
314
|
+
currentBody.push(
|
|
315
|
+
ExpressionStatement(
|
|
316
|
+
AssignmentExpression(
|
|
317
|
+
"=",
|
|
318
|
+
Identifier(argVar),
|
|
319
|
+
ArrayExpression([
|
|
320
|
+
{
|
|
321
|
+
type: "StateIdentifier",
|
|
322
|
+
label: afterPath,
|
|
323
|
+
},
|
|
324
|
+
ArrayExpression(args),
|
|
325
|
+
])
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
finishCurrentChunk(fnToLabel[stmt.$fnName], afterPath);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (stmt.type == "GotoStatement" && i !== body.length - 1) {
|
|
333
|
+
finishCurrentChunk(stmt.label);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (stmt.type == "LabeledStatement") {
|
|
338
|
+
var lbl = stmt.label.name;
|
|
339
|
+
var control = stmt.body;
|
|
340
|
+
|
|
341
|
+
var isSwitchStatement = control.type === "SwitchStatement";
|
|
342
|
+
|
|
343
|
+
if (
|
|
344
|
+
isSwitchStatement ||
|
|
345
|
+
((control.type == "ForStatement" ||
|
|
346
|
+
control.type == "WhileStatement" ||
|
|
347
|
+
control.type == "DoWhileStatement") &&
|
|
348
|
+
control.body.type == "BlockStatement")
|
|
349
|
+
) {
|
|
350
|
+
if (isSwitchStatement) {
|
|
351
|
+
if (
|
|
352
|
+
control.cases.length == 0 || // at least 1 case
|
|
353
|
+
control.cases.find(
|
|
354
|
+
(x) =>
|
|
355
|
+
!x.test || // cant be default case
|
|
356
|
+
!x.consequent.length || // must have body
|
|
357
|
+
x.consequent.findIndex(
|
|
358
|
+
(node) => node.type == "BreakStatement"
|
|
359
|
+
) !==
|
|
360
|
+
x.consequent.length - 1 || // break statement must be at the end
|
|
361
|
+
x.consequent[x.consequent.length - 1].type !== // must end with break statement
|
|
362
|
+
"BreakStatement" ||
|
|
363
|
+
!x.consequent[x.consequent.length - 1].label || // must be labeled and correct
|
|
364
|
+
x.consequent[x.consequent.length - 1].label.name != lbl
|
|
365
|
+
)
|
|
366
|
+
) {
|
|
367
|
+
currentBody.push(stmt);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var isLoop = !isSwitchStatement;
|
|
373
|
+
var supportContinueStatement = isLoop;
|
|
374
|
+
|
|
375
|
+
var testPath = this.getPlaceholder();
|
|
376
|
+
var updatePath = this.getPlaceholder();
|
|
377
|
+
var bodyPath = this.getPlaceholder();
|
|
378
|
+
var afterPath = this.getPlaceholder();
|
|
379
|
+
var possible = true;
|
|
380
|
+
var toReplace = [];
|
|
381
|
+
|
|
382
|
+
walk(control.body, [], (o, p) => {
|
|
383
|
+
if (
|
|
384
|
+
o.type == "BreakStatement" ||
|
|
385
|
+
(supportContinueStatement && o.type == "ContinueStatement")
|
|
386
|
+
) {
|
|
387
|
+
if (!o.label || o.label.name !== lbl) {
|
|
388
|
+
possible = false;
|
|
389
|
+
return "EXIT";
|
|
390
|
+
}
|
|
391
|
+
if (o.label.name === lbl) {
|
|
392
|
+
return () => {
|
|
393
|
+
toReplace.push([
|
|
394
|
+
o,
|
|
395
|
+
{
|
|
396
|
+
type: "GotoStatement",
|
|
397
|
+
label:
|
|
398
|
+
o.type == "BreakStatement" ? afterPath : updatePath,
|
|
399
|
+
},
|
|
400
|
+
]);
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
if (!possible) {
|
|
406
|
+
currentBody.push(stmt);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
toReplace.forEach((v) => this.replace(v[0], v[1]));
|
|
410
|
+
|
|
411
|
+
if (isSwitchStatement) {
|
|
412
|
+
var switchVarName = this.getPlaceholder();
|
|
413
|
+
|
|
414
|
+
currentBody.push(
|
|
415
|
+
VariableDeclaration(
|
|
416
|
+
VariableDeclarator(switchVarName, control.discriminant)
|
|
417
|
+
)
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
var afterPath = this.getPlaceholder();
|
|
421
|
+
finishCurrentChunk();
|
|
422
|
+
control.cases.forEach((switchCase, i) => {
|
|
423
|
+
var entryPath = this.getPlaceholder();
|
|
424
|
+
|
|
425
|
+
currentBody.push(
|
|
426
|
+
IfStatement(
|
|
427
|
+
BinaryExpression(
|
|
428
|
+
"===",
|
|
429
|
+
Identifier(switchVarName),
|
|
430
|
+
switchCase.test
|
|
431
|
+
),
|
|
432
|
+
[
|
|
433
|
+
{
|
|
434
|
+
type: "GotoStatement",
|
|
435
|
+
label: entryPath,
|
|
436
|
+
},
|
|
437
|
+
]
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
chunks.push(
|
|
442
|
+
...flattenBody(
|
|
443
|
+
[
|
|
444
|
+
...switchCase.consequent.slice(
|
|
445
|
+
0,
|
|
446
|
+
switchCase.consequent.length - 1
|
|
447
|
+
),
|
|
448
|
+
{
|
|
449
|
+
type: "GotoStatement",
|
|
450
|
+
label: afterPath,
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
entryPath
|
|
454
|
+
)
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
if (i === control.cases.length - 1) {
|
|
458
|
+
} else {
|
|
459
|
+
finishCurrentChunk();
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
finishCurrentChunk(afterPath, afterPath);
|
|
464
|
+
return;
|
|
465
|
+
} else if (isLoop) {
|
|
466
|
+
var isPostTest = control.type == "DoWhileStatement";
|
|
467
|
+
|
|
468
|
+
// add initializing section to current chunk
|
|
469
|
+
if (control.init) {
|
|
470
|
+
if (control.init.type == "VariableDeclaration") {
|
|
471
|
+
currentBody.push(control.init);
|
|
472
|
+
} else {
|
|
473
|
+
currentBody.push(ExpressionStatement(control.init));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// create new label called `testPath` and have current chunk point to it (goto testPath)
|
|
478
|
+
finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
|
|
479
|
+
|
|
480
|
+
currentBody.push(
|
|
481
|
+
IfStatement(control.test || Literal(true), [
|
|
482
|
+
{
|
|
483
|
+
type: "GotoStatement",
|
|
484
|
+
label: bodyPath,
|
|
485
|
+
},
|
|
486
|
+
])
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
// create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
|
|
490
|
+
finishCurrentChunk(afterPath, bodyPath);
|
|
491
|
+
|
|
492
|
+
var innerBothPath = this.getPlaceholder();
|
|
493
|
+
chunks.push(
|
|
494
|
+
...flattenBody(
|
|
495
|
+
[
|
|
496
|
+
...control.body.body,
|
|
497
|
+
{
|
|
498
|
+
type: "GotoStatement",
|
|
499
|
+
label: updatePath,
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
innerBothPath
|
|
503
|
+
)
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
finishCurrentChunk(innerBothPath, updatePath);
|
|
507
|
+
|
|
508
|
+
if (control.update) {
|
|
509
|
+
currentBody.push(ExpressionStatement(control.update));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
finishCurrentChunk(testPath, afterPath);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (
|
|
519
|
+
stmt.type == "IfStatement" &&
|
|
520
|
+
stmt.consequent.type == "BlockStatement" &&
|
|
521
|
+
(!stmt.alternate || stmt.alternate.type == "BlockStatement")
|
|
522
|
+
) {
|
|
523
|
+
finishCurrentChunk();
|
|
524
|
+
|
|
525
|
+
var hasAlternate = !!stmt.alternate;
|
|
526
|
+
ok(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
|
|
527
|
+
|
|
528
|
+
var yesPath = this.getPlaceholder();
|
|
529
|
+
var noPath = this.getPlaceholder();
|
|
530
|
+
var afterPath = this.getPlaceholder();
|
|
531
|
+
|
|
532
|
+
currentBody.push(
|
|
533
|
+
IfStatement(stmt.test, [
|
|
534
|
+
{
|
|
535
|
+
type: "GotoStatement",
|
|
536
|
+
label: yesPath,
|
|
537
|
+
},
|
|
538
|
+
])
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
chunks.push(
|
|
542
|
+
...flattenBody(
|
|
543
|
+
[
|
|
544
|
+
...stmt.consequent.body,
|
|
545
|
+
{
|
|
546
|
+
type: "GotoStatement",
|
|
547
|
+
label: afterPath,
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
yesPath
|
|
551
|
+
)
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
if (hasAlternate) {
|
|
555
|
+
chunks.push(
|
|
556
|
+
...flattenBody(
|
|
557
|
+
[
|
|
558
|
+
...stmt.alternate.body,
|
|
559
|
+
{
|
|
560
|
+
type: "GotoStatement",
|
|
561
|
+
label: afterPath,
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
noPath
|
|
565
|
+
)
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
finishCurrentChunk(noPath, afterPath);
|
|
569
|
+
} else {
|
|
570
|
+
finishCurrentChunk(afterPath, afterPath);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!currentBody.length || Math.random() < fraction) {
|
|
577
|
+
currentBody.push(stmt);
|
|
578
|
+
} else {
|
|
579
|
+
// Start new chunk
|
|
580
|
+
finishCurrentChunk();
|
|
581
|
+
currentBody.push(stmt);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
finishCurrentChunk();
|
|
586
|
+
chunks[chunks.length - 1].body.pop();
|
|
587
|
+
|
|
588
|
+
return chunks;
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
var chunks = [];
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* label: switch(a+b+c){...break label...}
|
|
595
|
+
*/
|
|
596
|
+
var switchLabel = this.getPlaceholder();
|
|
597
|
+
|
|
598
|
+
functionDeclarations.forEach((node) => {
|
|
599
|
+
if (node.id && fnNames.has(node.id.name)) {
|
|
600
|
+
var exitStateName = this.getPlaceholder();
|
|
601
|
+
var argumentsName = this.getPlaceholder();
|
|
602
|
+
|
|
603
|
+
needsResultAndArgVar = true;
|
|
604
|
+
|
|
605
|
+
node.body.body.push(ReturnStatement());
|
|
606
|
+
|
|
607
|
+
walk(node.body, [], (o, p) => {
|
|
608
|
+
if (o.type == "ReturnStatement") {
|
|
609
|
+
if (!getFunction(o, p)) {
|
|
610
|
+
return () => {
|
|
611
|
+
var exitExpr = SequenceExpression([
|
|
612
|
+
AssignmentExpression(
|
|
613
|
+
"=",
|
|
614
|
+
ArrayPattern(stateVars.map(Identifier)),
|
|
615
|
+
Identifier(exitStateName)
|
|
616
|
+
),
|
|
617
|
+
AssignmentExpression(
|
|
618
|
+
"=",
|
|
619
|
+
Identifier(resultVar),
|
|
620
|
+
o.argument || Identifier("undefined")
|
|
621
|
+
),
|
|
622
|
+
]);
|
|
623
|
+
|
|
624
|
+
this.replace(o, ReturnStatement(exitExpr));
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
var declarations = [
|
|
631
|
+
VariableDeclarator(
|
|
632
|
+
ArrayPattern([
|
|
633
|
+
Identifier(exitStateName),
|
|
634
|
+
Identifier(argumentsName),
|
|
635
|
+
]),
|
|
636
|
+
Identifier(argVar)
|
|
637
|
+
),
|
|
638
|
+
];
|
|
639
|
+
|
|
640
|
+
if (node.params.length) {
|
|
641
|
+
declarations.push(
|
|
642
|
+
VariableDeclarator(
|
|
643
|
+
ArrayPattern(node.params),
|
|
644
|
+
Identifier(argumentsName)
|
|
645
|
+
)
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
var innerName = this.getPlaceholder();
|
|
650
|
+
|
|
651
|
+
chunks.push(
|
|
652
|
+
...flattenBody(
|
|
653
|
+
[
|
|
654
|
+
FunctionDeclaration(
|
|
655
|
+
innerName,
|
|
656
|
+
[],
|
|
657
|
+
[VariableDeclaration(declarations), ...node.body.body]
|
|
658
|
+
),
|
|
659
|
+
this.objectAssign(
|
|
660
|
+
ExpressionStatement(
|
|
661
|
+
CallExpression(Identifier(innerName), [])
|
|
662
|
+
),
|
|
663
|
+
{
|
|
664
|
+
$exit: true,
|
|
665
|
+
} as any
|
|
666
|
+
),
|
|
667
|
+
],
|
|
668
|
+
fnToLabel[node.id.name]
|
|
669
|
+
)
|
|
670
|
+
);
|
|
146
671
|
}
|
|
147
672
|
});
|
|
148
673
|
|
|
149
|
-
|
|
150
|
-
chunks.pop();
|
|
151
|
-
}
|
|
152
|
-
if (chunks.length < 2) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
674
|
+
var startLabel = this.getPlaceholder();
|
|
155
675
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
676
|
+
chunks.push(...flattenBody(body, startLabel));
|
|
677
|
+
chunks[chunks.length - 1].body.push({
|
|
678
|
+
type: "GotoStatement",
|
|
679
|
+
label: "END_LABEL",
|
|
680
|
+
});
|
|
681
|
+
chunks.push({
|
|
682
|
+
label: "END_LABEL",
|
|
683
|
+
body: [],
|
|
684
|
+
});
|
|
162
685
|
|
|
163
686
|
var caseSelection: Set<number> = new Set();
|
|
164
687
|
|
|
165
|
-
var uniqueStatesNeeded = chunks.length
|
|
166
|
-
|
|
167
|
-
for (var i = 0; i < uniqueStatesNeeded; i++) {
|
|
168
|
-
var newState;
|
|
169
|
-
do {
|
|
170
|
-
newState = getRandomInteger(1, chunks.length * 15);
|
|
171
|
-
} while (caseSelection.has(newState));
|
|
688
|
+
var uniqueStatesNeeded = chunks.length;
|
|
689
|
+
var endLabel = chunks[Object.keys(chunks).length - 1].label;
|
|
172
690
|
|
|
691
|
+
do {
|
|
692
|
+
var newState = getRandomInteger(1, chunks.length * 15);
|
|
693
|
+
if (this.isDebug) {
|
|
694
|
+
newState = caseSelection.size;
|
|
695
|
+
}
|
|
173
696
|
caseSelection.add(newState);
|
|
174
|
-
}
|
|
697
|
+
} while (caseSelection.size !== uniqueStatesNeeded);
|
|
175
698
|
|
|
176
699
|
ok(caseSelection.size == uniqueStatesNeeded);
|
|
177
700
|
|
|
701
|
+
/**
|
|
702
|
+
* The accumulated state values
|
|
703
|
+
*
|
|
704
|
+
* index -> total state value
|
|
705
|
+
*/
|
|
178
706
|
var caseStates = Array.from(caseSelection);
|
|
179
707
|
|
|
180
|
-
|
|
181
|
-
|
|
708
|
+
/**
|
|
709
|
+
* The individual state values for each label
|
|
710
|
+
*
|
|
711
|
+
* labels right now are just chunk indexes (numbers)
|
|
712
|
+
*
|
|
713
|
+
* but will expand to if statements and functions when `goto statement` obfuscation is added
|
|
714
|
+
*/
|
|
715
|
+
var labelToStates: { [label: string]: number[] } = Object.create(null);
|
|
182
716
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
.map(() => this.getPlaceholder());
|
|
186
|
-
var stateValues = Array(stateVars.length)
|
|
187
|
-
.fill(0)
|
|
188
|
-
.map(() => getRandomInteger(-250, 250));
|
|
717
|
+
Object.values(chunks).forEach((chunk, i) => {
|
|
718
|
+
var state = caseStates[i];
|
|
189
719
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
720
|
+
var stateValues = Array(stateVars.length)
|
|
721
|
+
.fill(0)
|
|
722
|
+
.map(() => getRandomInteger(-250, 250));
|
|
723
|
+
|
|
724
|
+
const getCurrentState = () => {
|
|
725
|
+
return stateValues.reduce((a, b) => b + a, 0);
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
var correctIndex = getRandomInteger(0, stateValues.length);
|
|
729
|
+
stateValues[correctIndex] =
|
|
730
|
+
state - (getCurrentState() - stateValues[correctIndex]);
|
|
731
|
+
|
|
732
|
+
labelToStates[chunk.label] = stateValues;
|
|
733
|
+
});
|
|
193
734
|
|
|
194
|
-
|
|
195
|
-
stateValues[correctIndex] =
|
|
196
|
-
startState - (getCurrentState() - stateValues[correctIndex]);
|
|
735
|
+
// console.log(labelToStates);
|
|
197
736
|
|
|
198
|
-
var initStateValues = [...
|
|
737
|
+
var initStateValues = [...labelToStates[startLabel]];
|
|
738
|
+
var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
|
|
199
739
|
|
|
200
|
-
const numberLiteral = (num, depth) => {
|
|
201
|
-
|
|
740
|
+
const numberLiteral = (num, depth, stateValues) => {
|
|
741
|
+
ok(Array.isArray(stateValues));
|
|
742
|
+
if (depth > 10 || Math.random() > 0.8 / (depth * 4)) {
|
|
202
743
|
return Literal(num);
|
|
203
744
|
}
|
|
204
745
|
|
|
@@ -211,13 +752,17 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
211
752
|
operator == "<"
|
|
212
753
|
? x < stateValues[opposing]
|
|
213
754
|
: x > stateValues[opposing];
|
|
214
|
-
var correct = numberLiteral(num, depth + 1);
|
|
215
|
-
var incorrect = numberLiteral(
|
|
755
|
+
var correct = numberLiteral(num, depth + 1, stateValues);
|
|
756
|
+
var incorrect = numberLiteral(
|
|
757
|
+
getRandomInteger(-250, 250),
|
|
758
|
+
depth + 1,
|
|
759
|
+
stateValues
|
|
760
|
+
);
|
|
216
761
|
|
|
217
762
|
return ConditionalExpression(
|
|
218
763
|
BinaryExpression(
|
|
219
764
|
operator,
|
|
220
|
-
numberLiteral(x, depth + 1),
|
|
765
|
+
numberLiteral(x, depth + 1, stateValues),
|
|
221
766
|
Identifier(stateVars[opposing])
|
|
222
767
|
),
|
|
223
768
|
answer ? correct : incorrect,
|
|
@@ -228,135 +773,180 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
228
773
|
return BinaryExpression(
|
|
229
774
|
"+",
|
|
230
775
|
Identifier(stateVars[opposing]),
|
|
231
|
-
numberLiteral(num - stateValues[opposing], depth + 1)
|
|
776
|
+
numberLiteral(num - stateValues[opposing], depth + 1, stateValues)
|
|
232
777
|
);
|
|
233
778
|
};
|
|
234
779
|
|
|
235
|
-
const
|
|
236
|
-
|
|
780
|
+
const createTransitionExpression = (
|
|
781
|
+
index: number,
|
|
782
|
+
add: number,
|
|
783
|
+
mutatingStateValues: number[]
|
|
784
|
+
) => {
|
|
785
|
+
var newValue = mutatingStateValues[index] + add;
|
|
237
786
|
|
|
238
787
|
var expr = null;
|
|
239
788
|
|
|
240
|
-
if (
|
|
241
|
-
expr =
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
789
|
+
if (this.isDebug) {
|
|
790
|
+
expr = AssignmentExpression(
|
|
791
|
+
"=",
|
|
792
|
+
Identifier(stateVars[index]),
|
|
793
|
+
Literal(newValue)
|
|
794
|
+
);
|
|
795
|
+
} else if (Math.random() > 0.5) {
|
|
796
|
+
expr = AssignmentExpression(
|
|
797
|
+
"+=",
|
|
798
|
+
Identifier(stateVars[index]),
|
|
799
|
+
numberLiteral(add, 0, mutatingStateValues)
|
|
247
800
|
);
|
|
248
801
|
} else {
|
|
249
|
-
var double =
|
|
802
|
+
var double = mutatingStateValues[index] * 2;
|
|
250
803
|
var diff = double - newValue;
|
|
251
804
|
|
|
252
805
|
var first = AssignmentExpression(
|
|
253
806
|
"*=",
|
|
254
807
|
Identifier(stateVars[index]),
|
|
255
|
-
numberLiteral(2, 0)
|
|
256
|
-
);
|
|
257
|
-
stateValues[index] = double;
|
|
258
|
-
|
|
259
|
-
expr = ExpressionStatement(
|
|
260
|
-
SequenceExpression([
|
|
261
|
-
first,
|
|
262
|
-
AssignmentExpression(
|
|
263
|
-
"-=",
|
|
264
|
-
Identifier(stateVars[index]),
|
|
265
|
-
numberLiteral(diff, 0)
|
|
266
|
-
),
|
|
267
|
-
])
|
|
808
|
+
numberLiteral(2, 0, mutatingStateValues)
|
|
268
809
|
);
|
|
810
|
+
mutatingStateValues[index] = double;
|
|
811
|
+
|
|
812
|
+
expr = SequenceExpression([
|
|
813
|
+
first,
|
|
814
|
+
AssignmentExpression(
|
|
815
|
+
"-=",
|
|
816
|
+
Identifier(stateVars[index]),
|
|
817
|
+
numberLiteral(diff, 0, mutatingStateValues)
|
|
818
|
+
),
|
|
819
|
+
]);
|
|
269
820
|
}
|
|
270
821
|
|
|
271
|
-
|
|
822
|
+
mutatingStateValues[index] = newValue;
|
|
272
823
|
|
|
273
824
|
return expr;
|
|
274
825
|
};
|
|
275
826
|
|
|
276
827
|
interface Case {
|
|
277
828
|
state: number;
|
|
278
|
-
nextState: number;
|
|
279
829
|
body: Node[];
|
|
280
830
|
order: number;
|
|
281
|
-
transitionStatements: Node[];
|
|
282
831
|
}
|
|
283
832
|
|
|
284
833
|
var order = Object.create(null);
|
|
285
|
-
var cases: Case[] =
|
|
286
|
-
|
|
834
|
+
var cases: Case[] = [];
|
|
835
|
+
|
|
836
|
+
chunks.forEach((chunk, i) => {
|
|
837
|
+
// skip last case, its empty and never ran
|
|
838
|
+
if (chunk.label === endLabel) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
ok(labelToStates[chunk.label]);
|
|
843
|
+
var state = caseStates[i];
|
|
844
|
+
var made = 1;
|
|
845
|
+
|
|
846
|
+
var breaksInsertion = [];
|
|
847
|
+
var staticStateValues = [...labelToStates[chunk.label]];
|
|
848
|
+
|
|
849
|
+
chunk.body.forEach((stmt, stmtIndex) => {
|
|
850
|
+
var addBreak = false;
|
|
287
851
|
walk(stmt, [], (o, p) => {
|
|
288
852
|
if (
|
|
853
|
+
!this.isDebug &&
|
|
289
854
|
o.type == "Literal" &&
|
|
290
855
|
typeof o.value === "number" &&
|
|
291
856
|
Math.floor(o.value) === o.value &&
|
|
292
857
|
Math.abs(o.value) < 100_000 &&
|
|
293
|
-
Math.random()
|
|
858
|
+
Math.random() < 4 / made &&
|
|
294
859
|
!p.find((x) => isVarContext(x))
|
|
295
860
|
) {
|
|
861
|
+
made++;
|
|
296
862
|
return () => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
863
|
+
this.replaceIdentifierOrLiteral(
|
|
864
|
+
o,
|
|
865
|
+
numberLiteral(o.value, 0, staticStateValues),
|
|
866
|
+
p
|
|
867
|
+
);
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (o.type == "StateIdentifier") {
|
|
872
|
+
return () => {
|
|
873
|
+
ok(labelToStates[o.label]);
|
|
874
|
+
this.replace(
|
|
875
|
+
o,
|
|
876
|
+
ArrayExpression(labelToStates[o.label].map(Literal))
|
|
877
|
+
);
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (o.type == "GotoStatement") {
|
|
882
|
+
return () => {
|
|
883
|
+
var blockIndex = p.findIndex((node) => isBlock(node));
|
|
884
|
+
if (blockIndex === -1) {
|
|
885
|
+
addBreak = true;
|
|
886
|
+
} else {
|
|
887
|
+
var child = p[blockIndex - 2] || o;
|
|
888
|
+
var childIndex = p[blockIndex].body.indexOf(child);
|
|
889
|
+
|
|
890
|
+
p[blockIndex].body.splice(
|
|
891
|
+
childIndex + 1,
|
|
892
|
+
0,
|
|
893
|
+
BreakStatement(switchLabel)
|
|
894
|
+
);
|
|
304
895
|
}
|
|
305
|
-
|
|
896
|
+
|
|
897
|
+
var mutatingStateValues = [...labelToStates[chunk.label]];
|
|
898
|
+
var nextStateValues = labelToStates[o.label];
|
|
899
|
+
ok(nextStateValues, o.label);
|
|
900
|
+
this.replace(
|
|
901
|
+
o,
|
|
902
|
+
ExpressionStatement(
|
|
903
|
+
SequenceExpression(
|
|
904
|
+
mutatingStateValues.map((_v, stateValueIndex) => {
|
|
905
|
+
var diff =
|
|
906
|
+
nextStateValues[stateValueIndex] -
|
|
907
|
+
mutatingStateValues[stateValueIndex];
|
|
908
|
+
return createTransitionExpression(
|
|
909
|
+
stateValueIndex,
|
|
910
|
+
diff,
|
|
911
|
+
mutatingStateValues
|
|
912
|
+
);
|
|
913
|
+
})
|
|
914
|
+
)
|
|
915
|
+
)
|
|
916
|
+
);
|
|
306
917
|
};
|
|
307
918
|
}
|
|
308
919
|
});
|
|
920
|
+
|
|
921
|
+
if (addBreak) {
|
|
922
|
+
breaksInsertion.push(stmtIndex);
|
|
923
|
+
}
|
|
309
924
|
});
|
|
310
925
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
var diff = nextState - state;
|
|
314
|
-
var transitionStatements = [];
|
|
315
|
-
|
|
316
|
-
ok(!isNaN(diff));
|
|
317
|
-
var modifying = getRandomInteger(0, stateVars.length);
|
|
318
|
-
var shift = 0;
|
|
319
|
-
|
|
320
|
-
// var c1 = Identifier("undefined");
|
|
321
|
-
// this.addComment(c1, stateValues.join(", "));
|
|
322
|
-
// transitionStatements.push(c1);
|
|
323
|
-
|
|
324
|
-
transitionStatements.push(
|
|
325
|
-
...Array.from(
|
|
326
|
-
new Set(
|
|
327
|
-
Array(getRandomInteger(0, stateVars.length - 2))
|
|
328
|
-
.fill(0)
|
|
329
|
-
.map(() => getRandomInteger(0, stateVars.length))
|
|
330
|
-
.filter((x) => x != modifying)
|
|
331
|
-
)
|
|
332
|
-
).map((x) => {
|
|
333
|
-
var randomNumber = getRandomInteger(-250, 250);
|
|
926
|
+
breaksInsertion.sort();
|
|
927
|
+
breaksInsertion.reverse();
|
|
334
928
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
);
|
|
339
|
-
transitionStatements.push(
|
|
340
|
-
createTransitionStatement(modifying, diff - shift)
|
|
341
|
-
);
|
|
929
|
+
breaksInsertion.forEach((index) => {
|
|
930
|
+
chunk.body.splice(index + 1, 0, BreakStatement(switchLabel));
|
|
931
|
+
});
|
|
342
932
|
|
|
343
933
|
// var c = Identifier("undefined");
|
|
344
934
|
// this.addComment(c, stateValues.join(", "));
|
|
345
935
|
// transitionStatements.push(c);
|
|
346
936
|
|
|
347
937
|
var caseObject = {
|
|
348
|
-
body: body,
|
|
938
|
+
body: chunk.body,
|
|
349
939
|
state: state,
|
|
350
|
-
nextState: nextState,
|
|
351
940
|
order: i,
|
|
352
|
-
transitionStatements: transitionStatements,
|
|
353
941
|
};
|
|
354
942
|
order[i] = caseObject;
|
|
355
943
|
|
|
356
|
-
|
|
944
|
+
cases.push(caseObject);
|
|
357
945
|
});
|
|
358
946
|
|
|
359
|
-
|
|
947
|
+
if (!this.isDebug) {
|
|
948
|
+
shuffle(cases);
|
|
949
|
+
}
|
|
360
950
|
|
|
361
951
|
var discriminant = Template(`${stateVars.join("+")}`).single().expression;
|
|
362
952
|
|
|
@@ -364,7 +954,9 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
364
954
|
|
|
365
955
|
if (functionDeclarations.size) {
|
|
366
956
|
functionDeclarations.forEach((x) => {
|
|
367
|
-
|
|
957
|
+
if (!x.id || illegalFnNames.has(x.id.name)) {
|
|
958
|
+
body.unshift(clone(x));
|
|
959
|
+
}
|
|
368
960
|
});
|
|
369
961
|
}
|
|
370
962
|
|
|
@@ -375,28 +967,36 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
375
967
|
|
|
376
968
|
statements.push(...x.body);
|
|
377
969
|
|
|
378
|
-
statements.push(...x.transitionStatements);
|
|
379
|
-
|
|
380
|
-
statements.push(BreakStatement());
|
|
381
|
-
|
|
382
970
|
var test = Literal(x.state);
|
|
383
971
|
|
|
384
972
|
return SwitchCase(test, statements);
|
|
385
973
|
})
|
|
386
974
|
);
|
|
387
975
|
|
|
976
|
+
var declarations = [];
|
|
977
|
+
|
|
978
|
+
if (needsResultAndArgVar) {
|
|
979
|
+
declarations.push(VariableDeclarator(resultVar));
|
|
980
|
+
declarations.push(VariableDeclarator(argVar));
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
declarations.push(
|
|
984
|
+
...stateVars.map((stateVar, i) => {
|
|
985
|
+
return VariableDeclarator(stateVar, Literal(initStateValues[i]));
|
|
986
|
+
})
|
|
987
|
+
);
|
|
988
|
+
|
|
388
989
|
body.push(
|
|
389
|
-
VariableDeclaration(
|
|
390
|
-
stateVars.map((stateVar, i) => {
|
|
391
|
-
return VariableDeclarator(stateVar, Literal(initStateValues[i]));
|
|
392
|
-
})
|
|
393
|
-
),
|
|
990
|
+
VariableDeclaration(declarations),
|
|
394
991
|
|
|
395
992
|
WhileStatement(
|
|
396
993
|
BinaryExpression("!=", clone(discriminant), Literal(endState)),
|
|
397
|
-
[switchStatement]
|
|
994
|
+
[LabeledStatement(switchLabel, switchStatement)]
|
|
398
995
|
)
|
|
399
996
|
);
|
|
997
|
+
|
|
998
|
+
// mark this object for switch case obfuscation
|
|
999
|
+
switchStatement.$controlFlowFlattening = true;
|
|
400
1000
|
};
|
|
401
1001
|
}
|
|
402
1002
|
}
|