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
|
@@ -29,10 +29,13 @@ var _controlFlowObfuscation = _interopRequireDefault(require("./controlFlowObfus
|
|
|
29
29
|
|
|
30
30
|
var _expressionObfuscation = _interopRequireDefault(require("./expressionObfuscation"));
|
|
31
31
|
|
|
32
|
-
var
|
|
32
|
+
var _switchCaseObfuscation = _interopRequireDefault(require("./switchCaseObfuscation"));
|
|
33
33
|
|
|
34
34
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
35
35
|
|
|
36
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
37
|
+
|
|
38
|
+
var flattenStructures = new Set(["IfStatement", "ForStatement", "WhileStatement", "DoWhileStatement"]);
|
|
36
39
|
/**
|
|
37
40
|
* Breaks functions into DAGs (Directed Acyclic Graphs)
|
|
38
41
|
*
|
|
@@ -46,20 +49,33 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
46
49
|
* - 2. At the end of each case, the state variable is updated to the next block of code.
|
|
47
50
|
* - 3. The while loop continues until the the state variable is the end state.
|
|
48
51
|
*/
|
|
52
|
+
|
|
49
53
|
class ControlFlowFlattening extends _transform.default {
|
|
50
54
|
constructor(o) {
|
|
51
55
|
super(o, _order.ObfuscateOrder.ControlFlowFlattening);
|
|
52
|
-
|
|
53
|
-
this
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
_defineProperty(this, "isDebug", false);
|
|
58
|
+
|
|
59
|
+
if (!this.isDebug) {
|
|
60
|
+
this.before.push(new _expressionObfuscation.default(o));
|
|
61
|
+
this.after.push(new _controlFlowObfuscation.default(o));
|
|
62
|
+
this.after.push(new _switchCaseObfuscation.default(o));
|
|
63
|
+
} else {
|
|
64
|
+
console.warn("Debug mode enabled");
|
|
65
|
+
} // this.after.push(new ChoiceFlowObfuscation(o));
|
|
66
|
+
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
match(object, parents) {
|
|
58
|
-
return (0, _traverse.isBlock)(object);
|
|
70
|
+
return (0, _traverse.isBlock)(object) && (!parents[1] || !flattenStructures.has(parents[1].type)) && (!parents[2] || !flattenStructures.has(parents[2].type));
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
transform(object, parents) {
|
|
62
74
|
return () => {
|
|
75
|
+
if (object.body.length < 3) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
if ((0, _identifiers.containsLexicallyBoundVariables)(object, parents)) {
|
|
64
80
|
return;
|
|
65
81
|
}
|
|
@@ -78,14 +94,34 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
78
94
|
|
|
79
95
|
var functionDeclarations = new Set();
|
|
80
96
|
var fnNames = new Set();
|
|
97
|
+
var illegalFnNames = new Set();
|
|
98
|
+
/**
|
|
99
|
+
* The variable names
|
|
100
|
+
*
|
|
101
|
+
* index -> var name
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
var stateVars = Array(this.isDebug ? 1 : (0, _random.getRandomInteger)(2, 5)).fill(0).map(() => this.getPlaceholder());
|
|
81
105
|
body.forEach((stmt, i) => {
|
|
82
106
|
if (stmt.type == "FunctionDeclaration") {
|
|
83
107
|
functionDeclarations.add(stmt);
|
|
84
|
-
|
|
108
|
+
var name = stmt.id && stmt.id.name;
|
|
109
|
+
fnNames.add(name);
|
|
110
|
+
|
|
111
|
+
if (stmt.body.type !== "BlockStatement") {
|
|
112
|
+
illegalFnNames.add(name);
|
|
113
|
+
} else {
|
|
114
|
+
(0, _traverse.walk)(stmt, [body, object, ...parents], (o, p) => {
|
|
115
|
+
if (o.type == "ThisExpression" || o.type == "SuperExpression" || o.type == "Identifier" && (o.name == "arguments" || o.name == "eval")) {
|
|
116
|
+
illegalFnNames.add(name);
|
|
117
|
+
return "EXIT";
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
85
121
|
}
|
|
86
122
|
});
|
|
87
123
|
(0, _traverse.walk)(object, parents, (o, p) => {
|
|
88
|
-
if (o.type == "Identifier") {
|
|
124
|
+
if (o.type == "Identifier" && fnNames.has(o.name)) {
|
|
89
125
|
var info = (0, _identifiers.getIdentifierInfo)(o, p);
|
|
90
126
|
|
|
91
127
|
if (!info.spec.isReferenced) {
|
|
@@ -103,6 +139,36 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
103
139
|
fnNames.delete(o.name);
|
|
104
140
|
}
|
|
105
141
|
}
|
|
142
|
+
|
|
143
|
+
if (!info.spec.isDefined) {
|
|
144
|
+
var b = (0, _traverse.getBlock)(o, p);
|
|
145
|
+
|
|
146
|
+
if (b !== object || !p[0] || p[0].type !== "CallExpression") {
|
|
147
|
+
illegalFnNames.add(o.name);
|
|
148
|
+
} else {
|
|
149
|
+
var isExtractable = false;
|
|
150
|
+
|
|
151
|
+
if (p[1]) {
|
|
152
|
+
if (p[1].type == "ExpressionStatement" && p[1].expression == p[0] && p[2] == object.body) {
|
|
153
|
+
isExtractable = true;
|
|
154
|
+
p[1].$callExpression = "ExpressionStatement";
|
|
155
|
+
p[1].$fnName = o.name;
|
|
156
|
+
} else if (p[1].type == "VariableDeclarator" && p[1].init == p[0] && p[2].length === 1 && p[4] == object.body) {
|
|
157
|
+
isExtractable = true;
|
|
158
|
+
p[3].$callExpression = "VariableDeclarator";
|
|
159
|
+
p[3].$fnName = o.name;
|
|
160
|
+
} else if (p[1].type == "AssignmentExpression" && p[1].operator == "=" && p[1].right === p[0] && p[2] && p[2].type == "ExpressionStatement" && p[3] == object.body) {
|
|
161
|
+
isExtractable = true;
|
|
162
|
+
p[2].$callExpression = "AssignmentExpression";
|
|
163
|
+
p[2].$fnName = o.name;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!isExtractable) {
|
|
168
|
+
illegalFnNames.add(o.name);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
106
172
|
}
|
|
107
173
|
}); // redefined function,
|
|
108
174
|
|
|
@@ -110,7 +176,9 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
110
176
|
return;
|
|
111
177
|
}
|
|
112
178
|
|
|
113
|
-
|
|
179
|
+
illegalFnNames.forEach(illegal => {
|
|
180
|
+
fnNames.delete(illegal);
|
|
181
|
+
});
|
|
114
182
|
var fraction = 0.9;
|
|
115
183
|
|
|
116
184
|
if (body.length > 20) {
|
|
@@ -123,63 +191,339 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
123
191
|
fraction = 0.5;
|
|
124
192
|
}
|
|
125
193
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
194
|
+
var resultVar = this.getPlaceholder();
|
|
195
|
+
var argVar = this.getPlaceholder();
|
|
196
|
+
var needsResultAndArgVar = false;
|
|
197
|
+
var fnToLabel = Object.create(null);
|
|
198
|
+
fnNames.forEach(fnName => {
|
|
199
|
+
fnToLabel[fnName] = this.getPlaceholder();
|
|
200
|
+
});
|
|
130
201
|
|
|
131
|
-
|
|
202
|
+
const flattenBody = (body, startingLabel = this.getPlaceholder()) => {
|
|
203
|
+
var chunks = [];
|
|
204
|
+
var currentBody = [];
|
|
205
|
+
var currentLabel = startingLabel;
|
|
132
206
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
chunks.push([x]);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
207
|
+
const finishCurrentChunk = (pointingLabel, newLabel, addGotoStatement = true) => {
|
|
208
|
+
if (!newLabel) {
|
|
209
|
+
newLabel = this.getPlaceholder();
|
|
210
|
+
}
|
|
140
211
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
212
|
+
if (!pointingLabel) {
|
|
213
|
+
pointingLabel = newLabel;
|
|
214
|
+
}
|
|
144
215
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
216
|
+
if (addGotoStatement) {
|
|
217
|
+
currentBody.push({
|
|
218
|
+
type: "GotoStatement",
|
|
219
|
+
label: pointingLabel
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
chunks.push({
|
|
224
|
+
label: currentLabel,
|
|
225
|
+
body: [...currentBody]
|
|
226
|
+
});
|
|
227
|
+
currentLabel = newLabel;
|
|
228
|
+
currentBody = [];
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
body.forEach((stmt, i) => {
|
|
232
|
+
if (functionDeclarations.has(stmt)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (stmt.$exit) {
|
|
237
|
+
currentBody.push(stmt);
|
|
238
|
+
currentBody.push((0, _gen.BreakStatement)(switchLabel));
|
|
239
|
+
finishCurrentChunk(null, null, false);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (stmt.$callExpression && fnToLabel[stmt.$fnName]) {
|
|
244
|
+
var afterPath = this.getPlaceholder();
|
|
245
|
+
var args = [];
|
|
246
|
+
|
|
247
|
+
switch (stmt.$callExpression) {
|
|
248
|
+
// var a = fn();
|
|
249
|
+
case "VariableDeclarator":
|
|
250
|
+
args = stmt.declarations[0].init.arguments;
|
|
251
|
+
stmt.declarations[0].init = (0, _gen.Identifier)(resultVar);
|
|
252
|
+
break;
|
|
253
|
+
// fn();
|
|
254
|
+
|
|
255
|
+
case "ExpressionStatement":
|
|
256
|
+
args = stmt.expression.arguments;
|
|
257
|
+
stmt.expression = (0, _gen.Identifier)("undefined");
|
|
258
|
+
break;
|
|
259
|
+
// a = fn();
|
|
260
|
+
|
|
261
|
+
case "AssignmentExpression":
|
|
262
|
+
args = stmt.expression.right.arguments;
|
|
263
|
+
stmt.expression.right = (0, _gen.Identifier)(resultVar);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
needsResultAndArgVar = true;
|
|
268
|
+
currentBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(argVar), (0, _gen.ArrayExpression)([{
|
|
269
|
+
type: "StateIdentifier",
|
|
270
|
+
label: afterPath
|
|
271
|
+
}, (0, _gen.ArrayExpression)(args)]))));
|
|
272
|
+
finishCurrentChunk(fnToLabel[stmt.$fnName], afterPath);
|
|
273
|
+
}
|
|
148
274
|
|
|
275
|
+
if (stmt.type == "GotoStatement" && i !== body.length - 1) {
|
|
276
|
+
finishCurrentChunk(stmt.label);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (stmt.type == "LabeledStatement") {
|
|
281
|
+
var lbl = stmt.label.name;
|
|
282
|
+
var control = stmt.body;
|
|
283
|
+
var isSwitchStatement = control.type === "SwitchStatement";
|
|
284
|
+
|
|
285
|
+
if (isSwitchStatement || (control.type == "ForStatement" || control.type == "WhileStatement" || control.type == "DoWhileStatement") && control.body.type == "BlockStatement") {
|
|
286
|
+
if (isSwitchStatement) {
|
|
287
|
+
if (control.cases.length == 0 || // at least 1 case
|
|
288
|
+
control.cases.find(x => !x.test || // cant be default case
|
|
289
|
+
!x.consequent.length || // must have body
|
|
290
|
+
x.consequent.findIndex(node => node.type == "BreakStatement") !== x.consequent.length - 1 || // break statement must be at the end
|
|
291
|
+
x.consequent[x.consequent.length - 1].type !== // must end with break statement
|
|
292
|
+
"BreakStatement" || !x.consequent[x.consequent.length - 1].label || // must be labeled and correct
|
|
293
|
+
x.consequent[x.consequent.length - 1].label.name != lbl)) {
|
|
294
|
+
currentBody.push(stmt);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
var isLoop = !isSwitchStatement;
|
|
300
|
+
var supportContinueStatement = isLoop;
|
|
301
|
+
var testPath = this.getPlaceholder();
|
|
302
|
+
var updatePath = this.getPlaceholder();
|
|
303
|
+
var bodyPath = this.getPlaceholder();
|
|
304
|
+
var afterPath = this.getPlaceholder();
|
|
305
|
+
var possible = true;
|
|
306
|
+
var toReplace = [];
|
|
307
|
+
(0, _traverse.walk)(control.body, [], (o, p) => {
|
|
308
|
+
if (o.type == "BreakStatement" || supportContinueStatement && o.type == "ContinueStatement") {
|
|
309
|
+
if (!o.label || o.label.name !== lbl) {
|
|
310
|
+
possible = false;
|
|
311
|
+
return "EXIT";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (o.label.name === lbl) {
|
|
315
|
+
return () => {
|
|
316
|
+
toReplace.push([o, {
|
|
317
|
+
type: "GotoStatement",
|
|
318
|
+
label: o.type == "BreakStatement" ? afterPath : updatePath
|
|
319
|
+
}]);
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
if (!possible) {
|
|
326
|
+
currentBody.push(stmt);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
toReplace.forEach(v => this.replace(v[0], v[1]));
|
|
331
|
+
|
|
332
|
+
if (isSwitchStatement) {
|
|
333
|
+
var switchVarName = this.getPlaceholder();
|
|
334
|
+
currentBody.push((0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(switchVarName, control.discriminant)));
|
|
335
|
+
var afterPath = this.getPlaceholder();
|
|
336
|
+
finishCurrentChunk();
|
|
337
|
+
control.cases.forEach((switchCase, i) => {
|
|
338
|
+
var entryPath = this.getPlaceholder();
|
|
339
|
+
currentBody.push((0, _gen.IfStatement)((0, _gen.BinaryExpression)("===", (0, _gen.Identifier)(switchVarName), switchCase.test), [{
|
|
340
|
+
type: "GotoStatement",
|
|
341
|
+
label: entryPath
|
|
342
|
+
}]));
|
|
343
|
+
chunks.push(...flattenBody([...switchCase.consequent.slice(0, switchCase.consequent.length - 1), {
|
|
344
|
+
type: "GotoStatement",
|
|
345
|
+
label: afterPath
|
|
346
|
+
}], entryPath));
|
|
347
|
+
|
|
348
|
+
if (i === control.cases.length - 1) {} else {
|
|
349
|
+
finishCurrentChunk();
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
finishCurrentChunk(afterPath, afterPath);
|
|
353
|
+
return;
|
|
354
|
+
} else if (isLoop) {
|
|
355
|
+
var isPostTest = control.type == "DoWhileStatement"; // add initializing section to current chunk
|
|
356
|
+
|
|
357
|
+
if (control.init) {
|
|
358
|
+
if (control.init.type == "VariableDeclaration") {
|
|
359
|
+
currentBody.push(control.init);
|
|
360
|
+
} else {
|
|
361
|
+
currentBody.push((0, _gen.ExpressionStatement)(control.init));
|
|
362
|
+
}
|
|
363
|
+
} // create new label called `testPath` and have current chunk point to it (goto testPath)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
|
|
367
|
+
currentBody.push((0, _gen.IfStatement)(control.test || (0, _gen.Literal)(true), [{
|
|
368
|
+
type: "GotoStatement",
|
|
369
|
+
label: bodyPath
|
|
370
|
+
}])); // create new label called `bodyPath` and have test body point to afterPath (goto afterPath)
|
|
371
|
+
|
|
372
|
+
finishCurrentChunk(afterPath, bodyPath);
|
|
373
|
+
var innerBothPath = this.getPlaceholder();
|
|
374
|
+
chunks.push(...flattenBody([...control.body.body, {
|
|
375
|
+
type: "GotoStatement",
|
|
376
|
+
label: updatePath
|
|
377
|
+
}], innerBothPath));
|
|
378
|
+
finishCurrentChunk(innerBothPath, updatePath);
|
|
379
|
+
|
|
380
|
+
if (control.update) {
|
|
381
|
+
currentBody.push((0, _gen.ExpressionStatement)(control.update));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
finishCurrentChunk(testPath, afterPath);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
149
389
|
|
|
150
|
-
|
|
151
|
-
|
|
390
|
+
if (stmt.type == "IfStatement" && stmt.consequent.type == "BlockStatement" && (!stmt.alternate || stmt.alternate.type == "BlockStatement")) {
|
|
391
|
+
finishCurrentChunk();
|
|
392
|
+
var hasAlternate = !!stmt.alternate;
|
|
393
|
+
(0, _assert.ok)(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
|
|
394
|
+
var yesPath = this.getPlaceholder();
|
|
395
|
+
var noPath = this.getPlaceholder();
|
|
396
|
+
var afterPath = this.getPlaceholder();
|
|
397
|
+
currentBody.push((0, _gen.IfStatement)(stmt.test, [{
|
|
398
|
+
type: "GotoStatement",
|
|
399
|
+
label: yesPath
|
|
400
|
+
}]));
|
|
401
|
+
chunks.push(...flattenBody([...stmt.consequent.body, {
|
|
402
|
+
type: "GotoStatement",
|
|
403
|
+
label: afterPath
|
|
404
|
+
}], yesPath));
|
|
405
|
+
|
|
406
|
+
if (hasAlternate) {
|
|
407
|
+
chunks.push(...flattenBody([...stmt.alternate.body, {
|
|
408
|
+
type: "GotoStatement",
|
|
409
|
+
label: afterPath
|
|
410
|
+
}], noPath));
|
|
411
|
+
finishCurrentChunk(noPath, afterPath);
|
|
412
|
+
} else {
|
|
413
|
+
finishCurrentChunk(afterPath, afterPath);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!currentBody.length || Math.random() < fraction) {
|
|
420
|
+
currentBody.push(stmt);
|
|
421
|
+
} else {
|
|
422
|
+
// Start new chunk
|
|
423
|
+
finishCurrentChunk();
|
|
424
|
+
currentBody.push(stmt);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
finishCurrentChunk();
|
|
428
|
+
chunks[chunks.length - 1].body.pop();
|
|
429
|
+
return chunks;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
var chunks = [];
|
|
433
|
+
/**
|
|
434
|
+
* label: switch(a+b+c){...break label...}
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
var switchLabel = this.getPlaceholder();
|
|
438
|
+
functionDeclarations.forEach(node => {
|
|
439
|
+
if (node.id && fnNames.has(node.id.name)) {
|
|
440
|
+
var exitStateName = this.getPlaceholder();
|
|
441
|
+
var argumentsName = this.getPlaceholder();
|
|
442
|
+
needsResultAndArgVar = true;
|
|
443
|
+
node.body.body.push((0, _gen.ReturnStatement)());
|
|
444
|
+
(0, _traverse.walk)(node.body, [], (o, p) => {
|
|
445
|
+
if (o.type == "ReturnStatement") {
|
|
446
|
+
if (!(0, _insert.getFunction)(o, p)) {
|
|
447
|
+
return () => {
|
|
448
|
+
var exitExpr = (0, _gen.SequenceExpression)([(0, _gen.AssignmentExpression)("=", (0, _gen.ArrayPattern)(stateVars.map(_gen.Identifier)), (0, _gen.Identifier)(exitStateName)), (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(resultVar), o.argument || (0, _gen.Identifier)("undefined"))]);
|
|
449
|
+
this.replace(o, (0, _gen.ReturnStatement)(exitExpr));
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
var declarations = [(0, _gen.VariableDeclarator)((0, _gen.ArrayPattern)([(0, _gen.Identifier)(exitStateName), (0, _gen.Identifier)(argumentsName)]), (0, _gen.Identifier)(argVar))];
|
|
455
|
+
|
|
456
|
+
if (node.params.length) {
|
|
457
|
+
declarations.push((0, _gen.VariableDeclarator)((0, _gen.ArrayPattern)(node.params), (0, _gen.Identifier)(argumentsName)));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
var innerName = this.getPlaceholder();
|
|
461
|
+
chunks.push(...flattenBody([(0, _gen.FunctionDeclaration)(innerName, [], [(0, _gen.VariableDeclaration)(declarations), ...node.body.body]), this.objectAssign((0, _gen.ExpressionStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(innerName), [])), {
|
|
462
|
+
$exit: true
|
|
463
|
+
})], fnToLabel[node.id.name]));
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
var startLabel = this.getPlaceholder();
|
|
467
|
+
chunks.push(...flattenBody(body, startLabel));
|
|
468
|
+
chunks[chunks.length - 1].body.push({
|
|
469
|
+
type: "GotoStatement",
|
|
470
|
+
label: "END_LABEL"
|
|
471
|
+
});
|
|
472
|
+
chunks.push({
|
|
473
|
+
label: "END_LABEL",
|
|
474
|
+
body: []
|
|
152
475
|
});
|
|
153
476
|
var caseSelection = new Set();
|
|
154
|
-
var uniqueStatesNeeded = chunks.length
|
|
477
|
+
var uniqueStatesNeeded = chunks.length;
|
|
478
|
+
var endLabel = chunks[Object.keys(chunks).length - 1].label;
|
|
155
479
|
|
|
156
|
-
|
|
157
|
-
var newState;
|
|
480
|
+
do {
|
|
481
|
+
var newState = (0, _random.getRandomInteger)(1, chunks.length * 15);
|
|
158
482
|
|
|
159
|
-
|
|
160
|
-
newState =
|
|
161
|
-
}
|
|
483
|
+
if (this.isDebug) {
|
|
484
|
+
newState = caseSelection.size;
|
|
485
|
+
}
|
|
162
486
|
|
|
163
487
|
caseSelection.add(newState);
|
|
164
|
-
}
|
|
488
|
+
} while (caseSelection.size !== uniqueStatesNeeded);
|
|
165
489
|
|
|
166
490
|
(0, _assert.ok)(caseSelection.size == uniqueStatesNeeded);
|
|
491
|
+
/**
|
|
492
|
+
* The accumulated state values
|
|
493
|
+
*
|
|
494
|
+
* index -> total state value
|
|
495
|
+
*/
|
|
496
|
+
|
|
167
497
|
var caseStates = Array.from(caseSelection);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
498
|
+
/**
|
|
499
|
+
* The individual state values for each label
|
|
500
|
+
*
|
|
501
|
+
* labels right now are just chunk indexes (numbers)
|
|
502
|
+
*
|
|
503
|
+
* but will expand to if statements and functions when `goto statement` obfuscation is added
|
|
504
|
+
*/
|
|
505
|
+
|
|
506
|
+
var labelToStates = Object.create(null);
|
|
507
|
+
Object.values(chunks).forEach((chunk, i) => {
|
|
508
|
+
var state = caseStates[i];
|
|
509
|
+
var stateValues = Array(stateVars.length).fill(0).map(() => (0, _random.getRandomInteger)(-250, 250));
|
|
172
510
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
511
|
+
const getCurrentState = () => {
|
|
512
|
+
return stateValues.reduce((a, b) => b + a, 0);
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
var correctIndex = (0, _random.getRandomInteger)(0, stateValues.length);
|
|
516
|
+
stateValues[correctIndex] = state - (getCurrentState() - stateValues[correctIndex]);
|
|
517
|
+
labelToStates[chunk.label] = stateValues;
|
|
518
|
+
}); // console.log(labelToStates);
|
|
176
519
|
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
var initStateValues = [...stateValues];
|
|
520
|
+
var initStateValues = [...labelToStates[startLabel]];
|
|
521
|
+
var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
|
|
180
522
|
|
|
181
|
-
const numberLiteral = (num, depth) => {
|
|
182
|
-
|
|
523
|
+
const numberLiteral = (num, depth, stateValues) => {
|
|
524
|
+
(0, _assert.ok)(Array.isArray(stateValues));
|
|
525
|
+
|
|
526
|
+
if (depth > 10 || Math.random() > 0.8 / (depth * 4)) {
|
|
183
527
|
return (0, _gen.Literal)(num);
|
|
184
528
|
}
|
|
185
529
|
|
|
@@ -189,95 +533,142 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
189
533
|
var x = (0, _random.getRandomInteger)(-250, 250);
|
|
190
534
|
var operator = (0, _random.choice)(["<", ">"]);
|
|
191
535
|
var answer = operator == "<" ? x < stateValues[opposing] : x > stateValues[opposing];
|
|
192
|
-
var correct = numberLiteral(num, depth + 1);
|
|
193
|
-
var incorrect = numberLiteral((0, _random.getRandomInteger)(-250, 250), depth + 1);
|
|
194
|
-
return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, numberLiteral(x, depth + 1), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
|
|
536
|
+
var correct = numberLiteral(num, depth + 1, stateValues);
|
|
537
|
+
var incorrect = numberLiteral((0, _random.getRandomInteger)(-250, 250), depth + 1, stateValues);
|
|
538
|
+
return (0, _gen.ConditionalExpression)((0, _gen.BinaryExpression)(operator, numberLiteral(x, depth + 1, stateValues), (0, _gen.Identifier)(stateVars[opposing])), answer ? correct : incorrect, answer ? incorrect : correct);
|
|
195
539
|
}
|
|
196
540
|
|
|
197
|
-
return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), numberLiteral(num - stateValues[opposing], depth + 1));
|
|
541
|
+
return (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)(stateVars[opposing]), numberLiteral(num - stateValues[opposing], depth + 1, stateValues));
|
|
198
542
|
};
|
|
199
543
|
|
|
200
|
-
const
|
|
201
|
-
var newValue =
|
|
544
|
+
const createTransitionExpression = (index, add, mutatingStateValues) => {
|
|
545
|
+
var newValue = mutatingStateValues[index] + add;
|
|
202
546
|
var expr = null;
|
|
203
547
|
|
|
204
|
-
if (
|
|
205
|
-
expr = (0, _gen.
|
|
548
|
+
if (this.isDebug) {
|
|
549
|
+
expr = (0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(stateVars[index]), (0, _gen.Literal)(newValue));
|
|
550
|
+
} else if (Math.random() > 0.5) {
|
|
551
|
+
expr = (0, _gen.AssignmentExpression)("+=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(add, 0, mutatingStateValues));
|
|
206
552
|
} else {
|
|
207
|
-
var double =
|
|
553
|
+
var double = mutatingStateValues[index] * 2;
|
|
208
554
|
var diff = double - newValue;
|
|
209
|
-
var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(2, 0));
|
|
210
|
-
|
|
211
|
-
expr = (0, _gen.
|
|
555
|
+
var first = (0, _gen.AssignmentExpression)("*=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(2, 0, mutatingStateValues));
|
|
556
|
+
mutatingStateValues[index] = double;
|
|
557
|
+
expr = (0, _gen.SequenceExpression)([first, (0, _gen.AssignmentExpression)("-=", (0, _gen.Identifier)(stateVars[index]), numberLiteral(diff, 0, mutatingStateValues))]);
|
|
212
558
|
}
|
|
213
559
|
|
|
214
|
-
|
|
560
|
+
mutatingStateValues[index] = newValue;
|
|
215
561
|
return expr;
|
|
216
562
|
};
|
|
217
563
|
|
|
218
564
|
var order = Object.create(null);
|
|
219
|
-
var cases =
|
|
565
|
+
var cases = [];
|
|
566
|
+
chunks.forEach((chunk, i) => {
|
|
567
|
+
// skip last case, its empty and never ran
|
|
568
|
+
if (chunk.label === endLabel) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
(0, _assert.ok)(labelToStates[chunk.label]);
|
|
573
|
+
var state = caseStates[i];
|
|
220
574
|
var made = 1;
|
|
221
|
-
|
|
575
|
+
var breaksInsertion = [];
|
|
576
|
+
var staticStateValues = [...labelToStates[chunk.label]];
|
|
577
|
+
chunk.body.forEach((stmt, stmtIndex) => {
|
|
578
|
+
var addBreak = false;
|
|
222
579
|
(0, _traverse.walk)(stmt, [], (o, p) => {
|
|
223
|
-
if (o.type == "Literal" && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100000 && Math.random() < 4 / made && !p.find(x => (0, _insert.isVarContext)(x))) {
|
|
580
|
+
if (!this.isDebug && o.type == "Literal" && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100000 && Math.random() < 4 / made && !p.find(x => (0, _insert.isVarContext)(x))) {
|
|
224
581
|
made++;
|
|
225
582
|
return () => {
|
|
226
|
-
this.replaceIdentifierOrLiteral(o, numberLiteral(o.value, 0), p);
|
|
583
|
+
this.replaceIdentifierOrLiteral(o, numberLiteral(o.value, 0, staticStateValues), p);
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (o.type == "StateIdentifier") {
|
|
588
|
+
return () => {
|
|
589
|
+
(0, _assert.ok)(labelToStates[o.label]);
|
|
590
|
+
this.replace(o, (0, _gen.ArrayExpression)(labelToStates[o.label].map(_gen.Literal)));
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (o.type == "GotoStatement") {
|
|
595
|
+
return () => {
|
|
596
|
+
var blockIndex = p.findIndex(node => (0, _traverse.isBlock)(node));
|
|
597
|
+
|
|
598
|
+
if (blockIndex === -1) {
|
|
599
|
+
addBreak = true;
|
|
600
|
+
} else {
|
|
601
|
+
var child = p[blockIndex - 2] || o;
|
|
602
|
+
var childIndex = p[blockIndex].body.indexOf(child);
|
|
603
|
+
p[blockIndex].body.splice(childIndex + 1, 0, (0, _gen.BreakStatement)(switchLabel));
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
var mutatingStateValues = [...labelToStates[chunk.label]];
|
|
607
|
+
var nextStateValues = labelToStates[o.label];
|
|
608
|
+
(0, _assert.ok)(nextStateValues, o.label);
|
|
609
|
+
this.replace(o, (0, _gen.ExpressionStatement)((0, _gen.SequenceExpression)(mutatingStateValues.map((_v, stateValueIndex) => {
|
|
610
|
+
var diff = nextStateValues[stateValueIndex] - mutatingStateValues[stateValueIndex];
|
|
611
|
+
return createTransitionExpression(stateValueIndex, diff, mutatingStateValues);
|
|
612
|
+
}))));
|
|
227
613
|
};
|
|
228
614
|
}
|
|
229
615
|
});
|
|
616
|
+
|
|
617
|
+
if (addBreak) {
|
|
618
|
+
breaksInsertion.push(stmtIndex);
|
|
619
|
+
}
|
|
230
620
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
var modifying = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
237
|
-
var shift = 0; // var c1 = Identifier("undefined");
|
|
238
|
-
// this.addComment(c1, stateValues.join(", "));
|
|
239
|
-
// transitionStatements.push(c1);
|
|
240
|
-
|
|
241
|
-
transitionStatements.push(...Array.from(new Set(Array((0, _random.getRandomInteger)(0, stateVars.length - 2)).fill(0).map(() => (0, _random.getRandomInteger)(0, stateVars.length)).filter(x => x != modifying))).map(x => {
|
|
242
|
-
var randomNumber = (0, _random.getRandomInteger)(-250, 250);
|
|
243
|
-
shift += randomNumber;
|
|
244
|
-
return createTransitionStatement(x, randomNumber);
|
|
245
|
-
}));
|
|
246
|
-
transitionStatements.push(createTransitionStatement(modifying, diff - shift)); // var c = Identifier("undefined");
|
|
621
|
+
breaksInsertion.sort();
|
|
622
|
+
breaksInsertion.reverse();
|
|
623
|
+
breaksInsertion.forEach(index => {
|
|
624
|
+
chunk.body.splice(index + 1, 0, (0, _gen.BreakStatement)(switchLabel));
|
|
625
|
+
}); // var c = Identifier("undefined");
|
|
247
626
|
// this.addComment(c, stateValues.join(", "));
|
|
248
627
|
// transitionStatements.push(c);
|
|
249
628
|
|
|
250
629
|
var caseObject = {
|
|
251
|
-
body: body,
|
|
630
|
+
body: chunk.body,
|
|
252
631
|
state: state,
|
|
253
|
-
|
|
254
|
-
order: i,
|
|
255
|
-
transitionStatements: transitionStatements
|
|
632
|
+
order: i
|
|
256
633
|
};
|
|
257
634
|
order[i] = caseObject;
|
|
258
|
-
|
|
635
|
+
cases.push(caseObject);
|
|
259
636
|
});
|
|
260
|
-
|
|
637
|
+
|
|
638
|
+
if (!this.isDebug) {
|
|
639
|
+
(0, _random.shuffle)(cases);
|
|
640
|
+
}
|
|
641
|
+
|
|
261
642
|
var discriminant = (0, _template.default)("".concat(stateVars.join("+"))).single().expression;
|
|
262
643
|
body.length = 0;
|
|
263
644
|
|
|
264
645
|
if (functionDeclarations.size) {
|
|
265
646
|
functionDeclarations.forEach(x => {
|
|
266
|
-
|
|
647
|
+
if (!x.id || illegalFnNames.has(x.id.name)) {
|
|
648
|
+
body.unshift((0, _insert.clone)(x));
|
|
649
|
+
}
|
|
267
650
|
});
|
|
268
651
|
}
|
|
269
652
|
|
|
270
653
|
var switchStatement = (0, _gen.SwitchStatement)(discriminant, cases.map((x, i) => {
|
|
271
654
|
var statements = [];
|
|
272
655
|
statements.push(...x.body);
|
|
273
|
-
statements.push(...x.transitionStatements);
|
|
274
|
-
statements.push((0, _gen.BreakStatement)());
|
|
275
656
|
var test = (0, _gen.Literal)(x.state);
|
|
276
657
|
return (0, _gen.SwitchCase)(test, statements);
|
|
277
658
|
}));
|
|
278
|
-
|
|
659
|
+
var declarations = [];
|
|
660
|
+
|
|
661
|
+
if (needsResultAndArgVar) {
|
|
662
|
+
declarations.push((0, _gen.VariableDeclarator)(resultVar));
|
|
663
|
+
declarations.push((0, _gen.VariableDeclarator)(argVar));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
declarations.push(...stateVars.map((stateVar, i) => {
|
|
279
667
|
return (0, _gen.VariableDeclarator)(stateVar, (0, _gen.Literal)(initStateValues[i]));
|
|
280
|
-
}))
|
|
668
|
+
}));
|
|
669
|
+
body.push((0, _gen.VariableDeclaration)(declarations), (0, _gen.WhileStatement)((0, _gen.BinaryExpression)("!=", (0, _insert.clone)(discriminant), (0, _gen.Literal)(endState)), [(0, _gen.LabeledStatement)(switchLabel, switchStatement)])); // mark this object for switch case obfuscation
|
|
670
|
+
|
|
671
|
+
switchStatement.$controlFlowFlattening = true;
|
|
281
672
|
};
|
|
282
673
|
}
|
|
283
674
|
|