js-confuser 2.0.0-alpha.4 → 2.0.0-alpha.5

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/dist/constants.js CHANGED
@@ -3,9 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.variableFunctionName = exports.reservedObjectPrototype = exports.reservedNodeModuleIdentifiers = exports.reservedKeywords = exports.reservedIdentifiers = exports.predictableFunctionTag = exports.placeholderVariablePrefix = exports.noRenameVariablePrefix = exports.WITH_STATEMENT = exports.UNSAFE = exports.SKIP = exports.PREDICTABLE = exports.NO_RENAME = exports.NO_REMOVE = exports.MULTI_TRANSFORM = exports.GEN_NODE = exports.FN_LENGTH = void 0;
7
- var predictableFunctionTag = exports.predictableFunctionTag = "__JS_PREDICT__";
8
-
6
+ exports.variableFunctionName = exports.reservedObjectPrototype = exports.reservedNodeModuleIdentifiers = exports.reservedKeywords = exports.reservedIdentifiers = exports.placeholderVariablePrefix = exports.noRenameVariablePrefix = exports.WITH_STATEMENT = exports.UNSAFE = exports.SKIP = exports.PREDICTABLE = exports.NO_RENAME = exports.NO_REMOVE = exports.MULTI_TRANSFORM = exports.GEN_NODE = exports.FN_LENGTH = void 0;
9
7
  /**
10
8
  * A function is 'unsafe' if it requires 'eval', 'arguments' or 'this'
11
9
  *
@@ -89,6 +87,6 @@ var reservedObjectPrototype = exports.reservedObjectPrototype = new Set(["toStri
89
87
  /**
90
88
  * For Zero Width generator - Mangled variable names
91
89
  */
92
- var reservedKeywords = exports.reservedKeywords = ["if", "in", "for", "let", "new", "try", "var", "case", "else", "null", "break", "catch", "class", "const", "super", "throw", "while", "yield", "delete", "export", "import", "public", "return", "switch", "default", "finally", "private", "continue", "debugger", "function", "arguments", "protected", "instanceof", "await", "async",
90
+ var reservedKeywords = exports.reservedKeywords = ["if", "in", "do", "for", "let", "new", "try", "var", "case", "else", "null", "with", "break", "catch", "class", "const", "super", "throw", "while", "yield", "delete", "export", "import", "public", "return", "switch", "default", "finally", "private", "continue", "debugger", "function", "arguments", "protected", "instanceof", "await", "async",
93
91
  // new key words and other fun stuff :P
94
92
  "NaN", "undefined", "true", "false", "typeof", "this", "static", "void", "of"];
package/dist/index.js CHANGED
@@ -119,8 +119,13 @@ function _obfuscateWithProfiler() {
119
119
  changeData: {}
120
120
  };
121
121
  transformMap[log.currentTransform] = entry;
122
- currentTransformTime = nowTime;
122
+
123
+ // (JS-Confuser.com can run performance benchmark tests here)
123
124
  (_profiler$callback = _profiler.callback) === null || _profiler$callback === void 0 || _profiler$callback.call(_profiler, log, entry, ast);
125
+
126
+ // The profiler.callback() function may take a long time to execute,
127
+ // so we need to update the currentTransformTime here for accurate profiling.
128
+ currentTransformTime = performance.now();
124
129
  }
125
130
  });
