UNPKG

js-confuser

Version:

JavaScript Obfuscation Tool.

175 lines (162 loc) 9.85 kB
"use strict"; 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 _order = require("../order"); var t = _interopRequireWildcard(require("@babel/types")); var _obfuscator = _interopRequireDefault(require("../obfuscator")); var _astUtils = require("../utils/ast-utils"); var _constants = require("../constants"); var _functionUtils = require("../utils/function-utils"); var _node = require("../utils/node"); var _template = _interopRequireDefault(require("../templates/template")); var _tamperProtectionTemplates = require("../templates/tamperProtectionTemplates"); 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; } var RGF_ELIGIBLE = Symbol("rgfEligible"); /** * RGF (Runtime-Generated-Function) uses the `new Function("code")` syntax to create executable code from strings. * * Limitations: * * 1. Does not apply to async or generator functions * 2. Does not apply to functions that reference outside variables */ var _default = exports["default"] = function _default(_ref) { var Plugin = _ref.Plugin; var me = Plugin(_order.Order.RGF, { changeData: { functions: 0 } }); var rgfArrayName = me.getPlaceholder() + "_rgf"; var rgfEvalName = me.getPlaceholder() + "_rgf_eval"; var rgfArrayExpression = t.arrayExpression([]); var active = true; return { visitor: { Program: { enter: function enter(path) { path.scope.crawl(); }, exit: function exit(path) { active = false; if (rgfArrayExpression.elements.length === 0) return; // Insert the RGF array at the top of the program (0, _astUtils.prepend)(path, t.variableDeclaration("var", [t.variableDeclarator(t.identifier(rgfArrayName), rgfArrayExpression)])); var rgfEvalIntegrity = me.getPlaceholder() + "_rgf_eval_integrity"; (0, _astUtils.prepend)(path, new _template["default"]("\n {EvalIntegrity}\n var ".concat(rgfEvalIntegrity, " = {EvalIntegrityName}();\n ")).compile({ EvalIntegrity: (0, _tamperProtectionTemplates.createEvalIntegrityTemplate)(me, path), EvalIntegrityName: me.getPlaceholder() })); (0, _astUtils.append)(path, new _template["default"]("\n function ".concat(rgfEvalName, "(code) {\n if (").concat(rgfEvalIntegrity, ") {\n return eval(code);\n }\n }\n ")).addSymbols(_constants.UNSAFE).single()); } }, "FunctionDeclaration|FunctionExpression": { enter: function enter(_path) { var _me$options$lock; if (!active) return; // On enter, determine if Function is eligible for RGF transformation var path = _path; if (me.isSkipped(path)) return; // Skip nested functions if the parent function is already deemed eligible if (path.find(function (p) { return p.node[RGF_ELIGIBLE] || p.node[_constants.MULTI_TRANSFORM]; })) return; // Skip async and generator functions if (path.node.async || path.node.generator) return; var name = (0, _astUtils.getFunctionName)(path); if (name === ((_me$options$lock = me.options.lock) === null || _me$options$lock === void 0 ? void 0 : _me$options$lock.countermeasures)) return; if (me.obfuscator.isInternalVariable(name)) return; if (!me.computeProbabilityMap(me.options.rgf, name, path.getFunctionParent() === null)) return; // Skip functions with references to outside variables // Check the scope to see if this function relies on any variables defined outside the function var identifierPreventingTransform; path.traverse({ Identifier: function Identifier(idPath) { if (!(0, _astUtils.isVariableIdentifier)(idPath)) return; if (idPath.isBindingIdentifier() && (0, _astUtils.isDefiningIdentifier)(idPath)) return; var name = idPath.node.name; // RGF array name is allowed, it is not considered an outside reference if (name === rgfArrayName) return; if (_constants.reservedIdentifiers.has(name)) return; if (me.options.globalVariables.has(name)) return; var binding = idPath.scope.getBinding(name); if (!binding) { // Global variables are allowed return; } var isOutsideVariable = path.scope.parent.getBinding(name) === binding; // If the binding is not in the current scope, it is an outside reference if (isOutsideVariable) { identifierPreventingTransform = name; idPath.stop(); } } }); if (identifierPreventingTransform) { me.log("Skipping function " + name + " due to reference to outside variable: " + identifierPreventingTransform); return; } me.log("Function " + name + " is eligible for RGF transformation"); path.node[RGF_ELIGIBLE] = true; }, exit: function exit(_path) { if (!active) return; var path = _path; if (me.isSkipped(path)) return; // Function is not eligible for RGF transformation if (!path.node[RGF_ELIGIBLE]) return; var embeddedName = me.getPlaceholder() + "_embedded"; var replacementName = me.getPlaceholder() + "_replacement"; var argumentsName = me.getPlaceholder() + "_args"; var lastNode = t.expressionStatement(t.identifier(embeddedName)); lastNode[_constants.SKIP] = true; // Transform the function var evalProgram = t.program([t.functionDeclaration(t.identifier(embeddedName), [], t.blockStatement([t.variableDeclaration("var", [t.variableDeclarator(t.arrayPattern([t.identifier(rgfArrayName), t.identifier(argumentsName)]), t.identifier("arguments"))]), t.functionDeclaration(t.identifier(replacementName), path.node.params, path.node.body), t.returnStatement(t.callExpression(t.memberExpression(t.identifier(replacementName), t.identifier("apply")), [t.thisExpression(), t.identifier(argumentsName)]))])), lastNode]); var strictModeEnforcingBlock = path.find(function (p) { return (0, _astUtils.isStrictMode)(p); }); if (strictModeEnforcingBlock) { // Preserve 'use strict' directive // This is necessary to enure subsequent transforms (Control Flow Flattening) are aware of the strict mode directive evalProgram.directives.push(t.directive(t.directiveLiteral("use strict"))); } var evalFile = t.file(evalProgram); var newObfuscator = new _obfuscator["default"](me.options, me.obfuscator); var hasRan = new Set(me.obfuscator.plugins.filter(function (plugin, i) { return i <= me.obfuscator.index; }).map(function (plugin) { return plugin.pluginInstance.order; })); newObfuscator.plugins = newObfuscator.plugins.filter(function (plugin) { return plugin.pluginInstance.order == _order.Order.Preparation || !hasRan.has(plugin.pluginInstance.order); }); newObfuscator.obfuscateAST(evalFile, { disablePack: true }); var generated = _obfuscator["default"].generateCode(evalFile); var functionExpression = t.callExpression(t.identifier(rgfEvalName), [t.stringLiteral(generated)]); var index = rgfArrayExpression.elements.length; rgfArrayExpression.elements.push(functionExpression); // Params no longer needed, using 'arguments' instead var originalLength = (0, _functionUtils.computeFunctionLength)(path); path.node.params = []; // Function is now unsafe path.node[_constants.UNSAFE] = true; // Params changed and using 'arguments' path.node[_constants.PREDICTABLE] = false; me.skip(path); // Update body to point to new function path.get("body").replaceWith(t.blockStatement([t.returnStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(rgfArrayName), (0, _node.numericLiteral)(index), true), t.stringLiteral("apply"), true), [t.thisExpression(), t.arrayExpression([t.identifier(rgfArrayName), t.identifier("arguments")])]))])); path.skip(); me.setFunctionLength(path, originalLength); me.changeData.functions++; } } } }; };