js-confuser
Version:
JavaScript Obfuscation Tool.
175 lines (162 loc) • 9.85 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 _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++;
}
}
}
};
};
;