126
131
  obfuscator.plugins.forEach(function (_ref) {
@@ -154,7 +154,6 @@ var _default = exports["default"] = function _default(_ref) {
154
154
  if (functionPath.node.async || functionPath.node.generator) return;
155
155
  }
156
156
  programOrFunctionPath.scope.crawl();
157
- var blockFnParent = (0, _astUtils.getParentFunctionOrProgram)(blockPath);
158
157
  var hasIllegalNode = false;
159
158
  var bindingNames = new Set();
160
159
  blockPath.traverse({
@@ -204,6 +203,7 @@ var _default = exports["default"] = function _default(_ref) {
204
203
  return withIdentifier("state_".concat(i));
205
204
  });
206
205
  var argVar = withIdentifier("_arg");
206
+ var usedArgVar = false;
207
207
  var _didReturnVar = withIdentifier("return");
208
208
  var basicBlocks = new Map();
209
209
 
@@ -220,11 +220,6 @@ var _default = exports["default"] = function _default(_ref) {
220
220
  var withProperty = isDebug ? "with" : scopeNameGen.generate(false);
221
221
  var withMemberExpression = new _template["default"]("".concat(scopeVar.name, "[\"").concat(withProperty, "\"]")).expression();
222
222
  withMemberExpression.object[_constants.NO_RENAME] = cffIndex;
223
-
224
- // Create 'resetWith' function - Safely resets the 'with' object to none
225
- var resetWithProperty = isDebug ? "resetWith" : scopeNameGen.generate(false);
226
- var resetWithMemberExpression = new _template["default"]("".concat(scopeVar.name, "[\"").concat(resetWithProperty, "\"]")).expression();
227
- resetWithMemberExpression.object[_constants.NO_RENAME] = cffIndex;
228
223
  var ScopeManager = /*#__PURE__*/function () {
229
224
  function ScopeManager(scope, initializingBasicBlock) {
230
225
  _classCallCheck(this, ScopeManager);
@@ -234,14 +229,17 @@ var _default = exports["default"] = function _default(_ref) {
234
229
  _defineProperty(this, "nameGen", addWithStatement ? me.obfuscator.nameGen : new _NameGen.NameGen(me.options.identifierGenerator));
235
230
  this.scope = scope;
236
231
  this.initializingBasicBlock = initializingBasicBlock;
237
- this.propertyName = isDebug ? "_" + scopeCounter++ : scopeNameGen.generate();
232
+ this.propertyName = isDebug ? "_" + cffIndex + "_" + scopeCounter++ : scopeNameGen.generate();
238
233
  }
239
234
  return _createClass(ScopeManager, [{
240
235
  key: "findBestWithDiscriminant",
241
236
  value: function findBestWithDiscriminant(basicBlock) {
242
237
  var _this$parent;
238
+ // This initializing block is forbidden to have a with discriminant
239
+ // (As no previous code is able to prepare the with discriminant)
243
240
  if (basicBlock !== this.initializingBasicBlock) {
244
- if (this.nameMap.size > 0) return this;
241
+ // If no variables were defined in this scope, don't use it
242
+ if (Object.keys(this.scope.bindings).length > 0) return this;
245
243
  }
246
244
  return (_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.findBestWithDiscriminant(basicBlock);
247
245
  }
@@ -290,7 +288,8 @@ var _default = exports["default"] = function _default(_ref) {
290
288
  }, {
291
289
  key: "getMemberExpression",
292
290
  value: function getMemberExpression(name) {
293
- var memberExpression = t.memberExpression(this.getScopeObject(), t.stringLiteral(name), true);
291
+ var object = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getScopeObject();
292
+ var memberExpression = t.memberExpression(object, t.stringLiteral(name), true);
294
293
  return memberExpression;
295
294
  }
296
295
  }, {
@@ -316,13 +315,6 @@ var _default = exports["default"] = function _default(_ref) {
316
315
  for (var key in propertyMap) {
317
316
  properties.push(t.objectProperty(t.stringLiteral(key), propertyMap[key], true));
318
317
  }
319
- if (this === mainScope) {
320
- // Reset With logic
321
- properties.push(t.objectProperty(t.stringLiteral(resetWithProperty), new _template["default"]("\n (function(newStateValues, alwaysUndefined){\n {withMemberExpression} = alwaysUndefined;\n {arrayPattern} = newStateValues\n })\n ").expression({
322
- withMemberExpression: (0, _node.deepClone)(withMemberExpression),
323
- arrayPattern: t.arrayPattern((0, _node.deepClone)(stateVars))
324
- }), true));
325
- }
326
318
  return t.objectExpression(properties);
327
319
  }
328
320
  }, {
@@ -396,7 +388,7 @@ var _default = exports["default"] = function _default(_ref) {
396
388
  return _createClass(BasicBlock, [{
397
389
  key: "withDiscriminant",
398
390
  get: function get() {
399
- if (!this.allowWithDiscriminant) return null;
391
+ if (!this.allowWithDiscriminant) return;
400
392
  return this.bestWithDiscriminant;
401
393
  }
402
394
  }, {
@@ -599,8 +591,9 @@ var _default = exports["default"] = function _default(_ref) {
599
591
  fnTopBlock.body.unshift(t.expressionStatement(t.stringLiteral("Function " + statement.node.id.name + " -> Renamed to " + rename)));
600
592
  }
601
593
 
602
- // Unpack parameters
594
+ // Unpack parameters from the parameter 'argVar'
603
595
  if (statement.node.params.length > 0) {
596
+ usedArgVar = true;
604
597
  fnTopBlock.body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(statement.node.params), (0, _node.deepClone)(argVar))]));
605
598
 
606
599
  // Change bindings from 'param' to 'var'
@@ -838,6 +831,8 @@ var _default = exports["default"] = function _default(_ref) {
838
831
  if (!(0, _astUtils.isVariableIdentifier)(path)) return;
839
832
  if (me.isSkipped(path)) return;
840
833
  if (path.node[_constants.NO_RENAME] === cffIndex) return;
834
+ // For identifiers using implicit with discriminant, skip
835
+ if (path.node[_constants.WITH_STATEMENT]) return;
841
836
  var identifierName = path.node.name;
842
837
  if (identifierName === gotoFunctionName) return;
843
838
  var binding = path.scope.getBinding(identifierName);
@@ -847,22 +842,29 @@ var _default = exports["default"] = function _default(_ref) {
847
842
  if (binding.kind === "var" || binding.kind === "let" || binding.kind === "const") {} else {
848
843
  return;
849
844
  }
850
-
851
- // console.log("No binding found for " + identifierName);
852
-
853
845
  var scopeManager = scopeToScopeManager.get(binding.scope);
854
846
  if (!scopeManager) return;
855
847
  var newName = scopeManager.getNewName(identifierName, path.node);
856
848
  var memberExpression = scopeManager.getMemberExpression(newName);
857
849
  scopeManager.isNotUsed = false;
850
+
851
+ // Scope object as with discriminant? Use identifier
852
+ if (typeof basicBlock.withDiscriminant === "undefined") {
853
+ var id = t.identifier(scopeManager.propertyName);
854
+ id[_constants.WITH_STATEMENT] = true;
855
+ id[_constants.NO_RENAME] = cffIndex;
856
+ memberExpression = scopeManager.getMemberExpression(newName, id);
857
+ }
858
858
  if ((0, _astUtils.isDefiningIdentifier)(path)) {
859
859
  (0, _astUtils.replaceDefiningIdentifierToMemberExpression)(path, memberExpression);
860
860
  return;
861
861
  }
862
862
  if (!path.container) return;
863
- var isModified = (0, _astUtils.isModifiedIdentifier)(path);
864
863
  if (basicBlock.withDiscriminant && basicBlock.withDiscriminant === scopeManager && basicBlock.withDiscriminant.hasOwnName(identifierName)) {
865
- if (!isModified) {
864
+ // The defining mode must directly append to the scope object
865
+ // Subsequent uses can use the identifier
866
+ var isDefiningNode = path.node === binding.identifier;
867
+ if (!isDefiningNode) {
866
868
  memberExpression = basicBlock.identifier(newName, scopeManager);
867
869
  }
868
870
  }
@@ -902,28 +904,28 @@ var _default = exports["default"] = function _default(_ref) {
902
904
  var newStateValues = jumpBlock.stateValues,
903
905
  newTotalState = jumpBlock.totalState;
904
906
  var assignments = [];
905
- var needsIndividualAssignments = true;
906
907
  if (jumpBlock.withDiscriminant) {
907
908
  assignments.push(t.assignmentExpression("=", (0, _node.deepClone)(withMemberExpression), jumpBlock.withDiscriminant.getScopeObject()));
908
909
  } else if (basicBlock.withDiscriminant) {
909
- assignments.push(t.callExpression((0, _node.deepClone)(resetWithMemberExpression), [t.arrayExpression(newStateValues.map(_node.numericLiteral))]));
910
- needsIndividualAssignments = false;
910
+ // Reset the with discriminant to undefined using fake property
911
+ // scope["fake"] -> undefined
912
+
913
+ var fakeProperty = scopeNameGen.generate();
914
+ assignments.push(t.assignmentExpression("=", (0, _node.deepClone)(withMemberExpression), t.memberExpression((0, _node.deepClone)(scopeVar), t.stringLiteral(fakeProperty), true)));
911
915
  }
912
- if (needsIndividualAssignments) {
913
- for (var _i8 = 0; _i8 < stateVars.length; _i8++) {
914
- var oldValue = currentStateValues[_i8];
915
- var newValue = newStateValues[_i8];
916
-
917
- // console.log(oldValue, newValue);
918
- if (oldValue === newValue) continue; // No diff needed if the value doesn't change
919
-
920
- var assignment = t.assignmentExpression("=", (0, _node.deepClone)(stateVars[_i8]), (0, _node.numericLiteral)(newValue));
921
- if (!isDebug && addRelativeAssignments) {
922
- // Use diffs to create confusing code
923
- assignment = t.assignmentExpression("+=", (0, _node.deepClone)(stateVars[_i8]), (0, _node.numericLiteral)(newValue - oldValue));
924
- }
925
- assignments.push(assignment);
916
+ for (var _i8 = 0; _i8 < stateVars.length; _i8++) {
917
+ var oldValue = currentStateValues[_i8];
918
+ var newValue = newStateValues[_i8];
919
+
920
+ // console.log(oldValue, newValue);
921
+ if (oldValue === newValue) continue; // No diff needed if the value doesn't change
922
+
923
+ var assignment = t.assignmentExpression("=", (0, _node.deepClone)(stateVars[_i8]), (0, _node.numericLiteral)(newValue));
924
+ if (!isDebug && addRelativeAssignments) {
925
+ // Use diffs to create confusing code
926
+ assignment = t.assignmentExpression("+=", (0, _node.deepClone)(stateVars[_i8]), (0, _node.numericLiteral)(newValue - oldValue));
926
927
  }
928
+ assignments.push(assignment);
927
929
  }
928
930
 
929
931
  // Add debug label
@@ -1113,10 +1115,11 @@ var _default = exports["default"] = function _default(_ref) {
1113
1115
  var switchStatement = t.labeledStatement(t.identifier(switchLabel), t.switchStatement(discriminant, switchCases));
1114
1116
  var startStateValues = basicBlocks.get(startLabel).stateValues;
1115
1117
  var endTotalState = basicBlocks.get(endLabel).totalState;
1116
- var whileStatement = t.whileStatement(t.binaryExpression("!==", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(endTotalState)), t.blockStatement([t.withStatement(new _template["default"]("{withDiscriminant} || {}").expression({
1117
- withDiscriminant: (0, _node.deepClone)(withMemberExpression)
1118
+ var whileStatement = t.whileStatement(t.binaryExpression("!==", (0, _node.deepClone)(discriminant), (0, _node.numericLiteral)(endTotalState)), t.blockStatement([t.withStatement(new _template["default"]("{withDiscriminant} || {scopeVar}").expression({
1119
+ withDiscriminant: (0, _node.deepClone)(withMemberExpression),
1120
+ scopeVar: (0, _node.deepClone)(scopeVar)
1118
1121
  }), t.blockStatement([switchStatement]))]));
1119
- var parameters = [].concat(_toConsumableArray(stateVars), [argVar, scopeVar]).map(function (id) {
1122
+ var parameters = [].concat(_toConsumableArray(stateVars), [scopeVar, argVar]).map(function (id) {
1120
1123
  return (0, _node.deepClone)(id);
1121
1124
  });
1122
1125
  var parametersNames = parameters.map(function (id) {
@@ -1152,24 +1155,38 @@ var _default = exports["default"] = function _default(_ref) {
1152
1155
  (0, _assert.ok)(false);
1153
1156
  }
1154
1157
  }
1158
+
1159
+ // Ensure parameter is added (No effect if not added in this case)
1155
1160
  } catch (err) {
1156
1161
  _iterator4.e(err);
1157
1162
  } finally {
1158
1163
  _iterator4.f();
1159
1164
  }
1165
+ usedArgVar = true;
1160
1166
  Object.assign(fn, new _template["default"]("\n (function (...".concat(argumentsRestName, "){\n ").concat(isDebug ? "\"Calling ".concat(originalFnName, ", Label: ").concat(fnLabel, "\";") : "", "\n return {callExpression}\n })\n \n ")).expression({
1161
1167
  callExpression: t.callExpression((0, _node.deepClone)(mainFnName), argumentsNodes)
1162
1168
  }));
1163
1169
  }
1164
1170
  var startProgramObjectExpression = basicBlocks.get(startLabel).scopeManager.getObjectExpression(startLabel);
1165
1171
  var mainParameters = parameters;
1166
- var scopeParameter = mainParameters.pop();
1167
- mainParameters.push(t.assignmentPattern(scopeParameter, startProgramObjectExpression));
1172
+
1173
+ // First state values use the default parameter for initialization
1174
+ // function main(..., scope = { mainScope: {} }, ...){...}
1175
+ mainParameters.splice(mainParameters.findIndex(function (p) {
1176
+ return p.name === scopeVar.name;
1177
+ }), 1, t.assignmentPattern((0, _node.deepClone)(scopeVar), startProgramObjectExpression));
1178
+
1179
+ // Remove parameter 'argVar' if never used (No function calls obfuscated)
1180
+ if (!usedArgVar) {
1181
+ mainParameters.pop();
1182
+ }
1168
1183
  var mainFnDeclaration = t.functionDeclaration((0, _node.deepClone)(mainFnName), parameters, t.blockStatement([whileStatement]));
1184
+
1185
+ // The main function is always called with same number of arguments
1169
1186
  mainFnDeclaration[_constants.PREDICTABLE] = true;
1170
- var startProgramExpression = t.callExpression((0, _node.deepClone)(mainFnName), [].concat(_toConsumableArray(startStateValues.map(function (stateValue) {
1187
+ var startProgramExpression = t.callExpression((0, _node.deepClone)(mainFnName), _toConsumableArray(startStateValues.map(function (stateValue) {
1171
1188
  return (0, _node.numericLiteral)(stateValue);
1172
- })), [t.identifier("undefined")]));
1189
+ })));
1173
1190
  var _resultVar = withIdentifier("result");
1174
1191
  var isTopLevel = blockPath.isProgram();
1175
1192
  var allowReturns = !isTopLevel && blockPath.find(function (p) {
@@ -89,10 +89,19 @@ var PluginInstance = exports.PluginInstance = /*#__PURE__*/function () {
89
89
  fnName: this.setFunctionLengthName
90
90
  })));
91
91
  }
92
+ var createCallArguments = function createCallArguments(node) {
93
+ var args = [node];
94
+
95
+ // 1 is the default value in the setFunction template, can exclude it
96
+ if (originalLength !== 1) {
97
+ args.push((0, _node.numericLiteral)(originalLength));
98
+ }
99
+ return args;
100
+ };
92
101
  if (t.isFunctionDeclaration(path.node)) {
93
- (0, _astUtils.prepend)(path.parentPath, t.expressionStatement(t.callExpression(t.identifier(this.setFunctionLengthName), [t.identifier(path.node.id.name), (0, _node.numericLiteral)(originalLength)])));
102
+ (0, _astUtils.prepend)(path.parentPath, t.expressionStatement(t.callExpression(t.identifier(this.setFunctionLengthName), createCallArguments(t.identifier(path.node.id.name)))));
94
103
  } else if (t.isFunctionExpression(path.node) || t.isArrowFunctionExpression(path.node)) {
95
- path.replaceWith(t.callExpression(t.identifier(this.setFunctionLengthName), [path.node, (0, _node.numericLiteral)(originalLength)]));
104
+ path.replaceWith(t.callExpression(t.identifier(this.setFunctionLengthName), createCallArguments(path.node)));
96
105
  } else {
97
106
  // TODO
98
107
  }
@@ -154,7 +163,7 @@ var PluginInstance = exports.PluginInstance = /*#__PURE__*/function () {
154
163
  for (var _len3 = arguments.length, messages = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
155
164
  messages[_key3] = arguments[_key3];
156
165
  }
157
- throw new Error("[".concat(this.name, "] ").concat(messages.join(", ")));
166
+ throw new Error("[".concat(this.name, "] ").concat(messages.join(" ")));
158
167
  }
159
168
  }]);
160
169
  }();
@@ -212,24 +212,6 @@ function getMemberExpressionPropertyAsString(member) {
212
212
  }
213
213
  return null; // If the property cannot be determined
214
214
  }
215
- function registerPaths(paths) {
216
- var _iterator3 = _createForOfIteratorHelper(paths),
217
- _step3;
218
- try {
219
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
220
- var path = _step3.value;
221
- if (path.isVariableDeclaration() && path.node.kind === "var") {
222
- getParentFunctionOrProgram(path).scope.registerDeclaration(path);
223
- }
224
- path.scope.registerDeclaration(path);
225
- }
226
- } catch (err) {
227
- _iterator3.e(err);
228
- } finally {
229
- _iterator3.f();
230
- }
231
- return paths;
232
- }
233
215
  function nodeListToNodes(nodesIn) {
234
216
  var nodes = [];
235
217
  if (Array.isArray(nodesIn[0])) {
@@ -258,11 +240,11 @@ function append(path) {
258
240
  if (listParent.isProgram()) {
259
241
  var lastExpression = listParent.get("body").at(-1);
260
242
  if (lastExpression.isExpressionStatement()) {
261
- return registerPaths(lastExpression.insertBefore(nodes));
243
+ return lastExpression.insertBefore(nodes);
262
244
  }
263
245
  }
264
246
  if (listParent.isSwitchCase()) {
265
- return registerPaths(listParent.pushContainer("consequent", nodes));
247
+ return listParent.pushContainer("consequent", nodes);
266
248
  }
267
249
  if (listParent.isFunction()) {
268
250
  var body = listParent.get("body");
@@ -272,10 +254,10 @@ function append(path) {
272
254
  }
273
255
  }
274
256
  (0, _assert.ok)(body.isBlockStatement());
275
- return registerPaths(body.pushContainer("body", nodes));
257
+ return body.pushContainer("body", nodes);
276
258
  }
277
259
  (0, _assert.ok)(listParent.isBlock());
278
- return registerPaths(listParent.pushContainer("body", nodes));
260
+ return listParent.pushContainer("body", nodes);
279
261
  }
280
262
 
281
263
  /**
@@ -304,28 +286,28 @@ function prepend(path) {
304
286
  // Filter out import declarations
305
287
  var _body = listParent.get("body");
306
288
  var afterImport = 0;
307
- var _iterator4 = _createForOfIteratorHelper(_body),
308
- _step4;
289
+ var _iterator3 = _createForOfIteratorHelper(_body),
290
+ _step3;
309
291
  try {
310
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
311
- var stmt = _step4.value;
292
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
293
+ var stmt = _step3.value;
312
294
  if (!stmt.isImportDeclaration()) {
313
295
  break;
314
296
  }
315
297
  afterImport++;
316
298
  }
317
299
  } catch (err) {
318
- _iterator4.e(err);
300
+ _iterator3.e(err);
319
301
  } finally {
320
- _iterator4.f();
302
+ _iterator3.f();
321
303
  }
322
304
  if (afterImport === 0) {
323
305
  // No import declarations, so we can safely unshift everything
324
- return registerPaths(listParent.unshiftContainer("body", nodes));
306
+ return listParent.unshiftContainer("body", nodes);
325
307
  }
326
308
 
327
309
  // Insert the nodes after the last import declaration
328
- return registerPaths(_body[afterImport - 1].insertAfter(nodes));
310
+ return _body[afterImport - 1].insertAfter(nodes);
329
311
  }
330
312
  if (listParent.isFunction()) {
331
313
  var body = listParent.get("body");
@@ -335,13 +317,13 @@ function prepend(path) {
335
317
  }
336
318
  }
337
319
  (0, _assert.ok)(body.isBlockStatement());
338
- return registerPaths(body.unshiftContainer("body", nodes));
320
+ return body.unshiftContainer("body", nodes);
339
321
  }
340
322
  if (listParent.isBlock()) {
341
- return registerPaths(listParent.unshiftContainer("body", nodes));
323
+ return listParent.unshiftContainer("body", nodes);
342
324
  }
343
325
  if (listParent.isSwitchCase()) {
344
- return registerPaths(listParent.unshiftContainer("consequent", nodes));
326
+ return listParent.unshiftContainer("consequent", nodes);
345
327
  }
346
328
  (0, _assert.ok)(false);
347
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-confuser",
3
- "version": "2.0.0-alpha.4",
3
+ "version": "2.0.0-alpha.5",
4
4
  "description": "JavaScript Obfuscation Tool.",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.d.ts",
package/src/constants.ts CHANGED
@@ -1,5 +1,3 @@
1
- export const predictableFunctionTag = "__JS_PREDICT__";
2
-
3
1
  /**
4
2
  * A function is 'unsafe' if it requires 'eval', 'arguments' or 'this'
5
3
  *
@@ -121,6 +119,7 @@ export const reservedObjectPrototype = new Set([
121
119
  export const reservedKeywords = [
122
120
  "if",
123
121
  "in",
122
+ "do",
124
123
  "for",
125
124
  "let",
126
125
  "new",
@@ -129,6 +128,7 @@ export const reservedKeywords = [
129
128
  "case",
130
129
  "else",
131
130
  "null",
131
+ "with",
132
132
  "break",
133
133
  "catch",
134
134
  "class",
package/src/index.ts CHANGED
@@ -66,8 +66,13 @@ export async function obfuscateWithProfiler(
66
66
  };
67
67
 
68
68
  transformMap[log.currentTransform] = entry;
69
- currentTransformTime = nowTime;
69
+
70
+ // (JS-Confuser.com can run performance benchmark tests here)
70
71
  profiler.callback?.(log, entry, ast);
72
+
73
+ // The profiler.callback() function may take a long time to execute,
74
+ // so we need to update the currentTransformTime here for accurate profiling.
75
+ currentTransformTime = performance.now();
71
76
  },
72
77
  });
73
78
 
@@ -1,4 +1,4 @@
1
- import { MULTI_TRANSFORM, SKIP, UNSAFE } from "../constants";
1
+ import { MULTI_TRANSFORM, SKIP } from "../constants";
2
2
  import Template from "./template";
3
3
 
4
4
  /**
@@ -5,7 +5,6 @@ import {
5
5
  ensureComputedExpression,
6
6
  getParentFunctionOrProgram,
7
7
  isDefiningIdentifier,
8
- isModifiedIdentifier,
9
8
  isStrictMode,
10
9
  isVariableIdentifier,
11
10
  replaceDefiningIdentifierToMemberExpression,
@@ -163,8 +162,6 @@ export default ({ Plugin }: PluginArg): PluginObject => {
163
162
  }
164
163
  programOrFunctionPath.scope.crawl();
165
164
 
166
- const blockFnParent = getParentFunctionOrProgram(blockPath);
167
-
168
165
  let hasIllegalNode = false;
169
166
  const bindingNames = new Set<string>();
170
167
  blockPath.traverse({
@@ -231,6 +228,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
231
228
  .map((_, i) => withIdentifier(`state_${i}`));
232
229
 
233
230
  const argVar = withIdentifier("_arg");
231
+ let usedArgVar = false;
234
232
 
235
233
  const didReturnVar = withIdentifier("return");
236
234
 
@@ -255,16 +253,6 @@ export default ({ Plugin }: PluginArg): PluginObject => {
255
253
  ).expression<t.MemberExpression>();
256
254
  withMemberExpression.object[NO_RENAME] = cffIndex;
257
255
 
258
- // Create 'resetWith' function - Safely resets the 'with' object to none
259
- const resetWithProperty = isDebug
260
- ? "resetWith"
261
- : scopeNameGen.generate(false);
262
-
263
- const resetWithMemberExpression = new Template(
264
- `${scopeVar.name}["${resetWithProperty}"]`
265
- ).expression<t.MemberExpression>();
266
- resetWithMemberExpression.object[NO_RENAME] = cffIndex;
267
-
268
256
  class ScopeManager {
269
257
  isNotUsed = true;
270
258
  requiresInitializing = true;
@@ -275,8 +263,11 @@ export default ({ Plugin }: PluginArg): PluginObject => {
275
263
  : new NameGen(me.options.identifierGenerator);
276
264
 
277
265
  findBestWithDiscriminant(basicBlock: BasicBlock): ScopeManager {
266
+ // This initializing block is forbidden to have a with discriminant
267
+ // (As no previous code is able to prepare the with discriminant)
278
268
  if (basicBlock !== this.initializingBasicBlock) {
279
- if (this.nameMap.size > 0) return this;
269
+ // If no variables were defined in this scope, don't use it
270
+ if (Object.keys(this.scope.bindings).length > 0) return this;
280
271
  }
281
272
 
282
273
  return this.parent?.findBestWithDiscriminant(basicBlock);
@@ -337,9 +328,12 @@ export default ({ Plugin }: PluginArg): PluginObject => {
337
328
  : new Template(`({})`).expression();
338
329
  }
339
330
 
340
- getMemberExpression(name: string) {
331
+ getMemberExpression(
332
+ name: string,
333
+ object: t.Expression = this.getScopeObject()
334
+ ) {
341
335
  const memberExpression = t.memberExpression(
342
- this.getScopeObject(),
336
+ object,
343
337
  t.stringLiteral(name),
344
338
  true
345
339
  );
@@ -353,7 +347,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
353
347
  public initializingBasicBlock: BasicBlock
354
348
  ) {
355
349
  this.propertyName = isDebug
356
- ? "_" + scopeCounter++
350
+ ? "_" + cffIndex + "_" + scopeCounter++
357
351
  : scopeNameGen.generate();
358
352
  }
359
353
 
@@ -390,25 +384,6 @@ export default ({ Plugin }: PluginArg): PluginObject => {
390
384
  );
391
385
  }
392
386
 
393
- if (this === mainScope) {
394
- // Reset With logic
395
- properties.push(
396
- t.objectProperty(
397
- t.stringLiteral(resetWithProperty),
398
- new Template(`
399
- (function(newStateValues, alwaysUndefined){
400
- {withMemberExpression} = alwaysUndefined;
401
- {arrayPattern} = newStateValues
402
- })
403
- `).expression({
404
- withMemberExpression: deepClone(withMemberExpression),
405
- arrayPattern: t.arrayPattern(deepClone(stateVars)),
406
- }),
407
- true
408
- )
409
- );
410
- }
411
-
412
387
  return t.objectExpression(properties);
413
388
  }
414
389
 
@@ -434,7 +409,7 @@ export default ({ Plugin }: PluginArg): PluginObject => {
434
409
  bestWithDiscriminant: ScopeManager;
435
410
 
436
411
  get withDiscriminant() {
437
- if (!this.allowWithDiscriminant) return null;
412
+ if (!this.allowWithDiscriminant) return;
438
413
 
439
414
  return this.bestWithDiscriminant;
440
415
  }
@@ -774,8 +749,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
774
749
  );
775
750
  }
776
751
 
777
- // Unpack parameters
752
+ // Unpack parameters from the parameter 'argVar'
778
753
  if (statement.node.params.length > 0) {
754
+ usedArgVar = true;
779
755
  fnTopBlock.body.unshift(
780
756
  t.variableDeclaration("var", [
781
757
  t.variableDeclarator(
@@ -1099,6 +1075,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1099
1075
  if (!isVariableIdentifier(path)) return;
1100
1076
  if (me.isSkipped(path)) return;
1101
1077
  if ((path.node as NodeSymbol)[NO_RENAME] === cffIndex) return;
1078
+ // For identifiers using implicit with discriminant, skip
1079
+ if ((path.node as NodeSymbol)[WITH_STATEMENT]) return;
1102
1080
 
1103
1081
  const identifierName = path.node.name;
1104
1082
  if (identifierName === gotoFunctionName) return;
@@ -1117,8 +1095,6 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1117
1095
  return;
1118
1096
  }
1119
1097
 
1120
- // console.log("No binding found for " + identifierName);
1121
-
1122
1098
  var scopeManager = scopeToScopeManager.get(binding.scope);
1123
1099
  if (!scopeManager) return;
1124
1100
 
@@ -1132,6 +1108,18 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1132
1108
 
1133
1109
  scopeManager.isNotUsed = false;
1134
1110
 
1111
+ // Scope object as with discriminant? Use identifier
1112
+ if (typeof basicBlock.withDiscriminant === "undefined") {
1113
+ const id = t.identifier(scopeManager.propertyName);
1114
+ (id as NodeSymbol)[WITH_STATEMENT] = true;
1115
+ (id as NodeSymbol)[NO_RENAME] = cffIndex;
1116
+
1117
+ memberExpression = scopeManager.getMemberExpression(
1118
+ newName,
1119
+ id
1120
+ );
1121
+ }
1122
+
1135
1123
  if (isDefiningIdentifier(path)) {
1136
1124
  replaceDefiningIdentifierToMemberExpression(
1137
1125
  path,
@@ -1142,14 +1130,16 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1142
1130
 
1143
1131
  if (!path.container) return;
1144
1132
 
1145
- var isModified = isModifiedIdentifier(path);
1146
-
1147
1133
  if (
1148
1134
  basicBlock.withDiscriminant &&
1149
1135
  basicBlock.withDiscriminant === scopeManager &&
1150
1136
  basicBlock.withDiscriminant.hasOwnName(identifierName)
1151
1137
  ) {
1152
- if (!isModified) {
1138
+ // The defining mode must directly append to the scope object
1139
+ // Subsequent uses can use the identifier
1140
+ const isDefiningNode = path.node === binding.identifier;
1141
+
1142
+ if (!isDefiningNode) {
1153
1143
  memberExpression = basicBlock.identifier(
1154
1144
  newName,
1155
1145
  scopeManager
@@ -1218,7 +1208,6 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1218
1208
  } = jumpBlock;
1219
1209
 
1220
1210
  const assignments: t.Expression[] = [];
1221
- let needsIndividualAssignments = true;
1222
1211
 
1223
1212
  if (jumpBlock.withDiscriminant) {
1224
1213
  assignments.push(
@@ -1229,39 +1218,47 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1229
1218
  )
1230
1219
  );
1231
1220
  } else if (basicBlock.withDiscriminant) {
1221
+ // Reset the with discriminant to undefined using fake property
1222
+ // scope["fake"] -> undefined
1223
+
1224
+ const fakeProperty = scopeNameGen.generate();
1225
+
1232
1226
  assignments.push(
1233
- t.callExpression(deepClone(resetWithMemberExpression), [
1234
- t.arrayExpression(newStateValues.map(numericLiteral)),
1235
- ])
1227
+ t.assignmentExpression(
1228
+ "=",
1229
+ deepClone(withMemberExpression),
1230
+ t.memberExpression(
1231
+ deepClone(scopeVar),
1232
+ t.stringLiteral(fakeProperty),
1233
+ true
1234
+ )
1235
+ )
1236
1236
  );
1237
- needsIndividualAssignments = false;
1238
1237
  }
1239
1238
 
1240
- if (needsIndividualAssignments) {
1241
- for (let i = 0; i < stateVars.length; i++) {
1242
- const oldValue = currentStateValues[i];
1243
- const newValue = newStateValues[i];
1239
+ for (let i = 0; i < stateVars.length; i++) {
1240
+ const oldValue = currentStateValues[i];
1241
+ const newValue = newStateValues[i];
1244
1242
 
1245
- // console.log(oldValue, newValue);
1246
- if (oldValue === newValue) continue; // No diff needed if the value doesn't change
1243
+ // console.log(oldValue, newValue);
1244
+ if (oldValue === newValue) continue; // No diff needed if the value doesn't change
1247
1245
 
1248
- let assignment = t.assignmentExpression(
1249
- "=",
1246
+ let assignment = t.assignmentExpression(
1247
+ "=",
1248
+ deepClone(stateVars[i]),
1249
+ numericLiteral(newValue)
1250
+ );
1251
+
1252
+ if (!isDebug && addRelativeAssignments) {
1253
+ // Use diffs to create confusing code
1254
+ assignment = t.assignmentExpression(
1255
+ "+=",
1250
1256
  deepClone(stateVars[i]),
1251
- numericLiteral(newValue)
1257
+ numericLiteral(newValue - oldValue)
1252
1258
  );
1253
-
1254
- if (!isDebug && addRelativeAssignments) {
1255
- // Use diffs to create confusing code
1256
- assignment = t.assignmentExpression(
1257
- "+=",
1258
- deepClone(stateVars[i]),
1259
- numericLiteral(newValue - oldValue)
1260
- );
1261
- }
1262
-
1263
- assignments.push(assignment);
1264
1259
  }
1260
+
1261
+ assignments.push(assignment);
1265
1262
  }
1266
1263
 
1267
1264
  // Add debug label
@@ -1515,8 +1512,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1515
1512
  ),
1516
1513
  t.blockStatement([
1517
1514
  t.withStatement(
1518
- new Template(`{withDiscriminant} || {}`).expression({
1515
+ new Template(`{withDiscriminant} || {scopeVar}`).expression({
1519
1516
  withDiscriminant: deepClone(withMemberExpression),
1517
+ scopeVar: deepClone(scopeVar),
1520
1518
  }),
1521
1519
  t.blockStatement([switchStatement])
1522
1520
  ),
@@ -1525,8 +1523,8 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1525
1523
 
1526
1524
  const parameters: t.Identifier[] = [
1527
1525
  ...stateVars,
1528
- argVar,
1529
1526
  scopeVar,
1527
+ argVar,
1530
1528
  ].map((id) => deepClone(id));
1531
1529
 
1532
1530
  const parametersNames: string[] = parameters.map((id) => id.name);
@@ -1558,6 +1556,9 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1558
1556
  }
1559
1557
  }
1560
1558
 
1559
+ // Ensure parameter is added (No effect if not added in this case)
1560
+ usedArgVar = true;
1561
+
1561
1562
  Object.assign(
1562
1563
  fn,
1563
1564
  new Template(`
@@ -1584,23 +1585,36 @@ export default ({ Plugin }: PluginArg): PluginObject => {
1584
1585
  .scopeManager.getObjectExpression(startLabel);
1585
1586
 
1586
1587
  const mainParameters: t.FunctionDeclaration["params"] = parameters;
1587
- const scopeParameter = mainParameters.pop() as t.Identifier;
1588
1588
 
1589
- mainParameters.push(
1590
- t.assignmentPattern(scopeParameter, startProgramObjectExpression)
1589
+ // First state values use the default parameter for initialization
1590
+ // function main(..., scope = { mainScope: {} }, ...){...}
1591
+ mainParameters.splice(
1592
+ (mainParameters as t.Identifier[]).findIndex(
1593
+ (p) => p.name === scopeVar.name
1594
+ ),
1595
+ 1,
1596
+ t.assignmentPattern(
1597
+ deepClone(scopeVar),
1598
+ startProgramObjectExpression
1599
+ )
1591
1600
  );
1592
1601
 
1602
+ // Remove parameter 'argVar' if never used (No function calls obfuscated)
1603
+ if (!usedArgVar) {
1604
+ mainParameters.pop();
1605
+ }
1606
+
1593
1607
  const mainFnDeclaration = t.functionDeclaration(
1594
1608
  deepClone(mainFnName),
1595
1609
  parameters,
1596
1610
  t.blockStatement([whileStatement])
1597
1611
  );
1598
1612
 
1613
+ // The main function is always called with same number of arguments
1599
1614
  (mainFnDeclaration as NodeSymbol)[PREDICTABLE] = true;
1600
1615
 
1601
1616
  var startProgramExpression = t.callExpression(deepClone(mainFnName), [
1602
1617
  ...startStateValues.map((stateValue) => numericLiteral(stateValue)),
1603
- t.identifier("undefined"),
1604
1618
  ]);
1605
1619
 
1606
1620
  const resultVar = withIdentifier("result");
@@ -97,14 +97,24 @@ export class PluginInstance {
97
97
  );
98
98
  }
99
99
 
100
+ const createCallArguments = (node: t.Expression): t.Expression[] => {
101
+ var args = [node];
102
+
103
+ // 1 is the default value in the setFunction template, can exclude it
104
+ if (originalLength !== 1) {
105
+ args.push(numericLiteral(originalLength));
106
+ }
107
+ return args;
108
+ };
109
+
100
110
  if (t.isFunctionDeclaration(path.node)) {
101
111
  prepend(
102
112
  path.parentPath,
103
113
  t.expressionStatement(
104
- t.callExpression(t.identifier(this.setFunctionLengthName), [
105
- t.identifier(path.node.id.name),
106
- numericLiteral(originalLength),
107
- ])
114
+ t.callExpression(
115
+ t.identifier(this.setFunctionLengthName),
116
+ createCallArguments(t.identifier(path.node.id.name))
117
+ )
108
118
  )
109
119
  );
110
120
  } else if (
@@ -112,10 +122,10 @@ export class PluginInstance {
112
122
  t.isArrowFunctionExpression(path.node)
113
123
  ) {
114
124
  path.replaceWith(
115
- t.callExpression(t.identifier(this.setFunctionLengthName), [
116
- path.node,
117
- numericLiteral(originalLength),
118
- ])
125
+ t.callExpression(
126
+ t.identifier(this.setFunctionLengthName),
127
+ createCallArguments(path.node)
128
+ )
119
129
  );
120
130
  } else {
121
131
  // TODO
@@ -158,6 +168,6 @@ export class PluginInstance {
158
168
  * @param messages
159
169
  */
160
170
  error(...messages: any[]): never {
161
- throw new Error(`[${this.name}] ${messages.join(", ")}`);
171
+ throw new Error(`[${this.name}] ${messages.join(" ")}`);
162
172
  }
163
173
  }
@@ -215,17 +215,6 @@ export function getMemberExpressionPropertyAsString(
215
215
  return null; // If the property cannot be determined
216
216
  }
217
217
 
218
- function registerPaths(paths: NodePath[]) {
219
- for (var path of paths) {
220
- if (path.isVariableDeclaration() && path.node.kind === "var") {
221
- getParentFunctionOrProgram(path).scope.registerDeclaration(path);
222
- }
223
- path.scope.registerDeclaration(path);
224
- }
225
-
226
- return paths;
227
- }
228
-
229
218
  function nodeListToNodes(nodesIn: (t.Statement | t.Statement[])[]) {
230
219
  var nodes: t.Statement[] = [];
231
220
  if (Array.isArray(nodesIn[0])) {
@@ -257,12 +246,12 @@ export function append(
257
246
  if (listParent.isProgram()) {
258
247
  var lastExpression = listParent.get("body").at(-1);
259
248
  if (lastExpression.isExpressionStatement()) {
260
- return registerPaths(lastExpression.insertBefore(nodes));
249
+ return lastExpression.insertBefore(nodes);
261
250
  }
262
251
  }
263
252
 
264
253
  if (listParent.isSwitchCase()) {
265
- return registerPaths(listParent.pushContainer("consequent", nodes));
254
+ return listParent.pushContainer("consequent", nodes);
266
255
  }
267
256
 
268
257
  if (listParent.isFunction()) {
@@ -278,11 +267,11 @@ export function append(
278
267
 
279
268
  ok(body.isBlockStatement());
280
269
 
281
- return registerPaths(body.pushContainer("body", nodes));
270
+ return body.pushContainer("body", nodes);
282
271
  }
283
272
 
284
273
  ok(listParent.isBlock());
285
- return registerPaths(listParent.pushContainer("body", nodes));
274
+ return listParent.pushContainer("body", nodes);
286
275
  }
287
276
 
288
277
  /**
@@ -322,11 +311,11 @@ export function prepend(
322
311
 
323
312
  if (afterImport === 0) {
324
313
  // No import declarations, so we can safely unshift everything
325
- return registerPaths(listParent.unshiftContainer("body", nodes));
314
+ return listParent.unshiftContainer("body", nodes);
326
315
  }
327
316
 
328
317
  // Insert the nodes after the last import declaration
329
- return registerPaths(body[afterImport - 1].insertAfter(nodes));
318
+ return body[afterImport - 1].insertAfter(nodes);
330
319
  }
331
320
 
332
321
  if (listParent.isFunction()) {
@@ -342,15 +331,15 @@ export function prepend(
342
331
 
343
332
  ok(body.isBlockStatement());
344
333
 
345
- return registerPaths(body.unshiftContainer("body", nodes));
334
+ return body.unshiftContainer("body", nodes);
346
335
  }
347
336
 
348
337
  if (listParent.isBlock()) {
349
- return registerPaths(listParent.unshiftContainer("body", nodes));
338
+ return listParent.unshiftContainer("body", nodes);
350
339
  }
351
340
 
352
341
  if (listParent.isSwitchCase()) {
353
- return registerPaths(listParent.unshiftContainer("consequent", nodes));
342
+ return listParent.unshiftContainer("consequent", nodes);
354
343
  }
355
344
 
356
345
  ok(false);
@@ -564,7 +553,7 @@ export function isModifiedIdentifier(identifierPath: NodePath<t.Identifier>) {
564
553
 
565
554
  export function replaceDefiningIdentifierToMemberExpression(
566
555
  path: NodePath<t.Identifier>,
567
- memberExpression: t.MemberExpression
556
+ memberExpression: t.MemberExpression | t.Identifier
568
557
  ) {
569
558
  // function id(){} -> var id = function() {}
570
559
  if (path.key === "id" && path.parentPath.isFunctionDeclaration()) {