js-confuser 1.5.5 → 1.5.6
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 +20 -2
- package/README.md +2 -2
- package/dist/transforms/flatten.js +1 -1
- package/dist/transforms/identifier/variableAnalysis.js +4 -2
- package/dist/transforms/minify.js +2 -2
- package/dist/transforms/rgf.js +48 -7
- package/dist/transforms/string/stringSplitting.js +1 -22
- package/package.json +2 -2
- package/src/transforms/flatten.ts +2 -2
- package/src/transforms/identifier/variableAnalysis.ts +4 -4
- package/src/transforms/minify.ts +5 -4
- package/src/transforms/rgf.ts +125 -26
- package/src/transforms/string/stringSplitting.ts +1 -25
- package/test/transforms/minify.test.ts +50 -5
- package/test/transforms/rgf.test.ts +97 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
# `1.5.6`
|
|
2
|
+
Website changed and RGF fixes
|
|
3
|
+
|
|
4
|
+
The website is back at a different domain now: [https://master--hungry-shannon-c1ce6b.netlify.app/](https://master--hungry-shannon-c1ce6b.netlify.app/)
|
|
5
|
+
|
|
6
|
+
This update focuses on fixing RGF bugs
|
|
7
|
+
|
|
8
|
+
- Fixed [#64](https://github.com/MichaelXF/js-confuser/issues/64)
|
|
9
|
+
- - RGF to properly handle Arrow functions and function expressions
|
|
10
|
+
|
|
11
|
+
- RGF will no longer change getter/setter methods
|
|
12
|
+
|
|
13
|
+
- RGF will no longer change class methods
|
|
14
|
+
|
|
15
|
+
- RGF now works when using `mangled` variable names
|
|
16
|
+
|
|
17
|
+
- Minify will remove unreachable code following a Throw statement
|
|
18
|
+
|
|
1
19
|
# `1.5.5`
|
|
2
20
|
Updates
|
|
3
21
|
|
|
4
22
|
- Fixed [#53](https://github.com/MichaelXF/js-confuser/issues/53)
|
|
5
|
-
- - Shuffle to not use common
|
|
23
|
+
- - Shuffle to not use common variable names like `x`
|
|
6
24
|
|
|
7
25
|
- Fixed [#60](https://github.com/MichaelXF/js-confuser/issues/60)
|
|
8
26
|
- - Rename Variables to properly handle function parameters
|
|
@@ -447,7 +465,7 @@ Available now on NPM: https://www.npmjs.com/package/js-confuser
|
|
|
447
465
|
# `1.1.7`
|
|
448
466
|
Website is Live
|
|
449
467
|
|
|
450
|
-
- [
|
|
468
|
+
- [https://master--hungry-shannon-c1ce6b.netlify.app/](https://master--hungry-shannon-c1ce6b.netlify.app/) is live!
|
|
451
469
|
- Check out the [js-confuser-website](https://github.com/MichaelXF/js-confuser-website) repo for more info
|
|
452
470
|
|
|
453
471
|
- **⚠️ Breaking change**: `Rename Globals` is now enabled by default
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# JS Confuser
|
|
2
2
|
|
|
3
|
-
JS-Confuser is a JavaScript obfuscation tool to make your programs _impossible_ to read. [Try the web version](https://
|
|
3
|
+
JS-Confuser is a JavaScript obfuscation tool to make your programs _impossible_ to read. [Try the web version](https://master--hungry-shannon-c1ce6b.netlify.app/).
|
|
4
4
|
|
|
5
|
-
[](https://npmjs.com/package/js-confuser) [](https://github.com/MichaelXF/js-confuser) [](https://
|
|
5
|
+
[](https://npmjs.com/package/js-confuser) [](https://github.com/MichaelXF/js-confuser) [](https://master--hungry-shannon-c1ce6b.netlify.app/)
|
|
6
6
|
|
|
7
7
|
## Key features
|
|
8
8
|
|
|
@@ -224,7 +224,7 @@ class Flatten extends _transform.default {
|
|
|
224
224
|
|
|
225
225
|
var call = (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(resultName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)("_", (0, _gen.AssignmentExpression)("=", identifier, (0, _gen.ArrayExpression)([(0, _gen.ArrayExpression)(input.map(_gen.Identifier)), (0, _gen.ArrayExpression)([...newParamNodes]), (0, _gen.Identifier)(resultName)])))]); // result.pop()
|
|
226
226
|
|
|
227
|
-
var pop = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), (0, _gen.
|
|
227
|
+
var pop = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), (0, _gen.Literal)("pop"), true), []); // var result = newFn.call([...refs], ...arguments)
|
|
228
228
|
// modified1 = result.pop();
|
|
229
229
|
// modified2 = result.pop();
|
|
230
230
|
// ...modifiedN = result.pop();...
|
|
@@ -103,8 +103,10 @@ class VariableAnalysis extends _transform.default {
|
|
|
103
103
|
var definingContexts = info.spec.isDefined ? (0, _insert.getAllDefiningContexts)(o, p) : (0, _insert.getReferencingContexts)(o, p, info);
|
|
104
104
|
(0, _assert.ok)(definingContexts.length);
|
|
105
105
|
definingContexts.forEach(definingContext => {
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
// ok(
|
|
107
|
+
// isContext(definingContext),
|
|
108
|
+
// `${definingContext.type} is not a context`
|
|
109
|
+
// );
|
|
108
110
|
if (isDefined) {
|
|
109
111
|
// Add to defined Map
|
|
110
112
|
if (!this.defined.has(definingContext)) {
|
|
@@ -53,7 +53,7 @@ class Minify extends _transform.default {
|
|
|
53
53
|
var earlyReturn = body.length;
|
|
54
54
|
var fnDecs = [];
|
|
55
55
|
body.forEach((stmt, i) => {
|
|
56
|
-
if (stmt.type
|
|
56
|
+
if (stmt.type === "ReturnStatement" || stmt.type === "BreakStatement" || stmt.type === "ContinueStatement" || stmt.type === "ThrowStatement") {
|
|
57
57
|
if (earlyReturn > i + 1) {
|
|
58
58
|
earlyReturn = i + 1;
|
|
59
59
|
}
|
|
@@ -136,7 +136,7 @@ class Minify extends _transform.default {
|
|
|
136
136
|
if (!lastDec || lastDec.kind !== x.kind || !lastDec.declarations.length) {
|
|
137
137
|
lastDec = x;
|
|
138
138
|
} else {
|
|
139
|
-
lastDec.declarations.push(...x.declarations);
|
|
139
|
+
lastDec.declarations.push(...(0, _insert.clone)(x.declarations));
|
|
140
140
|
remove.unshift(i);
|
|
141
141
|
}
|
|
142
142
|
} else {
|
package/dist/transforms/rgf.js
CHANGED
|
@@ -25,6 +25,8 @@ var _identifiers = require("../util/identifiers");
|
|
|
25
25
|
|
|
26
26
|
var _insert = require("../util/insert");
|
|
27
27
|
|
|
28
|
+
var _random = require("../util/random");
|
|
29
|
+
|
|
28
30
|
var _transform = _interopRequireDefault(require("./transform"));
|
|
29
31
|
|
|
30
32
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
@@ -69,9 +71,22 @@ class RGF extends _transform.default {
|
|
|
69
71
|
var collect = [];
|
|
70
72
|
var queue = [];
|
|
71
73
|
var names = new Map();
|
|
74
|
+
var referenceSignatures = {};
|
|
72
75
|
var definingNodes = new Map();
|
|
73
76
|
(0, _traverse.walk)(contextObject, contextParents, (object, parents) => {
|
|
74
77
|
if (object !== contextObject && (0, _insert.isFunction)(object) && !object.$requiresEval && !object.async && !object.generator && (0, _insert.getVarContext)(parents[0], parents.slice(1)) === contextObject) {
|
|
78
|
+
// Discard getter/setter methods
|
|
79
|
+
if (parents[0].type === "Property" && parents[0].value === object) {
|
|
80
|
+
if (parents[0].method || parents[0].kind === "get" || parents[0].kind === "set") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
} // Discard class methods
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
var defined = new Set(),
|
|
76
91
|
referenced = new Set();
|
|
77
92
|
var isBound = false;
|
|
@@ -159,6 +174,7 @@ class RGF extends _transform.default {
|
|
|
159
174
|
if (object.type == "FunctionDeclaration" && typeof object.id.name === "string") {
|
|
160
175
|
var index = names.size;
|
|
161
176
|
names.set(object.id.name, index);
|
|
177
|
+
referenceSignatures[index] = (0, _random.getRandomString)(10);
|
|
162
178
|
definingNodes.set(object.id.name, object.id);
|
|
163
179
|
}
|
|
164
180
|
}
|
|
@@ -166,9 +182,10 @@ class RGF extends _transform.default {
|
|
|
166
182
|
|
|
167
183
|
if (!queue.length) {
|
|
168
184
|
return;
|
|
169
|
-
}
|
|
185
|
+
} // An array containing all the function declarations
|
|
186
|
+
|
|
170
187
|
|
|
171
|
-
var referenceArray =
|
|
188
|
+
var referenceArray = "_" + (0, _random.getRandomString)(10);
|
|
172
189
|
(0, _traverse.walk)(contextObject, contextParents, (o, p) => {
|
|
173
190
|
if (o.type == "Identifier" && !_constants.reservedIdentifiers.has(o.name)) {
|
|
174
191
|
var index = names.get(o.name);
|
|
@@ -185,7 +202,19 @@ class RGF extends _transform.default {
|
|
|
185
202
|
|
|
186
203
|
if (pointingTo == shouldBe) {
|
|
187
204
|
this.log(o.name, "->", "".concat(referenceArray, "[").concat(index, "]"));
|
|
188
|
-
|
|
205
|
+
var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(referenceArray), (0, _gen.Literal)(index), true); // Allow re-assignment to the RGF function
|
|
206
|
+
|
|
207
|
+
if (p[0] && p[0].type === "AssignmentExpression" && p[0].left === o) {
|
|
208
|
+
// fn = ...
|
|
209
|
+
this.replace(o, memberExpression);
|
|
210
|
+
} else {
|
|
211
|
+
// fn()
|
|
212
|
+
// fn
|
|
213
|
+
// In most cases the identifier is being used like this (call expression, or referenced to be called later)
|
|
214
|
+
// Replace it with a simple wrapper function that will pass on the reference array
|
|
215
|
+
var conditionalExpression = (0, _gen.ConditionalExpression)((0, _template.default)("typeof ".concat(referenceArray, "[").concat(index, "] === \"function\" && ").concat(referenceArray, "[").concat(index, "][\"").concat(referenceSignatures[index] || "_", "\"]")).single().expression, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)(memberExpression, [(0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]), memberExpression);
|
|
216
|
+
this.replace(o, conditionalExpression);
|
|
217
|
+
}
|
|
189
218
|
}
|
|
190
219
|
}
|
|
191
220
|
}
|
|
@@ -202,6 +231,7 @@ class RGF extends _transform.default {
|
|
|
202
231
|
var name = object === null || object === void 0 ? void 0 : (_object$id2 = object.id) === null || _object$id2 === void 0 ? void 0 : _object$id2.name;
|
|
203
232
|
var hasName = !!name;
|
|
204
233
|
var params = object.params.map(x => x.name) || [];
|
|
234
|
+
var signature = referenceSignatures[names.get(name)];
|
|
205
235
|
var embeddedName = name || this.getPlaceholder(); // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
|
|
206
236
|
// RGF runs early and needs completed code before converting to a string.
|
|
207
237
|
// (^ the variables haven't been renamed yet)
|
|
@@ -222,7 +252,7 @@ class RGF extends _transform.default {
|
|
|
222
252
|
};
|
|
223
253
|
var tree = {
|
|
224
254
|
type: "Program",
|
|
225
|
-
body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedName), (0, _gen.
|
|
255
|
+
body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedName), (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.SpreadElement)((0, _template.default)("Array.prototype.slice.call(arguments, 1)").single().expression)]))]
|
|
226
256
|
};
|
|
227
257
|
tree.__hiddenDeclarations = (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(referenceArray));
|
|
228
258
|
tree.__hiddenDeclarations.hidden = true;
|
|
@@ -241,8 +271,17 @@ class RGF extends _transform.default {
|
|
|
241
271
|
var toString = (0, _compiler.compileJsSync)(tree, this.options);
|
|
242
272
|
var newFunction = (0, _gen.NewExpression)((0, _gen.Identifier)("Function"), [(0, _gen.Literal)(referenceArray), (0, _gen.Literal)(toString)]);
|
|
243
273
|
|
|
244
|
-
|
|
245
|
-
|
|
274
|
+
function applySignature(fn) {
|
|
275
|
+
if (!signature) {
|
|
276
|
+
return fn;
|
|
277
|
+
} // This code marks the function object with a unique property
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
return (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("fn", fn)), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)("fn"), (0, _gen.Literal)(signature), true), (0, _gen.Literal)(true))), (0, _gen.ReturnStatement)((0, _gen.Identifier)("fn"))]), []);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (object.type === "FunctionDeclaration") {
|
|
284
|
+
arrayExpression.elements[names.get(name)] = applySignature(newFunction);
|
|
246
285
|
|
|
247
286
|
if (Array.isArray(parents[0])) {
|
|
248
287
|
parents[0].splice(parents[0].indexOf(object), 1);
|
|
@@ -250,7 +289,9 @@ class RGF extends _transform.default {
|
|
|
250
289
|
this.error(new Error("Error deleting function declaration: " + parents.map(x => x.type).join(",")));
|
|
251
290
|
}
|
|
252
291
|
} else {
|
|
253
|
-
|
|
292
|
+
// The wrapper function passes the reference array around
|
|
293
|
+
var wrapperFunction = (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)(newFunction, (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]);
|
|
294
|
+
this.replace(object, applySignature(wrapperFunction));
|
|
254
295
|
}
|
|
255
296
|
});
|
|
256
297
|
};
|
|
@@ -45,28 +45,8 @@ class StringSplitting extends _transform.default {
|
|
|
45
45
|
this.vars = [];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
apply(tree) {
|
|
49
|
-
super.apply(tree);
|
|
50
|
-
|
|
51
|
-
if (this.vars.length) {
|
|
52
|
-
(0, _random.shuffle)(this.adders);
|
|
53
|
-
(0, _random.shuffle)(this.vars);
|
|
54
|
-
var body = tree.body;
|
|
55
|
-
this.adders.forEach(nodes => {
|
|
56
|
-
nodes.forEach(x => body.unshift(x));
|
|
57
|
-
});
|
|
58
|
-
var variableDeclaration = {
|
|
59
|
-
type: "VariableDeclaration",
|
|
60
|
-
declarations: [],
|
|
61
|
-
kind: "var"
|
|
62
|
-
};
|
|
63
|
-
this.vars.forEach(node => variableDeclaration.declarations.push(node));
|
|
64
|
-
body.unshift(variableDeclaration);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
48
|
match(object, parents) {
|
|
69
|
-
return object.type == "Literal" && typeof object.value === "string" && !(0, _stringConcealing.isModuleSource)(object, parents) && !(0, _compare.isDirective)(object, parents);
|
|
49
|
+
return object.type == "Literal" && typeof object.value === "string" && object.value.length >= 8 && !(0, _stringConcealing.isModuleSource)(object, parents) && !(0, _compare.isDirective)(object, parents);
|
|
70
50
|
}
|
|
71
51
|
|
|
72
52
|
transform(object, parents) {
|
|
@@ -92,7 +72,6 @@ class StringSplitting extends _transform.default {
|
|
|
92
72
|
var last = chunks.pop();
|
|
93
73
|
chunks.forEach((chunk, i) => {
|
|
94
74
|
if (i == 0) {
|
|
95
|
-
(0, _assert.ok)(i == 0);
|
|
96
75
|
parent = binaryExpression = (0, _gen.BinaryExpression)("+", (0, _gen.Literal)(chunk), (0, _gen.Literal)(""));
|
|
97
76
|
} else {
|
|
98
77
|
binaryExpression.left = (0, _gen.BinaryExpression)("+", (0, _insert.clone)(binaryExpression.left), (0, _gen.Literal)(chunk));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-confuser",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"description": "JavaScript Obfuscation Tool.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"bugs": {
|
|
46
46
|
"url": "https://github.com/MichaelXF/js-confuser/issues"
|
|
47
47
|
},
|
|
48
|
-
"homepage": "https://
|
|
48
|
+
"homepage": "https://master--hungry-shannon-c1ce6b.netlify.app/",
|
|
49
49
|
"jest": {
|
|
50
50
|
"coverageReporters": [
|
|
51
51
|
"html"
|
|
@@ -328,8 +328,8 @@ export default class Flatten extends Transform {
|
|
|
328
328
|
var pop = CallExpression(
|
|
329
329
|
MemberExpression(
|
|
330
330
|
MemberExpression(Identifier(resultName), Identifier(propName), false),
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
Literal("pop"),
|
|
332
|
+
true
|
|
333
333
|
),
|
|
334
334
|
[]
|
|
335
335
|
);
|
|
@@ -93,10 +93,10 @@ export default class VariableAnalysis extends Transform {
|
|
|
93
93
|
ok(definingContexts.length);
|
|
94
94
|
|
|
95
95
|
definingContexts.forEach((definingContext) => {
|
|
96
|
-
ok(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
96
|
+
// ok(
|
|
97
|
+
// isContext(definingContext),
|
|
98
|
+
// `${definingContext.type} is not a context`
|
|
99
|
+
// );
|
|
100
100
|
|
|
101
101
|
if (isDefined) {
|
|
102
102
|
// Add to defined Map
|
package/src/transforms/minify.ts
CHANGED
|
@@ -61,9 +61,10 @@ export default class Minify extends Transform {
|
|
|
61
61
|
|
|
62
62
|
body.forEach((stmt, i) => {
|
|
63
63
|
if (
|
|
64
|
-
stmt.type
|
|
65
|
-
stmt.type
|
|
66
|
-
stmt.type
|
|
64
|
+
stmt.type === "ReturnStatement" ||
|
|
65
|
+
stmt.type === "BreakStatement" ||
|
|
66
|
+
stmt.type === "ContinueStatement" ||
|
|
67
|
+
stmt.type === "ThrowStatement"
|
|
67
68
|
) {
|
|
68
69
|
if (earlyReturn > i + 1) {
|
|
69
70
|
earlyReturn = i + 1;
|
|
@@ -156,7 +157,7 @@ export default class Minify extends Transform {
|
|
|
156
157
|
) {
|
|
157
158
|
lastDec = x;
|
|
158
159
|
} else {
|
|
159
|
-
lastDec.declarations.push(...x.declarations);
|
|
160
|
+
lastDec.declarations.push(...clone(x.declarations));
|
|
160
161
|
remove.unshift(i);
|
|
161
162
|
}
|
|
162
163
|
} else {
|
package/src/transforms/rgf.ts
CHANGED
|
@@ -7,7 +7,10 @@ import Template from "../templates/template";
|
|
|
7
7
|
import traverse, { walk } from "../traverse";
|
|
8
8
|
import {
|
|
9
9
|
ArrayExpression,
|
|
10
|
+
AssignmentExpression,
|
|
10
11
|
CallExpression,
|
|
12
|
+
ConditionalExpression,
|
|
13
|
+
ExpressionStatement,
|
|
11
14
|
FunctionExpression,
|
|
12
15
|
Identifier,
|
|
13
16
|
Literal,
|
|
@@ -28,6 +31,7 @@ import {
|
|
|
28
31
|
isFunction,
|
|
29
32
|
prepend,
|
|
30
33
|
} from "../util/insert";
|
|
34
|
+
import { getRandomString } from "../util/random";
|
|
31
35
|
import Transform from "./transform";
|
|
32
36
|
|
|
33
37
|
/**
|
|
@@ -70,6 +74,8 @@ export default class RGF extends Transform {
|
|
|
70
74
|
}[] = [];
|
|
71
75
|
var queue: Location[] = [];
|
|
72
76
|
var names = new Map<string, number>();
|
|
77
|
+
var referenceSignatures: { [name: string]: string } = {};
|
|
78
|
+
|
|
73
79
|
var definingNodes = new Map<string, Node>();
|
|
74
80
|
|
|
75
81
|
walk(contextObject, contextParents, (object, parents) => {
|
|
@@ -81,6 +87,25 @@ export default class RGF extends Transform {
|
|
|
81
87
|
!object.generator &&
|
|
82
88
|
getVarContext(parents[0], parents.slice(1)) === contextObject
|
|
83
89
|
) {
|
|
90
|
+
// Discard getter/setter methods
|
|
91
|
+
if (parents[0].type === "Property" && parents[0].value === object) {
|
|
92
|
+
if (
|
|
93
|
+
parents[0].method ||
|
|
94
|
+
parents[0].kind === "get" ||
|
|
95
|
+
parents[0].kind === "set"
|
|
96
|
+
) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Discard class methods
|
|
102
|
+
if (
|
|
103
|
+
parents[0].type === "MethodDefinition" &&
|
|
104
|
+
parents[0].value === object
|
|
105
|
+
) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
84
109
|
var defined = new Set<string>(),
|
|
85
110
|
referenced = new Set<string>();
|
|
86
111
|
|
|
@@ -177,6 +202,8 @@ export default class RGF extends Transform {
|
|
|
177
202
|
var index = names.size;
|
|
178
203
|
|
|
179
204
|
names.set(object.id.name, index);
|
|
205
|
+
referenceSignatures[index] = getRandomString(10);
|
|
206
|
+
|
|
180
207
|
definingNodes.set(object.id.name, object.id);
|
|
181
208
|
}
|
|
182
209
|
}
|
|
@@ -186,7 +213,8 @@ export default class RGF extends Transform {
|
|
|
186
213
|
return;
|
|
187
214
|
}
|
|
188
215
|
|
|
189
|
-
|
|
216
|
+
// An array containing all the function declarations
|
|
217
|
+
var referenceArray = "_" + getRandomString(10);
|
|
190
218
|
|
|
191
219
|
walk(contextObject, contextParents, (o, p) => {
|
|
192
220
|
if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
|
|
@@ -204,27 +232,50 @@ export default class RGF extends Transform {
|
|
|
204
232
|
if (pointingTo == shouldBe) {
|
|
205
233
|
this.log(o.name, "->", `${referenceArray}[${index}]`);
|
|
206
234
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
235
|
+
var memberExpression = MemberExpression(
|
|
236
|
+
Identifier(referenceArray),
|
|
237
|
+
Literal(index),
|
|
238
|
+
true
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// Allow re-assignment to the RGF function
|
|
242
|
+
if (
|
|
243
|
+
p[0] &&
|
|
244
|
+
p[0].type === "AssignmentExpression" &&
|
|
245
|
+
p[0].left === o
|
|
246
|
+
) {
|
|
247
|
+
// fn = ...
|
|
248
|
+
|
|
249
|
+
this.replace(o, memberExpression);
|
|
250
|
+
} else {
|
|
251
|
+
// fn()
|
|
252
|
+
// fn
|
|
253
|
+
|
|
254
|
+
// In most cases the identifier is being used like this (call expression, or referenced to be called later)
|
|
255
|
+
// Replace it with a simple wrapper function that will pass on the reference array
|
|
256
|
+
|
|
257
|
+
var conditionalExpression = ConditionalExpression(
|
|
258
|
+
Template(
|
|
259
|
+
`typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
|
|
260
|
+
referenceSignatures[index] || "_"
|
|
261
|
+
}"]`
|
|
262
|
+
).single().expression,
|
|
263
|
+
FunctionExpression(
|
|
264
|
+
[],
|
|
265
|
+
[
|
|
266
|
+
ReturnStatement(
|
|
267
|
+
CallExpression(memberExpression, [
|
|
220
268
|
Identifier(referenceArray),
|
|
221
269
|
SpreadElement(Identifier("arguments")),
|
|
222
|
-
]
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
270
|
+
])
|
|
271
|
+
),
|
|
272
|
+
]
|
|
273
|
+
),
|
|
274
|
+
memberExpression
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
this.replace(o, conditionalExpression);
|
|
278
|
+
}
|
|
228
279
|
}
|
|
229
280
|
}
|
|
230
281
|
}
|
|
@@ -243,6 +294,7 @@ export default class RGF extends Transform {
|
|
|
243
294
|
var name = object?.id?.name;
|
|
244
295
|
var hasName = !!name;
|
|
245
296
|
var params = object.params.map((x) => x.name) || [];
|
|
297
|
+
var signature = referenceSignatures[names.get(name)];
|
|
246
298
|
|
|
247
299
|
var embeddedName = name || this.getPlaceholder();
|
|
248
300
|
|
|
@@ -280,11 +332,11 @@ export default class RGF extends Transform {
|
|
|
280
332
|
CallExpression(
|
|
281
333
|
MemberExpression(
|
|
282
334
|
Identifier(embeddedName),
|
|
283
|
-
|
|
284
|
-
|
|
335
|
+
Literal("call"),
|
|
336
|
+
true
|
|
285
337
|
),
|
|
286
338
|
[
|
|
287
|
-
|
|
339
|
+
Identifier("undefined"),
|
|
288
340
|
SpreadElement(
|
|
289
341
|
Template(
|
|
290
342
|
`Array.prototype.slice.call(arguments, 1)`
|
|
@@ -322,8 +374,38 @@ export default class RGF extends Transform {
|
|
|
322
374
|
Literal(toString),
|
|
323
375
|
]);
|
|
324
376
|
|
|
325
|
-
|
|
326
|
-
|
|
377
|
+
function applySignature(fn) {
|
|
378
|
+
if (!signature) {
|
|
379
|
+
return fn;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// This code marks the function object with a unique property
|
|
383
|
+
return CallExpression(
|
|
384
|
+
FunctionExpression(
|
|
385
|
+
[],
|
|
386
|
+
[
|
|
387
|
+
VariableDeclaration(VariableDeclarator("fn", fn)),
|
|
388
|
+
ExpressionStatement(
|
|
389
|
+
AssignmentExpression(
|
|
390
|
+
"=",
|
|
391
|
+
MemberExpression(
|
|
392
|
+
Identifier("fn"),
|
|
393
|
+
Literal(signature),
|
|
394
|
+
true
|
|
395
|
+
),
|
|
396
|
+
Literal(true)
|
|
397
|
+
)
|
|
398
|
+
),
|
|
399
|
+
ReturnStatement(Identifier("fn")),
|
|
400
|
+
]
|
|
401
|
+
),
|
|
402
|
+
[]
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (object.type === "FunctionDeclaration") {
|
|
407
|
+
arrayExpression.elements[names.get(name)] =
|
|
408
|
+
applySignature(newFunction);
|
|
327
409
|
|
|
328
410
|
if (Array.isArray(parents[0])) {
|
|
329
411
|
parents[0].splice(parents[0].indexOf(object), 1);
|
|
@@ -336,7 +418,24 @@ export default class RGF extends Transform {
|
|
|
336
418
|
);
|
|
337
419
|
}
|
|
338
420
|
} else {
|
|
339
|
-
|
|
421
|
+
// The wrapper function passes the reference array around
|
|
422
|
+
var wrapperFunction = FunctionExpression(
|
|
423
|
+
[],
|
|
424
|
+
[
|
|
425
|
+
ReturnStatement(
|
|
426
|
+
CallExpression(
|
|
427
|
+
MemberExpression(newFunction, Literal("call"), true),
|
|
428
|
+
[
|
|
429
|
+
Identifier("undefined"),
|
|
430
|
+
Identifier(referenceArray),
|
|
431
|
+
SpreadElement(Identifier("arguments")),
|
|
432
|
+
]
|
|
433
|
+
)
|
|
434
|
+
),
|
|
435
|
+
]
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
this.replace(object, applySignature(wrapperFunction));
|
|
340
439
|
}
|
|
341
440
|
});
|
|
342
441
|
};
|
|
@@ -25,34 +25,11 @@ export default class StringSplitting extends Transform {
|
|
|
25
25
|
this.vars = [];
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
apply(tree) {
|
|
29
|
-
super.apply(tree);
|
|
30
|
-
|
|
31
|
-
if (this.vars.length) {
|
|
32
|
-
shuffle(this.adders);
|
|
33
|
-
shuffle(this.vars);
|
|
34
|
-
|
|
35
|
-
var body: Node[] = tree.body;
|
|
36
|
-
|
|
37
|
-
this.adders.forEach((nodes) => {
|
|
38
|
-
nodes.forEach((x) => body.unshift(x));
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
var variableDeclaration = {
|
|
42
|
-
type: "VariableDeclaration",
|
|
43
|
-
declarations: [],
|
|
44
|
-
kind: "var",
|
|
45
|
-
};
|
|
46
|
-
this.vars.forEach((node) => variableDeclaration.declarations.push(node));
|
|
47
|
-
|
|
48
|
-
body.unshift(variableDeclaration);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
28
|
match(object: Node, parents: Node[]) {
|
|
53
29
|
return (
|
|
54
30
|
object.type == "Literal" &&
|
|
55
31
|
typeof object.value === "string" &&
|
|
32
|
+
object.value.length >= 8 &&
|
|
56
33
|
!isModuleSource(object, parents) &&
|
|
57
34
|
!isDirective(object, parents)
|
|
58
35
|
);
|
|
@@ -87,7 +64,6 @@ export default class StringSplitting extends Transform {
|
|
|
87
64
|
var last = chunks.pop();
|
|
88
65
|
chunks.forEach((chunk, i) => {
|
|
89
66
|
if (i == 0) {
|
|
90
|
-
ok(i == 0);
|
|
91
67
|
parent = binaryExpression = BinaryExpression(
|
|
92
68
|
"+",
|
|
93
69
|
Literal(chunk),
|
|
@@ -301,9 +301,9 @@ test("Variant #15: Removing implied 'return'", async () => {
|
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
// https://github.com/MichaelXF/js-confuser/issues/43
|
|
304
|
-
test("Variant #16: Handle deconstructuring in for loop", async ()=>{
|
|
305
|
-
// Valid
|
|
306
|
-
var output = await JsConfuser(
|
|
304
|
+
test("Variant #16: Handle deconstructuring in for loop", async () => {
|
|
305
|
+
// Valid
|
|
306
|
+
var output = await JsConfuser(
|
|
307
307
|
`
|
|
308
308
|
for(const [a] of [[1]]) {
|
|
309
309
|
input(a);
|
|
@@ -313,11 +313,56 @@ var output = await JsConfuser(
|
|
|
313
313
|
);
|
|
314
314
|
|
|
315
315
|
var value;
|
|
316
|
-
function input(valueIn){
|
|
316
|
+
function input(valueIn) {
|
|
317
317
|
value = valueIn;
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
eval(output);
|
|
321
321
|
|
|
322
322
|
expect(value).toStrictEqual(1);
|
|
323
|
-
})
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("Variant #17: Remove unreachable code following a return statement", async () => {
|
|
326
|
+
var output = await JsConfuser(
|
|
327
|
+
`
|
|
328
|
+
function myFunction(){
|
|
329
|
+
return;
|
|
330
|
+
unreachableStmt;
|
|
331
|
+
}
|
|
332
|
+
`,
|
|
333
|
+
{ target: "node", minify: true }
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
expect(output).not.toContain("unreachableStmt");
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("Variant #18: Remove unreachable code following a continue or break statement", async () => {
|
|
340
|
+
var output = await JsConfuser(
|
|
341
|
+
`
|
|
342
|
+
for(var i =0; i < 10; i++){
|
|
343
|
+
continue;
|
|
344
|
+
unreachableStmt
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
while(true){
|
|
348
|
+
break;
|
|
349
|
+
unreachableStmt
|
|
350
|
+
}
|
|
351
|
+
`,
|
|
352
|
+
{ target: "node", minify: true }
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
expect(output).not.toContain("unreachableStmt");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("Variant #19: Remove unreachable code following a throw statement", async () => {
|
|
359
|
+
var output = await JsConfuser(
|
|
360
|
+
`
|
|
361
|
+
throw new Error("No more code to run");
|
|
362
|
+
unreachableStmt;
|
|
363
|
+
`,
|
|
364
|
+
{ target: "node", minify: true }
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
expect(output).not.toContain("unreachableStmt");
|
|
368
|
+
});
|
|
@@ -154,6 +154,103 @@ input(console.log, result)
|
|
|
154
154
|
eval(output);
|
|
155
155
|
expect(value).toStrictEqual(60);
|
|
156
156
|
});
|
|
157
|
+
|
|
158
|
+
// https://github.com/MichaelXF/js-confuser/issues/64
|
|
159
|
+
it("should work on Arrow Functions", async () => {
|
|
160
|
+
var output = await JsConfuser.obfuscate(
|
|
161
|
+
`
|
|
162
|
+
var double = (num)=>num*2;
|
|
163
|
+
TEST_VALUE = double(10);
|
|
164
|
+
`,
|
|
165
|
+
{
|
|
166
|
+
target: "node",
|
|
167
|
+
rgf: true,
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
expect(output).toContain("new Function");
|
|
172
|
+
|
|
173
|
+
var TEST_VALUE;
|
|
174
|
+
|
|
175
|
+
eval(output);
|
|
176
|
+
expect(TEST_VALUE).toStrictEqual(20);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should work on Function Expressions", async () => {
|
|
180
|
+
var output = await JsConfuser.obfuscate(
|
|
181
|
+
`
|
|
182
|
+
var double = function(num){
|
|
183
|
+
return num * 2
|
|
184
|
+
};
|
|
185
|
+
TEST_VALUE = double(10);
|
|
186
|
+
`,
|
|
187
|
+
{
|
|
188
|
+
target: "node",
|
|
189
|
+
rgf: true,
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
expect(output).toContain("new Function");
|
|
194
|
+
|
|
195
|
+
var TEST_VALUE;
|
|
196
|
+
|
|
197
|
+
eval(output);
|
|
198
|
+
expect(TEST_VALUE).toStrictEqual(20);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should work on re-assigned functions", async () => {
|
|
202
|
+
var output = await JsConfuser.obfuscate(
|
|
203
|
+
`
|
|
204
|
+
var fn1 = ()=>{
|
|
205
|
+
return "FN1";
|
|
206
|
+
}
|
|
207
|
+
var fn2 = ()=>{
|
|
208
|
+
fn1 = ()=>{
|
|
209
|
+
return "FN1 - Modified"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
fn2();
|
|
213
|
+
TEST_VALUE = fn1();
|
|
214
|
+
`,
|
|
215
|
+
{
|
|
216
|
+
target: "node",
|
|
217
|
+
rgf: true,
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
expect(output).toContain("new Function");
|
|
222
|
+
|
|
223
|
+
var TEST_VALUE;
|
|
224
|
+
|
|
225
|
+
eval(output);
|
|
226
|
+
expect(TEST_VALUE).toStrictEqual("FN1 - Modified");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should work on re-assigned functions to non-function values", async () => {
|
|
230
|
+
var output = await JsConfuser.obfuscate(
|
|
231
|
+
`
|
|
232
|
+
var fn1 = ()=>{
|
|
233
|
+
return "FN1";
|
|
234
|
+
}
|
|
235
|
+
var fn2 = ()=>{
|
|
236
|
+
fn1 = undefined;
|
|
237
|
+
}
|
|
238
|
+
fn2();
|
|
239
|
+
TEST_VALUE = typeof fn1;
|
|
240
|
+
`,
|
|
241
|
+
{
|
|
242
|
+
target: "node",
|
|
243
|
+
rgf: true,
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
expect(output).toContain("new Function");
|
|
248
|
+
|
|
249
|
+
var TEST_VALUE;
|
|
250
|
+
|
|
251
|
+
eval(output);
|
|
252
|
+
expect(TEST_VALUE).toStrictEqual("undefined");
|
|
253
|
+
});
|
|
157
254
|
});
|
|
158
255
|
|
|
159
256
|
describe("RGF with the 'all' mode", () => {
|