js-confuser
Version:
JavaScript Obfuscation Tool.
293 lines (271 loc) • 15.7 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 _astUtils = require("../utils/ast-utils");
var _order = require("../order");
var _constants = require("../constants");
var _functionUtils = require("../utils/function-utils");
var _NameGen = require("../utils/NameGen");
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 _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 _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 _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.Flatten, {
changeData: {
functions: 0
}
});
var isDebug = false;
function flattenFunction(fnPath) {
// Skip if already processed
if (me.isSkipped(fnPath)) return;
// Don't apply to generator functions
if (fnPath.node.generator) return;
// Skip getter/setter methods
if (fnPath.isObjectMethod() || fnPath.isClassMethod()) {
if (fnPath.node.kind !== "method") return;
}
// Do not apply to arrow functions
if (t.isArrowFunctionExpression(fnPath.node)) return;
if (!t.isBlockStatement(fnPath.node.body)) return;
// Skip if marked as unsafe
if (fnPath.node[_constants.UNSAFE]) return;
var program = fnPath.findParent(function (p) {
return p.isProgram();
});
var functionName = (0, _astUtils.getFunctionName)(fnPath);
if (!t.isValidIdentifier(functionName, true)) {
functionName = "anonymous";
}
if (!me.computeProbabilityMap(me.options.flatten, functionName)) {
return;
}
var strictMode = fnPath.find(function (path) {
return (0, _astUtils.isStrictMode)(path);
});
if (strictMode === fnPath) return;
me.log("Transforming", functionName);
var flatObjectName = "".concat(me.getPlaceholder(), "_flat_object");
var newFnName = "".concat(me.getPlaceholder(), "_flat_").concat(functionName);
var nameGen = new _NameGen.NameGen(me.options.identifierGenerator);
function generateProp(originalName, type) {
var newPropertyName;
do {
newPropertyName = isDebug ? type + "_" + originalName : nameGen.generate();
} while (allPropertyNames.has(newPropertyName));
allPropertyNames.add(newPropertyName);
return newPropertyName;
}
var standardProps = new Map();
var setterPropsNeeded = new Set();
var typeofProps = new Map();
var functionCallProps = new Map();
var allPropertyNames = new Set();
var identifierPaths = [];
// Traverse function to identify variables to be replaced with flat object properties
fnPath.traverse({
Identifier: {
exit: function exit(identifierPath) {
if (!(0, _astUtils.isVariableIdentifier)(identifierPath)) return;
if (identifierPath.isBindingIdentifier() && (0, _astUtils.isDefiningIdentifier)(identifierPath)) return;
if ((0, _functionUtils.isVariableFunctionIdentifier)(identifierPath)) return;
if (identifierPath.node[_constants.UNSAFE]) return;
var identifierName = identifierPath.node.name;
if (identifierName === "arguments") return;
var binding = identifierPath.scope.getBinding(identifierName);
if (!binding) {
return;
}
var isOutsideVariable = fnPath.scope.parent.getBinding(identifierName) === binding;
if (!isOutsideVariable) {
return;
}
identifierPaths.push(identifierPath);
}
}
});
me.log("Function ".concat(functionName), "requires", Array.from(new Set(identifierPaths.map(function (x) {
return x.node.name;
}))));
for (var _i = 0, _identifierPaths = identifierPaths; _i < _identifierPaths.length; _i++) {
var identifierPath = _identifierPaths[_i];
var identifierName = identifierPath.node.name;
if (typeof identifierName !== "string") continue;
var isTypeof = identifierPath.parentPath.isUnaryExpression({
operator: "typeof"
});
var isFunctionCall = identifierPath.parentPath.isCallExpression() && identifierPath.parentPath.node.callee === identifierPath.node;
if (isTypeof) {
var typeofProp = typeofProps.get(identifierName);
if (!typeofProp) {
typeofProp = generateProp(identifierName, "typeof");
typeofProps.set(identifierName, typeofProp);
}
(0, _astUtils.ensureComputedExpression)(identifierPath.parentPath);
identifierPath.parentPath.replaceWith(t.memberExpression(t.identifier(flatObjectName), t.stringLiteral(typeofProp), true))[0].skip();
} else if (isFunctionCall) {
var functionCallProp = functionCallProps.get(identifierName);
if (!functionCallProp) {
functionCallProp = generateProp(identifierName, "call");
functionCallProps.set(identifierName, functionCallProp);
}
(0, _astUtils.ensureComputedExpression)(identifierPath);
// Replace identifier with a reference to the flat object property
identifierPath.replaceWith(t.memberExpression(t.identifier(flatObjectName), t.stringLiteral(functionCallProp), true))[0].skip();
} else {
var standardProp = standardProps.get(identifierName);
if (!standardProp) {
standardProp = generateProp(identifierName, "standard");
standardProps.set(identifierName, standardProp);
}
if (!setterPropsNeeded.has(identifierName)) {
// Only provide 'set' method if the variable is modified
var isModification = (0, _astUtils.isModifiedIdentifier)(identifierPath);
if (isModification) {
setterPropsNeeded.add(identifierName);
}
}
(0, _astUtils.ensureComputedExpression)(identifierPath);
// Replace identifier with a reference to the flat object property
identifierPath.replaceWith(t.memberExpression(t.identifier(flatObjectName), t.stringLiteral(standardProp), true))[0].skip();
}
}
// for (const prop of [...typeofProps.keys(), ...functionCallProps.keys()]) {
// if (!standardProps.has(prop)) {
// standardProps.set(prop, generateProp());
// }
// }
var flatObjectProperties = [];
var _iterator = _createForOfIteratorHelper(standardProps),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var entry = _step.value;
var _entry = entry,
_entry2 = _slicedToArray(_entry, 2),
_identifierName = _entry2[0],
objectProp = _entry2[1];
flatObjectProperties.push(me.skip(t.objectMethod("get", t.stringLiteral(objectProp), [], t.blockStatement([t.returnStatement(t.identifier(_identifierName))]), false, false, false)));
// Not all properties need a setter
if (setterPropsNeeded.has(_identifierName)) {
var valueArgName = me.getPlaceholder() + "_value";
flatObjectProperties.push(me.skip(t.objectMethod("set", t.stringLiteral(objectProp), [t.identifier(valueArgName)], t.blockStatement([t.expressionStatement(t.assignmentExpression("=", t.identifier(_identifierName), t.identifier(valueArgName)))]), false, false, false)));
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var _iterator2 = _createForOfIteratorHelper(typeofProps),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var _entry3 = _step2.value;
var _entry4 = _slicedToArray(_entry3, 2),
_identifierName2 = _entry4[0],
_objectProp = _entry4[1];
flatObjectProperties.push(me.skip(t.objectMethod("get", t.stringLiteral(_objectProp), [], t.blockStatement([t.returnStatement(t.unaryExpression("typeof", t.identifier(_identifierName2)))]), false, false, false)));
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var _iterator3 = _createForOfIteratorHelper(functionCallProps),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var _entry5 = _step3.value;
var _entry6 = _slicedToArray(_entry5, 2),
_identifierName3 = _entry6[0],
_objectProp2 = _entry6[1];
flatObjectProperties.push(me.skip(t.objectMethod("method", t.stringLiteral(_objectProp2), [t.restElement(t.identifier("args"))], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(_identifierName3), [t.spreadElement(t.identifier("args"))]))]), false, false, false)));
}
// Create the new flattened function
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
var flattenedFunctionDeclaration = t.functionDeclaration(t.identifier(newFnName), [t.arrayPattern(_toConsumableArray(fnPath.node.params)), t.identifier(flatObjectName)], t.blockStatement(_toConsumableArray(fnPath.node.body.body).concat()), false, fnPath.node.async);
// Create the flat object variable declaration
var flatObjectDeclaration = t.variableDeclaration("var", [t.variableDeclarator(t.identifier(flatObjectName), t.objectExpression(flatObjectProperties))]);
var argName = me.getPlaceholder() + "_args";
// Replace original function body with a call to the flattened function
fnPath.node.body = t.blockStatement([flatObjectDeclaration, t.returnStatement(t.callExpression(t.identifier(newFnName), [t.identifier(argName), t.identifier(flatObjectName)]))]);
var originalLength = (0, _functionUtils.computeFunctionLength)(fnPath);
fnPath.node.params = [t.restElement(t.identifier(argName))];
// Ensure updated parameter gets registered in the function scope
fnPath.scope.crawl();
fnPath.skip();
// Add the new flattened function at the top level
var newPath = (0, _astUtils.prependProgram)(program, flattenedFunctionDeclaration)[0];
me.skip(newPath);
// Copy over all properties except the predictable flag
var _iterator4 = _createForOfIteratorHelper(Object.getOwnPropertySymbols(fnPath.node)),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var symbol = _step4.value;
if (symbol !== _constants.PREDICTABLE) {
newPath.node[symbol] = fnPath.node[symbol];
}
}
// Old function is no longer predictable (rest element parameter)
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
fnPath.node[_constants.PREDICTABLE] = false;
// Old function is unsafe (uses arguments, this)
fnPath.node[_constants.UNSAFE] = true;
newPath.node[_constants.PREDICTABLE] = true;
// Carry over 'use strict' directive if not already present
if (strictMode) {
newPath.node.body.directives.push(t.directive(t.directiveLiteral("use strict")));
// Non-simple parameter list conversion
(0, _astUtils.prepend)(newPath, t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern(newPath.node.params), t.identifier("arguments"))]));
newPath.node.params = [];
// Using 'arguments' is unsafe
newPath.node[_constants.UNSAFE] = true;
// Params changed and using 'arguments'
newPath.node[_constants.PREDICTABLE] = false;
}
// Ensure parameters are registered in the new function scope
newPath.scope.crawl();
newPath.skip();
me.skip(newPath);
// Set function length
me.setFunctionLength(fnPath, originalLength);
me.changeData.functions++;
}
return {
visitor: {
Function: {
exit: function exit(path) {
flattenFunction(path);
}
},
Program: function Program(path) {
path.scope.crawl();
}
}
};
};
;