js-confuser 1.5.7 → 1.5.8
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 +14 -0
- package/dist/options.js +4 -4
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +16 -2
- package/dist/transforms/identifier/nameRecycling.js +8 -2
- package/dist/transforms/identifier/renameVariables.js +9 -0
- package/dist/transforms/lock/antiDebug.js +1 -1
- package/dist/transforms/string/stringConcealing.js +77 -40
- package/dist/transforms/transform.js +1 -1
- package/package.json +2 -2
- package/src/options.ts +10 -4
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +16 -1
- package/src/transforms/identifier/nameRecycling.ts +14 -3
- package/src/transforms/identifier/renameVariables.ts +19 -0
- package/src/transforms/lock/antiDebug.ts +1 -1
- package/src/transforms/string/stringConcealing.ts +120 -56
- package/src/transforms/transform.ts +1 -1
- package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +36 -0
- package/test/transforms/identifier/nameRecycling.test.ts +39 -0
- package/test/transforms/identifier/renameVariables.test.ts +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# `1.5.8`
|
|
2
|
+
Several fixes
|
|
3
|
+
|
|
4
|
+
- Fixed [#46](https://github.com/MichaelXF/js-confuser/issues/46)
|
|
5
|
+
- - Updated the validation on `lock` options
|
|
6
|
+
|
|
7
|
+
- Fixed [#68](https://github.com/MichaelXF/js-confuser/issues/68)
|
|
8
|
+
- - Name Recycling fixed to not break certain function declarations
|
|
9
|
+
|
|
10
|
+
- Fixed [#69](https://github.com/MichaelXF/js-confuser/issues/69), [#70](https://github.com/MichaelXF/js-confuser/issues/70) and [#71](https://github.com/MichaelXF/js-confuser/issues/71)
|
|
11
|
+
- - Import statements to be properly handled
|
|
12
|
+
|
|
13
|
+
- Slight improvements to String Concealing
|
|
14
|
+
|
|
1
15
|
# `1.5.7`
|
|
2
16
|
Countermeasures function fixes
|
|
3
17
|
|
package/dist/options.js
CHANGED
|
@@ -40,24 +40,24 @@ function validateOptions(options) {
|
|
|
40
40
|
|
|
41
41
|
if (options.lock) {
|
|
42
42
|
// Validate browser-lock option
|
|
43
|
-
if (typeof options.lock.browserLock !== "undefined") {
|
|
43
|
+
if (options.lock.browserLock && typeof options.lock.browserLock !== "undefined") {
|
|
44
44
|
(0, _assert.ok)(Array.isArray(options.lock.browserLock), "browserLock must be an array");
|
|
45
45
|
(0, _assert.ok)(!options.lock.browserLock.find(browserName => !validBrowsers.has(browserName)), 'Invalid browser name. Allowed: "firefox", "chrome", "iexplorer", "edge", "safari", "opera"');
|
|
46
46
|
} // Validate os-lock option
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
if (typeof options.lock.osLock !== "undefined") {
|
|
49
|
+
if (options.lock.osLock && typeof options.lock.osLock !== "undefined") {
|
|
50
50
|
(0, _assert.ok)(Array.isArray(options.lock.osLock), "osLock must be an array");
|
|
51
51
|
(0, _assert.ok)(!options.lock.osLock.find(osName => !validOses.has(osName)), 'Invalid OS name. Allowed: "windows", "linux", "osx", "ios", "android"');
|
|
52
52
|
} // Validate domain-lock option
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
if (typeof options.lock.domainLock !== "undefined") {
|
|
55
|
+
if (options.lock.domainLock && typeof options.lock.domainLock !== "undefined") {
|
|
56
56
|
(0, _assert.ok)(Array.isArray(options.lock.domainLock), "domainLock must be an array");
|
|
57
57
|
} // Validate context option
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
if (typeof options.lock.context !== "undefined") {
|
|
60
|
+
if (options.lock.context && typeof options.lock.context !== "undefined") {
|
|
61
61
|
(0, _assert.ok)(Array.isArray(options.lock.context), "context must be an array");
|
|
62
62
|
} // Validate start-date option
|
|
63
63
|
|
|
@@ -31,6 +31,8 @@ var _expressionObfuscation = _interopRequireDefault(require("./expressionObfusca
|
|
|
31
31
|
|
|
32
32
|
var _switchCaseObfuscation = _interopRequireDefault(require("./switchCaseObfuscation"));
|
|
33
33
|
|
|
34
|
+
var _stringConcealing = require("../string/stringConcealing");
|
|
35
|
+
|
|
34
36
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
35
37
|
|
|
36
38
|
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; }
|
|
@@ -181,6 +183,14 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
181
183
|
illegalFnNames.forEach(illegal => {
|
|
182
184
|
fnNames.delete(illegal);
|
|
183
185
|
});
|
|
186
|
+
var importDeclarations = [];
|
|
187
|
+
|
|
188
|
+
for (var stmt of body) {
|
|
189
|
+
if (stmt.type === "ImportDeclaration") {
|
|
190
|
+
importDeclarations.push(stmt);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
184
194
|
var fraction = 0.9;
|
|
185
195
|
|
|
186
196
|
if (body.length > 20) {
|
|
@@ -237,7 +247,7 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
237
247
|
body: [...currentBody]
|
|
238
248
|
});
|
|
239
249
|
(0, _traverse.walk)(currentBody, [], (o, p) => {
|
|
240
|
-
if (o.type == "Literal" && typeof o.value == "string" && !o.regex && Math.random() / (Object.keys(stringBank).length / 2 + 1) > 0.5) {
|
|
250
|
+
if (o.type == "Literal" && typeof o.value == "string" && !(0, _stringConcealing.isModuleSource)(o, p) && !o.regex && Math.random() / (Object.keys(stringBank).length / 2 + 1) > 0.5) {
|
|
241
251
|
needsStringBankVar = true;
|
|
242
252
|
|
|
243
253
|
if (!stringBankByLabels[currentLabel]) {
|
|
@@ -260,7 +270,7 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
260
270
|
};
|
|
261
271
|
|
|
262
272
|
body.forEach((stmt, i) => {
|
|
263
|
-
if (functionDeclarations.has(stmt)) {
|
|
273
|
+
if (functionDeclarations.has(stmt) || stmt.type === "ImportDeclaration") {
|
|
264
274
|
return;
|
|
265
275
|
}
|
|
266
276
|
|
|
@@ -708,6 +718,10 @@ class ControlFlowFlattening extends _transform.default {
|
|
|
708
718
|
var discriminant = (0, _template.default)("".concat(stateVars.join("+"))).single().expression;
|
|
709
719
|
body.length = 0;
|
|
710
720
|
|
|
721
|
+
for (var importDeclaration of importDeclarations) {
|
|
722
|
+
body.push(importDeclaration);
|
|
723
|
+
}
|
|
724
|
+
|
|
711
725
|
if (functionDeclarations.size) {
|
|
712
726
|
functionDeclarations.forEach(x => {
|
|
713
727
|
if (!x.id || illegalFnNames.has(x.id.name)) {
|
|
@@ -87,7 +87,6 @@ class NameRecycling extends _transform.default {
|
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
lastReferenceMap.set(o.name, i);
|
|
91
90
|
var comparingContext = info.spec.isDefined ? (0, _insert.getDefiningContext)(o, p) : (0, _insert.getReferencingContexts)(o, p).find(x => (0, _insert.isVarContext)(x));
|
|
92
91
|
|
|
93
92
|
if (comparingContext !== context) {
|
|
@@ -101,7 +100,12 @@ class NameRecycling extends _transform.default {
|
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
if (info.spec.isDefined) {
|
|
104
|
-
|
|
103
|
+
// Function Declarations can be used before they're defined, if so, don't change this
|
|
104
|
+
if (info.isFunctionDeclaration && lastReferenceMap.has(o.name)) {
|
|
105
|
+
illegal.add(o.name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (defined.has(o.name) || (0, _traverse.getBlock)(o, p) !== object || info.isImportSpecifier) {
|
|
105
109
|
illegal.add(o.name);
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -111,6 +115,8 @@ class NameRecycling extends _transform.default {
|
|
|
111
115
|
referencedHere.add(o.name);
|
|
112
116
|
}
|
|
113
117
|
}
|
|
118
|
+
|
|
119
|
+
lastReferenceMap.set(o.name, i);
|
|
114
120
|
};
|
|
115
121
|
}
|
|
116
122
|
}); // console.log(i, definedHere);
|
|
@@ -165,6 +165,15 @@ class RenameVariables extends _transform.default {
|
|
|
165
165
|
if (newName && typeof newName === "string") {
|
|
166
166
|
if (o.$renamed) {
|
|
167
167
|
return;
|
|
168
|
+
} // Strange behavior where the `local` and `imported` objects are the same
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if (info.isImportSpecifier) {
|
|
172
|
+
var importSpecifierIndex = p.findIndex(x => x.type === "ImportSpecifier");
|
|
173
|
+
|
|
174
|
+
if (importSpecifierIndex != -1 && p[importSpecifierIndex].imported === (p[importSpecifierIndex - 1] || o) && p[importSpecifierIndex].imported && p[importSpecifierIndex].imported.type === "Identifier") {
|
|
175
|
+
p[importSpecifierIndex].imported = (0, _insert.clone)(p[importSpecifierIndex - 1] || o);
|
|
176
|
+
}
|
|
168
177
|
} // console.log(o.name, "->", newName);
|
|
169
178
|
|
|
170
179
|
|
|
@@ -43,7 +43,7 @@ class AntiDebug extends _transform.default {
|
|
|
43
43
|
var startTimeName = this.getPlaceholder();
|
|
44
44
|
var endTimeName = this.getPlaceholder();
|
|
45
45
|
var isDevName = this.getPlaceholder();
|
|
46
|
-
var functionDeclaration = (0, _gen.FunctionDeclaration)(fnName, [], [...(0, _template.default)("\n var ".concat(startTimeName, " = new Date();\n debugger;\n var ").concat(endTimeName, " = new Date();\n var ").concat(isDevName, " = ").concat(endTimeName, "-").concat(startTimeName, " > 1000;\n ")).compile(), (0, _gen.IfStatement)((0, _gen.Identifier)(isDevName), this.options.lock.countermeasures ? this.lock.getCounterMeasuresCode() : [(0, _gen.WhileStatement)((0, _gen.Identifier)(isDevName), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(startTimeName), (0, _gen.Identifier)(endTimeName)))])], null)]);
|
|
46
|
+
var functionDeclaration = (0, _gen.FunctionDeclaration)(fnName, [], [...(0, _template.default)("\n var ".concat(startTimeName, " = new Date();\n debugger;\n var ").concat(endTimeName, " = new Date();\n var ").concat(isDevName, " = ").concat(endTimeName, "-").concat(startTimeName, " > 1000;\n ")).compile(), (0, _gen.IfStatement)((0, _gen.Identifier)(isDevName), this.options.lock.countermeasures ? this.lock.getCounterMeasuresCode(tree.body, [tree]) : [(0, _gen.WhileStatement)((0, _gen.Identifier)(isDevName), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(startTimeName), (0, _gen.Identifier)(endTimeName)))])], null)]);
|
|
47
47
|
tree.body.unshift(...DevToolsDetection.compile({
|
|
48
48
|
functionName: fnName
|
|
49
49
|
}));
|
|
@@ -70,17 +70,20 @@ class StringConcealing extends _transform.default {
|
|
|
70
70
|
|
|
71
71
|
_defineProperty(this, "encoding", Object.create(null));
|
|
72
72
|
|
|
73
|
+
_defineProperty(this, "gen", void 0);
|
|
74
|
+
|
|
73
75
|
_defineProperty(this, "hasAllEncodings", void 0);
|
|
74
76
|
|
|
75
77
|
this.set = new Set();
|
|
76
78
|
this.index = Object.create(null);
|
|
77
79
|
this.arrayExpression = (0, _gen.ArrayExpression)([]);
|
|
78
|
-
this.hasAllEncodings = false;
|
|
80
|
+
this.hasAllEncodings = false;
|
|
81
|
+
this.gen = this.getGenerator(); // Pad array with useless strings
|
|
79
82
|
|
|
80
|
-
var dead = (0, _random.getRandomInteger)(
|
|
83
|
+
var dead = (0, _random.getRandomInteger)(5, 15);
|
|
81
84
|
|
|
82
85
|
for (var i = 0; i < dead; i++) {
|
|
83
|
-
var str = (0, _random.getRandomString)((0, _random.getRandomInteger)(
|
|
86
|
+
var str = (0, _random.getRandomString)((0, _random.getRandomInteger)(5, 40));
|
|
84
87
|
var fn = this.transform((0, _gen.Literal)(str), []);
|
|
85
88
|
|
|
86
89
|
if (fn) {
|
|
@@ -104,7 +107,7 @@ class StringConcealing extends _transform.default {
|
|
|
104
107
|
(0, _insert.append)(tree, (0, _template.default)("\n \n function ".concat(getterFn, "(x, y, z, a = ").concat(decodeFn, ", b = ").concat(cacheName, "){\n if ( z ) {\n return y[").concat(cacheName, "[z]] = ").concat(getterFn, "(x, y);\n } else if ( y ) {\n [b, y] = [a(b), x || z]\n }\n \n return y ? x[b[y]] : ").concat(cacheName, "[x] || (z=(b[x], a), ").concat(cacheName, "[x] = z(").concat(this.arrayName, "[x]))\n }\n \n ")).single());
|
|
105
108
|
});
|
|
106
109
|
var flowIntegrity = this.getPlaceholder();
|
|
107
|
-
(0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(cacheName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)(flowIntegrity, (0, _gen.Literal)(0)), (0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("a", this.arrayExpression)), (0, _template.default)("return (".concat(flowIntegrity, " ? a
|
|
110
|
+
(0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(cacheName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)(flowIntegrity, (0, _gen.Literal)(0)), (0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("a", this.arrayExpression)), (0, _template.default)("return (".concat(flowIntegrity, " ? a[\"pop\"]() : ").concat(flowIntegrity, "++, a)")).single()]), []))]));
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
match(object, parents) {
|
|
@@ -117,7 +120,7 @@ class StringConcealing extends _transform.default {
|
|
|
117
120
|
transform(object, parents) {
|
|
118
121
|
return () => {
|
|
119
122
|
// Empty strings are discarded
|
|
120
|
-
if (!object.value || this.ignore.has(object.value)) {
|
|
123
|
+
if (!object.value || this.ignore.has(object.value) || object.value.length == 0) {
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -143,53 +146,87 @@ class StringConcealing extends _transform.default {
|
|
|
143
146
|
|
|
144
147
|
if (encoder.decode(encoded) != object.value) {
|
|
145
148
|
this.ignore.add(object.value);
|
|
146
|
-
this.warn(object.value.slice(0, 100));
|
|
149
|
+
this.warn(type, object.value.slice(0, 100));
|
|
147
150
|
return;
|
|
148
|
-
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var index = -1;
|
|
149
154
|
|
|
155
|
+
if (!this.set.has(object.value)) {
|
|
156
|
+
this.arrayExpression.elements.push((0, _gen.Literal)(encoded));
|
|
157
|
+
index = this.arrayExpression.elements.length - 1;
|
|
158
|
+
this.index[object.value] = [index, fnName];
|
|
159
|
+
this.set.add(object.value);
|
|
160
|
+
} else {
|
|
161
|
+
[index, fnName] = this.index[object.value];
|
|
162
|
+
(0, _assert.ok)(typeof index === "number");
|
|
163
|
+
}
|
|
150
164
|
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
(0, _assert.ok)(index != -1, "index == -1");
|
|
166
|
+
var callExpr = (0, _gen.CallExpression)((0, _gen.Identifier)(fnName), [(0, _gen.Literal)(index)]); // use `.apply` to fool automated de-obfuscators
|
|
153
167
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
[index, fnName] = this.index[object.value];
|
|
161
|
-
(0, _assert.ok)(typeof index === "number");
|
|
162
|
-
}
|
|
168
|
+
if (Math.random() > 0.5) {
|
|
169
|
+
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("apply"), false), [(0, _gen.ThisExpression)(), (0, _gen.ArrayExpression)([(0, _gen.Literal)(index)])]);
|
|
170
|
+
} // use `.call`
|
|
171
|
+
else if (Math.random() > 0.5) {
|
|
172
|
+
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)(), (0, _gen.Literal)(index)]);
|
|
173
|
+
}
|
|
163
174
|
|
|
164
|
-
|
|
165
|
-
var callExpr = (0, _gen.CallExpression)((0, _gen.Identifier)(fnName), [(0, _gen.Literal)(index)]); // use `.apply` to fool automated de-obfuscators
|
|
175
|
+
var referenceType = "call";
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
else if (Math.random() > 0.5) {
|
|
171
|
-
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)(), (0, _gen.Literal)(index)]);
|
|
172
|
-
}
|
|
177
|
+
if (parents.length && Math.random() < 0.5 / this.variablesMade) {
|
|
178
|
+
referenceType = "constantReference";
|
|
179
|
+
}
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
var newExpr = callExpr;
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
if (referenceType === "constantReference") {
|
|
184
|
+
// Define the string earlier, reference the name here
|
|
185
|
+
this.variablesMade++;
|
|
186
|
+
var constantReferenceType = (0, _random.choice)(["variable", "array", "object"]);
|
|
187
|
+
var place = (0, _random.choice)(parents.filter(node => (0, _traverse.isBlock)(node)));
|
|
180
188
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
if (!place) {
|
|
190
|
+
this.error(new Error("No lexical block to insert code"));
|
|
191
|
+
}
|
|
184
192
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
switch (constantReferenceType) {
|
|
194
|
+
case "variable":
|
|
195
|
+
var name = this.getPlaceholder();
|
|
196
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(name, callExpr)));
|
|
197
|
+
newExpr = (0, _gen.Identifier)(name);
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
case "array":
|
|
201
|
+
if (!place.$stringConcealingArray) {
|
|
202
|
+
place.$stringConcealingArray = (0, _gen.ArrayExpression)([]);
|
|
203
|
+
place.$stringConcealingArrayName = this.getPlaceholder();
|
|
204
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(place.$stringConcealingArrayName, place.$stringConcealingArray)));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var arrayIndex = place.$stringConcealingArray.elements.length;
|
|
208
|
+
place.$stringConcealingArray.elements.push(callExpr);
|
|
209
|
+
var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(place.$stringConcealingArrayName), (0, _gen.Literal)(arrayIndex), true);
|
|
210
|
+
newExpr = memberExpression;
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case "object":
|
|
214
|
+
if (!place.$stringConcealingObject) {
|
|
215
|
+
place.$stringConcealingObject = (0, _gen.ObjectExpression)([]);
|
|
216
|
+
place.$stringConcealingObjectName = this.getPlaceholder();
|
|
217
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(place.$stringConcealingObjectName, place.$stringConcealingObject)));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
var propName = this.gen.generate();
|
|
221
|
+
var property = (0, _gen.Property)((0, _gen.Literal)(propName), callExpr, true);
|
|
222
|
+
place.$stringConcealingObject.properties.push(property);
|
|
223
|
+
var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(place.$stringConcealingObjectName), (0, _gen.Literal)(propName), true);
|
|
224
|
+
newExpr = memberExpression;
|
|
225
|
+
break;
|
|
191
226
|
}
|
|
192
227
|
}
|
|
228
|
+
|
|
229
|
+
this.replaceIdentifierOrLiteral(object, newExpr, parents);
|
|
193
230
|
};
|
|
194
231
|
}
|
|
195
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-confuser",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.8",
|
|
4
4
|
"description": "JavaScript Obfuscation Tool.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"author": "MichaelXF",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"acorn": "^8.
|
|
25
|
+
"acorn": "^8.8.2",
|
|
26
26
|
"escodegen": "^2.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
package/src/options.ts
CHANGED
|
@@ -770,7 +770,10 @@ export function validateOptions(options: ObfuscateOptions) {
|
|
|
770
770
|
|
|
771
771
|
if (options.lock) {
|
|
772
772
|
// Validate browser-lock option
|
|
773
|
-
if (
|
|
773
|
+
if (
|
|
774
|
+
options.lock.browserLock &&
|
|
775
|
+
typeof options.lock.browserLock !== "undefined"
|
|
776
|
+
) {
|
|
774
777
|
ok(
|
|
775
778
|
Array.isArray(options.lock.browserLock),
|
|
776
779
|
"browserLock must be an array"
|
|
@@ -783,7 +786,7 @@ export function validateOptions(options: ObfuscateOptions) {
|
|
|
783
786
|
);
|
|
784
787
|
}
|
|
785
788
|
// Validate os-lock option
|
|
786
|
-
if (typeof options.lock.osLock !== "undefined") {
|
|
789
|
+
if (options.lock.osLock && typeof options.lock.osLock !== "undefined") {
|
|
787
790
|
ok(Array.isArray(options.lock.osLock), "osLock must be an array");
|
|
788
791
|
ok(
|
|
789
792
|
!options.lock.osLock.find((osName) => !validOses.has(osName)),
|
|
@@ -791,12 +794,15 @@ export function validateOptions(options: ObfuscateOptions) {
|
|
|
791
794
|
);
|
|
792
795
|
}
|
|
793
796
|
// Validate domain-lock option
|
|
794
|
-
if (
|
|
797
|
+
if (
|
|
798
|
+
options.lock.domainLock &&
|
|
799
|
+
typeof options.lock.domainLock !== "undefined"
|
|
800
|
+
) {
|
|
795
801
|
ok(Array.isArray(options.lock.domainLock), "domainLock must be an array");
|
|
796
802
|
}
|
|
797
803
|
|
|
798
804
|
// Validate context option
|
|
799
|
-
if (typeof options.lock.context !== "undefined") {
|
|
805
|
+
if (options.lock.context && typeof options.lock.context !== "undefined") {
|
|
800
806
|
ok(Array.isArray(options.lock.context), "context must be an array");
|
|
801
807
|
}
|
|
802
808
|
|
|
@@ -47,6 +47,7 @@ import ChoiceFlowObfuscation from "./choiceFlowObfuscation";
|
|
|
47
47
|
import ControlFlowObfuscation from "./controlFlowObfuscation";
|
|
48
48
|
import ExpressionObfuscation from "./expressionObfuscation";
|
|
49
49
|
import SwitchCaseObfuscation from "./switchCaseObfuscation";
|
|
50
|
+
import { isModuleSource } from "../string/stringConcealing";
|
|
50
51
|
|
|
51
52
|
var flattenStructures = new Set([
|
|
52
53
|
"IfStatement",
|
|
@@ -226,6 +227,13 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
226
227
|
fnNames.delete(illegal);
|
|
227
228
|
});
|
|
228
229
|
|
|
230
|
+
var importDeclarations = [];
|
|
231
|
+
for (var stmt of body) {
|
|
232
|
+
if (stmt.type === "ImportDeclaration") {
|
|
233
|
+
importDeclarations.push(stmt);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
229
237
|
var fraction = 0.9;
|
|
230
238
|
if (body.length > 20) {
|
|
231
239
|
fraction /= Math.max(1.2, body.length - 18);
|
|
@@ -285,6 +293,7 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
285
293
|
if (
|
|
286
294
|
o.type == "Literal" &&
|
|
287
295
|
typeof o.value == "string" &&
|
|
296
|
+
!isModuleSource(o, p) &&
|
|
288
297
|
!o.regex &&
|
|
289
298
|
Math.random() / (Object.keys(stringBank).length / 2 + 1) > 0.5
|
|
290
299
|
) {
|
|
@@ -318,7 +327,10 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
318
327
|
};
|
|
319
328
|
|
|
320
329
|
body.forEach((stmt, i) => {
|
|
321
|
-
if (
|
|
330
|
+
if (
|
|
331
|
+
functionDeclarations.has(stmt) ||
|
|
332
|
+
stmt.type === "ImportDeclaration"
|
|
333
|
+
) {
|
|
322
334
|
return;
|
|
323
335
|
}
|
|
324
336
|
|
|
@@ -1045,6 +1057,9 @@ export default class ControlFlowFlattening extends Transform {
|
|
|
1045
1057
|
var discriminant = Template(`${stateVars.join("+")}`).single().expression;
|
|
1046
1058
|
|
|
1047
1059
|
body.length = 0;
|
|
1060
|
+
for (var importDeclaration of importDeclarations) {
|
|
1061
|
+
body.push(importDeclaration);
|
|
1062
|
+
}
|
|
1048
1063
|
|
|
1049
1064
|
if (functionDeclarations.size) {
|
|
1050
1065
|
functionDeclarations.forEach((x) => {
|
|
@@ -97,8 +97,6 @@ export default class NameRecycling extends Transform {
|
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
lastReferenceMap.set(o.name, i);
|
|
101
|
-
|
|
102
100
|
var comparingContext = info.spec.isDefined
|
|
103
101
|
? getDefiningContext(o, p)
|
|
104
102
|
: getReferencingContexts(o, p).find((x) => isVarContext(x));
|
|
@@ -114,7 +112,18 @@ export default class NameRecycling extends Transform {
|
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
if (info.spec.isDefined) {
|
|
117
|
-
|
|
115
|
+
// Function Declarations can be used before they're defined, if so, don't change this
|
|
116
|
+
if (
|
|
117
|
+
info.isFunctionDeclaration &&
|
|
118
|
+
lastReferenceMap.has(o.name)
|
|
119
|
+
) {
|
|
120
|
+
illegal.add(o.name);
|
|
121
|
+
}
|
|
122
|
+
if (
|
|
123
|
+
defined.has(o.name) ||
|
|
124
|
+
getBlock(o, p) !== object ||
|
|
125
|
+
info.isImportSpecifier
|
|
126
|
+
) {
|
|
118
127
|
illegal.add(o.name);
|
|
119
128
|
}
|
|
120
129
|
defined.add(o.name);
|
|
@@ -123,6 +132,8 @@ export default class NameRecycling extends Transform {
|
|
|
123
132
|
referencedHere.add(o.name);
|
|
124
133
|
}
|
|
125
134
|
}
|
|
135
|
+
|
|
136
|
+
lastReferenceMap.set(o.name, i);
|
|
126
137
|
};
|
|
127
138
|
}
|
|
128
139
|
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
isContext,
|
|
11
11
|
isLexContext,
|
|
12
12
|
getDefiningContext,
|
|
13
|
+
clone,
|
|
13
14
|
} from "../../util/insert";
|
|
14
15
|
import { isValidIdentifier } from "../../util/compare";
|
|
15
16
|
import Transform from "../transform";
|
|
@@ -185,6 +186,24 @@ export default class RenameVariables extends Transform {
|
|
|
185
186
|
return;
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
// Strange behavior where the `local` and `imported` objects are the same
|
|
190
|
+
if (info.isImportSpecifier) {
|
|
191
|
+
var importSpecifierIndex = p.findIndex(
|
|
192
|
+
(x) => x.type === "ImportSpecifier"
|
|
193
|
+
);
|
|
194
|
+
if (
|
|
195
|
+
importSpecifierIndex != -1 &&
|
|
196
|
+
p[importSpecifierIndex].imported ===
|
|
197
|
+
(p[importSpecifierIndex - 1] || o) &&
|
|
198
|
+
p[importSpecifierIndex].imported &&
|
|
199
|
+
p[importSpecifierIndex].imported.type === "Identifier"
|
|
200
|
+
) {
|
|
201
|
+
p[importSpecifierIndex].imported = clone(
|
|
202
|
+
p[importSpecifierIndex - 1] || o
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
// console.log(o.name, "->", newName);
|
|
189
208
|
o.name = newName;
|
|
190
209
|
o.$renamed = true;
|
|
@@ -62,7 +62,7 @@ export default class AntiDebug extends Transform {
|
|
|
62
62
|
IfStatement(
|
|
63
63
|
Identifier(isDevName),
|
|
64
64
|
this.options.lock.countermeasures
|
|
65
|
-
? this.lock.getCounterMeasuresCode()
|
|
65
|
+
? this.lock.getCounterMeasuresCode(tree.body, [tree])
|
|
66
66
|
: [
|
|
67
67
|
WhileStatement(Identifier(isDevName), [
|
|
68
68
|
ExpressionStatement(
|
|
@@ -6,22 +6,18 @@ import { isDirective } from "../../util/compare";
|
|
|
6
6
|
import {
|
|
7
7
|
ArrayExpression,
|
|
8
8
|
CallExpression,
|
|
9
|
-
ConditionalExpression,
|
|
10
|
-
FunctionDeclaration,
|
|
11
9
|
FunctionExpression,
|
|
12
10
|
Identifier,
|
|
13
|
-
IfStatement,
|
|
14
11
|
Literal,
|
|
15
12
|
MemberExpression,
|
|
16
13
|
Node,
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
ObjectExpression,
|
|
15
|
+
Property,
|
|
19
16
|
ThisExpression,
|
|
20
|
-
UpdateExpression,
|
|
21
17
|
VariableDeclaration,
|
|
22
18
|
VariableDeclarator,
|
|
23
19
|
} from "../../util/gen";
|
|
24
|
-
import { append,
|
|
20
|
+
import { append, prepend } from "../../util/insert";
|
|
25
21
|
import { choice, getRandomInteger, getRandomString } from "../../util/random";
|
|
26
22
|
import Transform from "../transform";
|
|
27
23
|
import Encoding from "./encoding";
|
|
@@ -65,6 +61,7 @@ export default class StringConcealing extends Transform {
|
|
|
65
61
|
ignore = new Set<string>();
|
|
66
62
|
variablesMade = 1;
|
|
67
63
|
encoding: { [type: string]: string } = Object.create(null);
|
|
64
|
+
gen: ReturnType<Transform["getGenerator"]>;
|
|
68
65
|
|
|
69
66
|
hasAllEncodings: boolean;
|
|
70
67
|
|
|
@@ -75,11 +72,12 @@ export default class StringConcealing extends Transform {
|
|
|
75
72
|
this.index = Object.create(null);
|
|
76
73
|
this.arrayExpression = ArrayExpression([]);
|
|
77
74
|
this.hasAllEncodings = false;
|
|
75
|
+
this.gen = this.getGenerator();
|
|
78
76
|
|
|
79
77
|
// Pad array with useless strings
|
|
80
|
-
var dead = getRandomInteger(
|
|
78
|
+
var dead = getRandomInteger(5, 15);
|
|
81
79
|
for (var i = 0; i < dead; i++) {
|
|
82
|
-
var str = getRandomString(getRandomInteger(
|
|
80
|
+
var str = getRandomString(getRandomInteger(5, 40));
|
|
83
81
|
var fn = this.transform(Literal(str), []);
|
|
84
82
|
if (fn) {
|
|
85
83
|
fn();
|
|
@@ -134,7 +132,7 @@ export default class StringConcealing extends Transform {
|
|
|
134
132
|
VariableDeclarator("a", this.arrayExpression)
|
|
135
133
|
),
|
|
136
134
|
Template(
|
|
137
|
-
`return (${flowIntegrity} ? a
|
|
135
|
+
`return (${flowIntegrity} ? a["pop"]() : ${flowIntegrity}++, a)`
|
|
138
136
|
).single(),
|
|
139
137
|
]
|
|
140
138
|
),
|
|
@@ -159,7 +157,11 @@ export default class StringConcealing extends Transform {
|
|
|
159
157
|
transform(object: Node, parents: Node[]) {
|
|
160
158
|
return () => {
|
|
161
159
|
// Empty strings are discarded
|
|
162
|
-
if (
|
|
160
|
+
if (
|
|
161
|
+
!object.value ||
|
|
162
|
+
this.ignore.has(object.value) ||
|
|
163
|
+
object.value.length == 0
|
|
164
|
+
) {
|
|
163
165
|
return;
|
|
164
166
|
}
|
|
165
167
|
|
|
@@ -188,69 +190,131 @@ export default class StringConcealing extends Transform {
|
|
|
188
190
|
var encoded = encoder.encode(object.value);
|
|
189
191
|
if (encoder.decode(encoded) != object.value) {
|
|
190
192
|
this.ignore.add(object.value);
|
|
191
|
-
this.warn(object.value.slice(0, 100));
|
|
193
|
+
this.warn(type, object.value.slice(0, 100));
|
|
192
194
|
return;
|
|
193
195
|
}
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
index = this.arrayExpression.elements.length - 1;
|
|
201
|
-
this.index[object.value] = [index, fnName];
|
|
197
|
+
var index = -1;
|
|
198
|
+
if (!this.set.has(object.value)) {
|
|
199
|
+
this.arrayExpression.elements.push(Literal(encoded));
|
|
200
|
+
index = this.arrayExpression.elements.length - 1;
|
|
201
|
+
this.index[object.value] = [index, fnName];
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
this.set.add(object.value);
|
|
204
|
+
} else {
|
|
205
|
+
[index, fnName] = this.index[object.value];
|
|
206
|
+
ok(typeof index === "number");
|
|
207
|
+
}
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
ok(index != -1, "index == -1");
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
var callExpr = CallExpression(Identifier(fnName), [Literal(index)]);
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
213
|
+
// use `.apply` to fool automated de-obfuscators
|
|
214
|
+
if (Math.random() > 0.5) {
|
|
215
|
+
callExpr = CallExpression(
|
|
216
|
+
MemberExpression(Identifier(fnName), Identifier("apply"), false),
|
|
217
|
+
[ThisExpression(), ArrayExpression([Literal(index)])]
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// use `.call`
|
|
222
|
+
else if (Math.random() > 0.5) {
|
|
223
|
+
callExpr = CallExpression(
|
|
224
|
+
MemberExpression(Identifier(fnName), Identifier("call"), false),
|
|
225
|
+
[ThisExpression(), Literal(index)]
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
var referenceType = "call";
|
|
230
|
+
if (parents.length && Math.random() < 0.5 / this.variablesMade) {
|
|
231
|
+
referenceType = "constantReference";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
var newExpr: Node = callExpr;
|
|
235
|
+
|
|
236
|
+
if (referenceType === "constantReference") {
|
|
237
|
+
// Define the string earlier, reference the name here
|
|
238
|
+
this.variablesMade++;
|
|
220
239
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
);
|
|
240
|
+
var constantReferenceType = choice(["variable", "array", "object"]);
|
|
241
|
+
|
|
242
|
+
var place = choice(parents.filter((node) => isBlock(node)));
|
|
243
|
+
if (!place) {
|
|
244
|
+
this.error(new Error("No lexical block to insert code"));
|
|
227
245
|
}
|
|
228
246
|
|
|
229
|
-
|
|
230
|
-
|
|
247
|
+
switch (constantReferenceType) {
|
|
248
|
+
case "variable":
|
|
249
|
+
var name = this.getPlaceholder();
|
|
250
|
+
|
|
251
|
+
prepend(
|
|
252
|
+
place,
|
|
253
|
+
VariableDeclaration(VariableDeclarator(name, callExpr))
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
newExpr = Identifier(name);
|
|
257
|
+
break;
|
|
258
|
+
case "array":
|
|
259
|
+
if (!place.$stringConcealingArray) {
|
|
260
|
+
place.$stringConcealingArray = ArrayExpression([]);
|
|
261
|
+
place.$stringConcealingArrayName = this.getPlaceholder();
|
|
231
262
|
|
|
232
|
-
|
|
233
|
-
|
|
263
|
+
prepend(
|
|
264
|
+
place,
|
|
265
|
+
VariableDeclaration(
|
|
266
|
+
VariableDeclarator(
|
|
267
|
+
place.$stringConcealingArrayName,
|
|
268
|
+
place.$stringConcealingArray
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
}
|
|
234
273
|
|
|
235
|
-
|
|
274
|
+
var arrayIndex = place.$stringConcealingArray.elements.length;
|
|
236
275
|
|
|
237
|
-
|
|
238
|
-
if (!place) {
|
|
239
|
-
this.error(Error("No lexical block to insert code"));
|
|
240
|
-
}
|
|
276
|
+
place.$stringConcealingArray.elements.push(callExpr);
|
|
241
277
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
278
|
+
var memberExpression = MemberExpression(
|
|
279
|
+
Identifier(place.$stringConcealingArrayName),
|
|
280
|
+
Literal(arrayIndex),
|
|
281
|
+
true
|
|
282
|
+
);
|
|
245
283
|
|
|
246
|
-
|
|
284
|
+
newExpr = memberExpression;
|
|
285
|
+
break;
|
|
286
|
+
case "object":
|
|
287
|
+
if (!place.$stringConcealingObject) {
|
|
288
|
+
place.$stringConcealingObject = ObjectExpression([]);
|
|
289
|
+
place.$stringConcealingObjectName = this.getPlaceholder();
|
|
247
290
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
291
|
+
prepend(
|
|
292
|
+
place,
|
|
293
|
+
VariableDeclaration(
|
|
294
|
+
VariableDeclarator(
|
|
295
|
+
place.$stringConcealingObjectName,
|
|
296
|
+
place.$stringConcealingObject
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
var propName = this.gen.generate();
|
|
303
|
+
var property = Property(Literal(propName), callExpr, true);
|
|
304
|
+
place.$stringConcealingObject.properties.push(property);
|
|
305
|
+
|
|
306
|
+
var memberExpression = MemberExpression(
|
|
307
|
+
Identifier(place.$stringConcealingObjectName),
|
|
308
|
+
Literal(propName),
|
|
309
|
+
true
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
newExpr = memberExpression;
|
|
313
|
+
break;
|
|
252
314
|
}
|
|
253
315
|
}
|
|
316
|
+
|
|
317
|
+
this.replaceIdentifierOrLiteral(object, newExpr, parents);
|
|
254
318
|
};
|
|
255
319
|
}
|
|
256
320
|
}
|
|
@@ -672,3 +672,39 @@ test("Variant #20: Don't apply when functions are redefined", async () => {
|
|
|
672
672
|
eval(output);
|
|
673
673
|
expect(TEST_ARRAY).toStrictEqual([0, 0, 0]);
|
|
674
674
|
});
|
|
675
|
+
|
|
676
|
+
// https://github.com/MichaelXF/js-confuser/issues/70
|
|
677
|
+
test("Variant #21: Don't move Import Declarations", async () => {
|
|
678
|
+
var output = await JsConfuser(
|
|
679
|
+
`
|
|
680
|
+
import {createHash} from "crypto";
|
|
681
|
+
var inputString = "Hash this string";
|
|
682
|
+
var hashed = createHash("sha256").update(inputString).digest("hex");
|
|
683
|
+
TEST_OUTPUT = hashed;
|
|
684
|
+
`,
|
|
685
|
+
{
|
|
686
|
+
target: "node",
|
|
687
|
+
controlFlowFlattening: true,
|
|
688
|
+
}
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
// Ensure Control Flow FLattening was applied
|
|
692
|
+
expect(output).toContain("switch");
|
|
693
|
+
|
|
694
|
+
// Ensure the import declaration wasn't moved
|
|
695
|
+
expect(output.startsWith("import")).toStrictEqual(true);
|
|
696
|
+
|
|
697
|
+
// Convert to runnable code
|
|
698
|
+
output = output.replace(
|
|
699
|
+
`import{createHash}from'crypto';`,
|
|
700
|
+
"const {createHash}=require('crypto');"
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
var TEST_OUTPUT = "";
|
|
704
|
+
|
|
705
|
+
eval(output);
|
|
706
|
+
|
|
707
|
+
expect(TEST_OUTPUT).toStrictEqual(
|
|
708
|
+
"1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
|
|
709
|
+
);
|
|
710
|
+
});
|
|
@@ -164,3 +164,42 @@ it("should convert variable declarations in for loop initializers properly", asy
|
|
|
164
164
|
expect(TEST_VAR_1).toStrictEqual("Hello World");
|
|
165
165
|
expect(TEST_VAR_2).toStrictEqual("Number: 0");
|
|
166
166
|
});
|
|
167
|
+
|
|
168
|
+
// https://github.com/MichaelXF/js-confuser/issues/68
|
|
169
|
+
it("should work on Function Declarations that are defined later in the code", async () => {
|
|
170
|
+
var output = await JsConfuser(
|
|
171
|
+
`
|
|
172
|
+
var result = MyFunction();
|
|
173
|
+
TEST_VAR = result;
|
|
174
|
+
|
|
175
|
+
function MyFunction(b) {
|
|
176
|
+
return "Hello World";
|
|
177
|
+
}
|
|
178
|
+
`,
|
|
179
|
+
{ target: "node", nameRecycling: true }
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
var TEST_VAR;
|
|
183
|
+
eval(output);
|
|
184
|
+
|
|
185
|
+
expect(TEST_VAR).toStrictEqual("Hello World");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// https://github.com/MichaelXF/js-confuser/issues/71
|
|
189
|
+
it("should work on Import Declarations", async () => {
|
|
190
|
+
var output = await JsConfuser(
|
|
191
|
+
`
|
|
192
|
+
import crypto from 'node:crypto'
|
|
193
|
+
|
|
194
|
+
var x = 1;
|
|
195
|
+
|
|
196
|
+
console.log(x);
|
|
197
|
+
`,
|
|
198
|
+
{
|
|
199
|
+
target: "node",
|
|
200
|
+
nameRecycling: true,
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(output).not.toContain("crypto=");
|
|
205
|
+
});
|
|
@@ -436,3 +436,41 @@ test("Variant #18: Catch parameter and lexical variable clash", async () => {
|
|
|
436
436
|
|
|
437
437
|
eval(output);
|
|
438
438
|
});
|
|
439
|
+
|
|
440
|
+
// https://github.com/MichaelXF/js-confuser/issues/69
|
|
441
|
+
test("Variant #19: Don't break Import Declarations", async () => {
|
|
442
|
+
var output = await JsConfuser(
|
|
443
|
+
`
|
|
444
|
+
import { createHash } from 'node:crypto'
|
|
445
|
+
|
|
446
|
+
function sha256(content) {
|
|
447
|
+
return createHash('sha256').update(content).digest('hex')
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
TEST_OUTPUT = sha256("Hash this string");
|
|
451
|
+
`,
|
|
452
|
+
{
|
|
453
|
+
target: "node",
|
|
454
|
+
renameVariables: true,
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// Ensure the createHash got renamed
|
|
459
|
+
expect(output).toContain("createHash as ");
|
|
460
|
+
|
|
461
|
+
// Convert to runnable code
|
|
462
|
+
// This smartly changes the `import` statement to a require call, keeping the new variable name intact
|
|
463
|
+
var newVarName = output.split("createHash as ")[1].split("}")[0];
|
|
464
|
+
output = output
|
|
465
|
+
.split(";")
|
|
466
|
+
.filter((s) => !s.startsWith("import"))
|
|
467
|
+
.join(";");
|
|
468
|
+
output = `var {createHash: ${newVarName}}=require('crypto');` + output;
|
|
469
|
+
|
|
470
|
+
var TEST_OUTPUT;
|
|
471
|
+
eval(output);
|
|
472
|
+
|
|
473
|
+
expect(TEST_OUTPUT).toStrictEqual(
|
|
474
|
+
"1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
|
|
475
|
+
);
|
|
476
|
+
});
|