js-confuser
Version:
JavaScript Obfuscation Tool.
339 lines (316 loc) • 19 kB
JavaScript
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var t = _interopRequireWildcard(require("@babel/types"));
var _template = _interopRequireDefault(require("../templates/template"));
var _assert = require("assert");
var _randomUtils = require("../utils/random-utils");
var _order = require("../order");
var _constants = require("../constants");
var _functionUtils = require("../utils/function-utils");
var _setFunctionLengthTemplate = require("../templates/setFunctionLengthTemplate");
var _node = require("../utils/node");
var _astUtils = require("../utils/ast-utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var _default = exports["default"] = function _default(_ref) {
var Plugin = _ref.Plugin;
var me = Plugin(_order.Order.Dispatcher, {
changeData: {
functions: 0
}
});
var dispatcherCounter = 0;
var setFunctionLength = me.getPlaceholder("d_fnLength");
// in Debug mode, function names are preserved
var isDebug = false;
return {
visitor: {
"Program|Function": {
exit: function exit(_path) {
var blockPath = _path;
if (blockPath.isProgram()) {
blockPath.scope.crawl();
// Don't insert function length code when disabled
// Instead insert empty function as the identifier is still referenced
var insertNode = t.functionDeclaration(t.identifier(setFunctionLength), [], t.blockStatement([]));
if (me.options.preserveFunctionLength) {
// Insert function length code
insertNode = _setFunctionLengthTemplate.SetFunctionLengthTemplate.single({
fnName: setFunctionLength
});
}
me.skip((0, _astUtils.prependProgram)(_path, insertNode));
}
if (blockPath.node[_constants.UNSAFE]) return;
// For testing
// if (!blockPath.isProgram()) return;
var blockStatement = blockPath.isProgram() ? blockPath : blockPath.get("body");
// Track functions and illegal ones
// A function is illegal if:
// - the function is async or generator
// - the function is redefined
// - the function uses 'this', 'eval', or 'arguments'
var functionPaths = new Map();
var illegalNames = new Set();
// Scan for function declarations
blockPath.traverse({
// Check for reassigned / redefined functions
BindingIdentifier: {
exit: function exit(path) {
var _path$parentPath;
if (!(0, _astUtils.isVariableIdentifier)(path)) return;
var name = path.node.name;
if (!((_path$parentPath = path.parentPath) !== null && _path$parentPath !== void 0 && _path$parentPath.isFunctionDeclaration())) {
illegalNames.add(name);
}
}
},
// Find functions eligible for dispatching
FunctionDeclaration: {
exit: function exit(path) {
var name = path.node.id.name;
// If the function is not named, we can't dispatch it
if (!name) {
return;
}
// Do not apply to async or generator functions
if (path.node.async || path.node.generator) {
return;
}
// Do not apply to functions in nested scopes
if (path.parentPath !== blockStatement || me.isSkipped(path)) {
illegalNames.add(name);
return;
}
if ((0, _astUtils.isStrictMode)(path)) {
illegalNames.add(name);
return;
}
// Do not apply to unsafe functions, redefined functions, or internal obfuscator functions
if (path.node[_constants.UNSAFE] || functionPaths.has(name) || me.obfuscator.isInternalVariable(name)) {
illegalNames.add(name);
return;
}
// Functions with default parameters are not fully supported
// (Could be a Function Expression referencing outer scope)
if (path.node.params.find(function (x) {
return x.type === "AssignmentPattern";
})) {
illegalNames.add(name);
return;
}
functionPaths.set(name, path);
}
}
});
var _iterator = _createForOfIteratorHelper(illegalNames),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _name = _step.value;
functionPaths["delete"](_name);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var _iterator2 = _createForOfIteratorHelper(functionPaths.keys()),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var name = _step2.value;
if (!me.computeProbabilityMap(me.options.dispatcher, name)) {
functionPaths["delete"](name);
}
}
// No functions here to change
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
if (functionPaths.size === 0) {
return;
}
me.changeData.functions += functionPaths.size;
var dispatcherName = me.getPlaceholder() + "_dispatcher_" + dispatcherCounter++;
var payloadName = me.getPlaceholder() + "_payload";
var cacheName = me.getPlaceholder() + "_cache";
var newNameMapping = new Map();
var keys = {
placeholderNoMeaning: isDebug ? "noMeaning" : (0, _randomUtils.getRandomString)(10),
clearPayload: isDebug ? "clearPayload" : (0, _randomUtils.getRandomString)(10),
nonCall: isDebug ? "nonCall" : (0, _randomUtils.getRandomString)(10),
returnAsObject: isDebug ? "returnAsObject" : (0, _randomUtils.getRandomString)(10),
returnAsObjectProperty: isDebug ? "returnAsObjectProperty" : (0, _randomUtils.getRandomString)(10)
};
var _iterator3 = _createForOfIteratorHelper(functionPaths.keys()),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var name = _step3.value;
newNameMapping.set(name, isDebug ? "_" + name : (0, _randomUtils.getRandomString)(6) /** "_" + name */);
}
// Find identifiers calling/referencing the functions
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
blockPath.traverse({
ReferencedIdentifier: {
exit: function exit(path) {
if (path.isJSX()) return;
if ((0, _functionUtils.isVariableFunctionIdentifier)(path)) return;
var name = path.node.name;
var fnPath = functionPaths.get(name);
if (!fnPath) return;
var newName = newNameMapping.get(name);
// Do not replace if not referencing the actual function
if (path.scope.getBinding(name).path !== fnPath) {
return;
}
var createDispatcherCall = function createDispatcherCall(name, flagArg) {
var dispatcherArgs = [t.stringLiteral(name)];
if (flagArg) {
dispatcherArgs.push(t.stringLiteral(flagArg));
}
var asObject = (0, _randomUtils.chance)(50);
if (asObject) {
if (dispatcherArgs.length < 2) {
dispatcherArgs.push(t.stringLiteral(keys.placeholderNoMeaning));
}
dispatcherArgs.push(t.stringLiteral(keys.returnAsObject));
}
var callExpression = t.callExpression(t.identifier(dispatcherName), dispatcherArgs);
if (!asObject) {
return callExpression;
}
if ((0, _randomUtils.chance)(50)) {
callExpression.type = "NewExpression";
}
return t.memberExpression(callExpression, t.stringLiteral(keys.returnAsObjectProperty), true);
};
// Replace the identifier with a call to the function
var parentPath = path.parentPath;
if (path.key === "callee" && parentPath !== null && parentPath !== void 0 && parentPath.isCallExpression()) {
var expressions = [];
var callArguments = parentPath.node.arguments;
if (callArguments.length === 0) {
expressions.push(
// Call the function
createDispatcherCall(newName, keys.clearPayload));
} else {
expressions.push(
// Prepare the payload arguments
t.assignmentExpression("=", t.identifier(payloadName), t.arrayExpression(callArguments)),
// Call the function
createDispatcherCall(newName));
}
var output = expressions.length === 1 ? expressions[0] : t.sequenceExpression(expressions);
if (!parentPath.container) return;
parentPath.replaceWith(output);
} else {
if (!path.container) return;
// Replace non-invocation references with a 'cached' version of the function
path.replaceWith(createDispatcherCall(newName, keys.nonCall));
}
}
}
});
var fnLengthProperties = [];
// Create the dispatcher function
var objectExpression = t.objectExpression(Array.from(newNameMapping).map(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 2),
name = _ref3[0],
newName = _ref3[1];
var originalPath = functionPaths.get(name);
var originalFn = originalPath.node;
if (me.options.preserveFunctionLength) {
var fnLength = (0, _functionUtils.computeFunctionLength)(originalPath);
if (fnLength >= 1) {
// 0 is already the default
fnLengthProperties.push(t.objectProperty(t.stringLiteral(newName), (0, _node.numericLiteral)(fnLength)));
}
}
var newBody = _toConsumableArray(originalFn.body.body);
(0, _assert.ok)(Array.isArray(newBody));
// Unpack parameters
if (originalFn.params.length > 0) {
newBody.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(_toConsumableArray(originalFn.params)), t.identifier(payloadName))]));
}
// Add debug label
if (isDebug) {
newBody.unshift(t.expressionStatement(t.stringLiteral("Dispatcher: ".concat(name, " -> ").concat(newName))));
}
var functionExpression = t.functionExpression(null, [], t.blockStatement(newBody));
var _iterator4 = _createForOfIteratorHelper(Object.getOwnPropertySymbols(originalFn)),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var symbol = _step4.value;
functionExpression[symbol] = originalFn[symbol];
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
functionExpression[_constants.PREDICTABLE] = true;
return t.objectProperty(t.stringLiteral(newName), functionExpression);
}));
var fnLengths = t.objectExpression(fnLengthProperties);
var dispatcher = new _template["default"]("\n function ".concat(dispatcherName, "(name, flagArg, returnTypeArg, fnLengths = {fnLengthsObjectExpression}) {\n var output;\n var fns = {objectExpression};\n\n if(flagArg === \"").concat(keys.clearPayload, "\") {\n ").concat(payloadName, " = [];\n }\n if(flagArg === \"").concat(keys.nonCall, "\") {\n function createFunction(){\n var fn = function(...args){ \n ").concat(payloadName, " = args;\n return fns[name].apply(this);\n }\n\n var fnLength = fnLengths[name];\n if(fnLength) {\n ").concat(setFunctionLength, "(fn, fnLength);\n }\n\n return fn;\n }\n output = ").concat(cacheName, "[name] || (").concat(cacheName, "[name] = createFunction());\n } else {\n output = fns[name]();\n }\n\n if(returnTypeArg === \"").concat(keys.returnAsObject, "\") {\n return { \"").concat(keys.returnAsObjectProperty, "\": output };\n } else {\n return output;\n }\n }\n ")).single({
objectExpression: objectExpression,
fnLengthsObjectExpression: fnLengths
});
dispatcher[_constants.PREDICTABLE] = true;
/**
* Prepends the node into the block. (And registers the declaration)
* @param node
*/
function prepend(node) {
var newPath = blockStatement.unshiftContainer("body", node)[0];
blockStatement.scope.registerDeclaration(newPath);
}
// Insert the dispatcher function
prepend(dispatcher);
// Insert the payload variable
prepend(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(payloadName))]));
// Insert the cache variable
prepend(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(cacheName), new _template["default"]("Object[\"create\"](null)").expression())]));
// Remove original functions
var _iterator5 = _createForOfIteratorHelper(functionPaths.values()),
_step5;
try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
var path = _step5.value;
path.remove();
}
} catch (err) {
_iterator5.e(err);
} finally {
_iterator5.f();
}
}
}
}
};
};
;