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
|
@@ -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
|
+
}
|
|
215
|
+
|
|
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
|
+
}
|
|
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)
|
|
144
364
|
|
|
145
|
-
if (chunks.length < 2) {
|
|
146
|
-
return;
|
|
147
|
-
} // Add empty chunks
|
|
148
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)
|
|
149
371
|
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
}
|
|
389
|
+
|
|
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);
|
|
519
|
+
|
|
520
|
+
var initStateValues = [...labelToStates[startLabel]];
|
|
521
|
+
var endState = labelToStates[endLabel].reduce((a, b) => b + a, 0);
|
|
176
522
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
var initStateValues = [...stateValues];
|
|
523
|
+
const numberLiteral = (num, depth, stateValues) => {
|
|
524
|
+
(0, _assert.ok)(Array.isArray(stateValues));
|
|
180
525
|
|
|
181
|
-
|
|
182
|
-
if (depth > 12 || Math.random() > 0.9 / (depth * 4)) {
|
|
526
|
+
if (depth > 10 || Math.random() > 0.8 / (depth * 4)) {
|
|
183
527
|
return (0, _gen.Literal)(num);
|
|
184
528
|
}
|
|
185
529
|
|
|
@@ -189,99 +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 =
|
|
220
|
-
|
|
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];
|
|
574
|
+
var made = 1;
|
|
575
|
+
var breaksInsertion = [];
|
|
576
|
+
var staticStateValues = [...labelToStates[chunk.label]];
|
|
577
|
+
chunk.body.forEach((stmt, stmtIndex) => {
|
|
578
|
+
var addBreak = false;
|
|
221
579
|
(0, _traverse.walk)(stmt, [], (o, p) => {
|
|
222
|
-
if (o.type == "Literal" && typeof o.value === "number" && Math.floor(o.value) === o.value && Math.abs(o.value) < 100000 && Math.random()
|
|
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))) {
|
|
581
|
+
made++;
|
|
223
582
|
return () => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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));
|
|
228
604
|
}
|
|
229
605
|
|
|
230
|
-
|
|
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
|
+
}))));
|
|
231
613
|
};
|
|
232
614
|
}
|
|
233
615
|
});
|
|
616
|
+
|
|
617
|
+
if (addBreak) {
|
|
618
|
+
breaksInsertion.push(stmtIndex);
|
|
619
|
+
}
|
|
234
620
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
var modifying = (0, _random.getRandomInteger)(0, stateVars.length);
|
|
241
|
-
var shift = 0; // var c1 = Identifier("undefined");
|
|
242
|
-
// this.addComment(c1, stateValues.join(", "));
|
|
243
|
-
// transitionStatements.push(c1);
|
|
244
|
-
|
|
245
|
-
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 => {
|
|
246
|
-
var randomNumber = (0, _random.getRandomInteger)(-250, 250);
|
|
247
|
-
shift += randomNumber;
|
|
248
|
-
return createTransitionStatement(x, randomNumber);
|
|
249
|
-
}));
|
|
250
|
-
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");
|
|
251
626
|
// this.addComment(c, stateValues.join(", "));
|
|
252
627
|
// transitionStatements.push(c);
|
|
253
628
|
|
|
254
629
|
var caseObject = {
|
|
255
|
-
body: body,
|
|
630
|
+
body: chunk.body,
|
|
256
631
|
state: state,
|
|
257
|
-
|
|
258
|
-
order: i,
|
|
259
|
-
transitionStatements: transitionStatements
|
|
632
|
+
order: i
|
|
260
633
|
};
|
|
261
634
|
order[i] = caseObject;
|
|
262
|
-
|
|
635
|
+
cases.push(caseObject);
|
|
263
636
|
});
|
|
264
|
-
|
|
637
|
+
|
|
638
|
+
if (!this.isDebug) {
|
|
639
|
+
(0, _random.shuffle)(cases);
|
|
640
|
+
}
|
|
641
|
+
|
|
265
642
|
var discriminant = (0, _template.default)("".concat(stateVars.join("+"))).single().expression;
|
|
266
643
|
body.length = 0;
|
|
267
644
|
|
|
268
645
|
if (functionDeclarations.size) {
|
|
269
646
|
functionDeclarations.forEach(x => {
|
|
270
|
-
|
|
647
|
+
if (!x.id || illegalFnNames.has(x.id.name)) {
|
|
648
|
+
body.unshift((0, _insert.clone)(x));
|
|
649
|
+
}
|
|
271
650
|
});
|
|
272
651
|
}
|
|
273
652
|
|
|
274
653
|
var switchStatement = (0, _gen.SwitchStatement)(discriminant, cases.map((x, i) => {
|
|
275
654
|
var statements = [];
|
|
276
655
|
statements.push(...x.body);
|
|
277
|
-
statements.push(...x.transitionStatements);
|
|
278
|
-
statements.push((0, _gen.BreakStatement)());
|
|
279
656
|
var test = (0, _gen.Literal)(x.state);
|
|
280
657
|
return (0, _gen.SwitchCase)(test, statements);
|
|
281
658
|
}));
|
|
282
|
-
|
|
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) => {
|
|
283
667
|
return (0, _gen.VariableDeclarator)(stateVar, (0, _gen.Literal)(initStateValues[i]));
|
|
284
|
-
}))
|
|
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;
|
|
285
672
|
};
|
|
286
673
|
}
|
|
287
674
|
|