js-confuser 1.2.2 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +132 -0
- package/README.md +4 -1
- package/dist/parser.js +1 -2
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +482 -91
- 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 +7 -6
- package/dist/transforms/eval.js +1 -1
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +4 -2
- package/dist/transforms/hideInitializingCode.js +4 -1
- package/dist/transforms/identifier/globalConcealing.js +18 -8
- package/dist/transforms/identifier/variableAnalysis.js +1 -1
- package/dist/transforms/label.js +11 -2
- package/dist/transforms/lock/antiDebug.js +32 -13
- package/dist/transforms/lock/lock.js +3 -3
- package/dist/transforms/minify.js +2 -2
- 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/stack.js +1 -1
- package/dist/transforms/string/encoding.js +74 -0
- package/dist/transforms/string/stringCompression.js +6 -2
- package/dist/transforms/string/stringConcealing.js +1 -1
- package/dist/transforms/string/stringSplitting.js +6 -0
- package/dist/traverse.js +0 -34
- package/dist/util/gen.js +3 -1
- package/dist/util/identifiers.js +8 -18
- package/dist/util/insert.js +4 -38
- package/package.json +2 -2
- package/src/options.ts +3 -3
- package/src/parser.ts +1 -2
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +735 -134
- 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 +16 -6
- package/src/transforms/eval.ts +2 -1
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +40 -5
- package/src/transforms/hideInitializingCode.ts +432 -425
- package/src/transforms/identifier/globalConcealing.ts +102 -38
- package/src/transforms/identifier/variableAnalysis.ts +1 -1
- package/src/transforms/label.ts +20 -2
- package/src/transforms/lock/antiDebug.ts +69 -33
- package/src/transforms/lock/lock.ts +4 -5
- package/src/transforms/minify.ts +2 -1
- 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/stack.ts +2 -1
- package/src/transforms/string/encoding.ts +107 -1
- package/src/transforms/string/stringCompression.ts +28 -3
- package/src/transforms/string/stringConcealing.ts +2 -0
- package/src/transforms/string/stringSplitting.ts +11 -0
- package/src/transforms/transform.ts +1 -2
- package/src/traverse.ts +0 -30
- package/src/util/gen.ts +5 -3
- package/src/util/identifiers.ts +18 -19
- package/src/util/insert.ts +10 -76
- package/src/util/scope.ts +9 -9
- 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/eval.test.ts +28 -0
- package/test/transforms/flatten.test.ts +28 -0
- package/test/transforms/hideInitializingCode.test.ts +336 -336
- package/test/transforms/identifier/renameVariables.test.ts +31 -0
- package/test/transforms/lock/antiDebug.test.ts +43 -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
|
+
};
|
|
193
727
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
728
|
+
var correctIndex = getRandomInteger(0, stateValues.length);
|
|
729
|
+
stateValues[correctIndex] =
|
|
730
|
+
state - (getCurrentState() - stateValues[correctIndex]);
|
|
197
731
|
|
|
198
|
-
|
|
732
|
+
labelToStates[chunk.label] = stateValues;
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// console.log(labelToStates);
|
|
736
|
+
|
|
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,66 +773,84 @@ 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[] =
|
|
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];
|
|
286
844
|
var made = 1;
|
|
287
845
|
|
|
288
|
-
|
|
846
|
+
var breaksInsertion = [];
|
|
847
|
+
var staticStateValues = [...labelToStates[chunk.label]];
|
|
848
|
+
|
|
849
|
+
chunk.body.forEach((stmt, stmtIndex) => {
|
|
850
|
+
var addBreak = false;
|
|
289
851
|
walk(stmt, [], (o, p) => {
|
|
290
852
|
if (
|
|
853
|
+
!this.isDebug &&
|
|
291
854
|
o.type == "Literal" &&
|
|
292
855
|
typeof o.value === "number" &&
|
|
293
856
|
Math.floor(o.value) === o.value &&
|
|
@@ -299,63 +862,91 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
299
862
|
return () => {
|
|
300
863
|
this.replaceIdentifierOrLiteral(
|
|
301
864
|
o,
|
|
302
|
-
numberLiteral(o.value, 0),
|
|
865
|
+
numberLiteral(o.value, 0, staticStateValues),
|
|
303
866
|
p
|
|
304
867
|
);
|
|
305
868
|
};
|
|
306
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
|
+
);
|
|
895
|
+
}
|
|
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
|
+
);
|
|
917
|
+
};
|
|
918
|
+
}
|
|
307
919
|
});
|
|
920
|
+
|
|
921
|
+
if (addBreak) {
|
|
922
|
+
breaksInsertion.push(stmtIndex);
|
|
923
|
+
}
|
|
308
924
|
});
|
|
309
925
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
var diff = nextState - state;
|
|
313
|
-
var transitionStatements = [];
|
|
314
|
-
|
|
315
|
-
ok(!isNaN(diff));
|
|
316
|
-
var modifying = getRandomInteger(0, stateVars.length);
|
|
317
|
-
var shift = 0;
|
|
318
|
-
|
|
319
|
-
// var c1 = Identifier("undefined");
|
|
320
|
-
// this.addComment(c1, stateValues.join(", "));
|
|
321
|
-
// transitionStatements.push(c1);
|
|
322
|
-
|
|
323
|
-
transitionStatements.push(
|
|
324
|
-
...Array.from(
|
|
325
|
-
new Set(
|
|
326
|
-
Array(getRandomInteger(0, stateVars.length - 2))
|
|
327
|
-
.fill(0)
|
|
328
|
-
.map(() => getRandomInteger(0, stateVars.length))
|
|
329
|
-
.filter((x) => x != modifying)
|
|
330
|
-
)
|
|
331
|
-
).map((x) => {
|
|
332
|
-
var randomNumber = getRandomInteger(-250, 250);
|
|
926
|
+
breaksInsertion.sort();
|
|
927
|
+
breaksInsertion.reverse();
|
|
333
928
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
);
|
|
338
|
-
transitionStatements.push(
|
|
339
|
-
createTransitionStatement(modifying, diff - shift)
|
|
340
|
-
);
|
|
929
|
+
breaksInsertion.forEach((index) => {
|
|
930
|
+
chunk.body.splice(index + 1, 0, BreakStatement(switchLabel));
|
|
931
|
+
});
|
|
341
932
|
|
|
342
933
|
// var c = Identifier("undefined");
|
|
343
934
|
// this.addComment(c, stateValues.join(", "));
|
|
344
935
|
// transitionStatements.push(c);
|
|
345
936
|
|
|
346
937
|
var caseObject = {
|
|
347
|
-
body: body,
|
|
938
|
+
body: chunk.body,
|
|
348
939
|
state: state,
|
|
349
|
-
nextState: nextState,
|
|
350
940
|
order: i,
|
|
351
|
-
transitionStatements: transitionStatements,
|
|
352
941
|
};
|
|
353
942
|
order[i] = caseObject;
|
|
354
943
|
|
|
355
|
-
|
|
944
|
+
cases.push(caseObject);
|
|
356
945
|
});
|
|
357
946
|
|
|
358
|
-
|
|
947
|
+
if (!this.isDebug) {
|
|
948
|
+
shuffle(cases);
|
|
949
|
+
}
|
|
359
950
|
|
|
360
951
|
var discriminant = Template(`${stateVars.join("+")}`).single().expression;
|
|
361
952
|
|
|
@@ -363,7 +954,9 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
363
954
|
|
|
364
955
|
if (functionDeclarations.size) {
|
|
365
956
|
functionDeclarations.forEach((x) => {
|
|
366
|
-
|
|
957
|
+
if (!x.id || illegalFnNames.has(x.id.name)) {
|
|
958
|
+
body.unshift(clone(x));
|
|
959
|
+
}
|
|
367
960
|
});
|
|
368
961
|
}
|
|
369
962
|
|
|
@@ -374,28 +967,36 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
374
967
|
|
|
375
968
|
statements.push(...x.body);
|
|
376
969
|
|
|
377
|
-
statements.push(...x.transitionStatements);
|
|
378
|
-
|
|
379
|
-
statements.push(BreakStatement());
|
|
380
|
-
|
|
381
970
|
var test = Literal(x.state);
|
|
382
971
|
|
|
383
972
|
return SwitchCase(test, statements);
|
|
384
973
|
})
|
|
385
974
|
);
|
|
386
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
|
+
|
|
387
989
|
body.push(
|
|
388
|
-
VariableDeclaration(
|
|
389
|
-
stateVars.map((stateVar, i) => {
|
|
390
|
-
return VariableDeclarator(stateVar, Literal(initStateValues[i]));
|
|
391
|
-
})
|
|
392
|
-
),
|
|
990
|
+
VariableDeclaration(declarations),
|
|
393
991
|
|
|
394
992
|
WhileStatement(
|
|
395
993
|
BinaryExpression("!=", clone(discriminant), Literal(endState)),
|
|
396
|
-
[switchStatement]
|
|
994
|
+
[LabeledStatement(switchLabel, switchStatement)]
|
|
397
995
|
)
|
|
398
996
|
);
|
|
997
|
+
|
|
998
|
+
// mark this object for switch case obfuscation
|
|
999
|
+
switchStatement.$controlFlowFlattening = true;
|
|
399
1000
|
};
|
|
400
1001
|
}
|
|
401
1002
|
}
|