UNPKG

js-confuser

Version:

JavaScript Obfuscation Tool.

575 lines (548 loc) 21.6 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.append = append; exports.ensureComputedExpression = ensureComputedExpression; exports.getBlock = getBlock; exports.getFunctionName = getFunctionName; exports.getMemberExpressionPropertyAsString = getMemberExpressionPropertyAsString; exports.getObjectPropertyAsString = getObjectPropertyAsString; exports.getParentFunctionOrProgram = getParentFunctionOrProgram; exports.getPatternIdentifierNames = getPatternIdentifierNames; exports.isDefiningIdentifier = isDefiningIdentifier; exports.isExportedIdentifier = isExportedIdentifier; exports.isModifiedIdentifier = isModifiedIdentifier; exports.isModuleImport = isModuleImport; exports.isStrictIdentifier = isStrictIdentifier; exports.isStrictMode = isStrictMode; exports.isUndefined = isUndefined; exports.isVariableIdentifier = isVariableIdentifier; exports.prepend = prepend; exports.prependProgram = prependProgram; exports.replaceDefiningIdentifierToMemberExpression = replaceDefiningIdentifierToMemberExpression; var t = _interopRequireWildcard(require("@babel/types")); var _assert = require("assert"); var _node = require("./node"); 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 _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; } function getPatternIdentifierNames(path) { if (Array.isArray(path)) { var allNames = new Set(); var _iterator = _createForOfIteratorHelper(path), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var p = _step.value; var names = getPatternIdentifierNames(p); var _iterator2 = _createForOfIteratorHelper(names), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var name = _step2.value; allNames.add(name); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return allNames; } var names = new Set(); var functionParent = path.find(function (parent) { return parent.isFunction(); }); path.traverse({ BindingIdentifier: function BindingIdentifier(bindingPath) { var bindingFunctionParent = bindingPath.find(function (parent) { return parent.isFunction(); }); if (functionParent === bindingFunctionParent) { names.add(bindingPath.node.name); } } }); // Check if the path itself is a binding identifier if (path.isBindingIdentifier()) { names.add(path.node.name); } return names; } /** * Ensures a `String Literal` is 'computed' before replacing it with a more complex expression. * * ```js * // Input * { * "myToBeEncodedString": "value" * } * * // Output * { * ["myToBeEncodedString"]: "value" * } * ``` * @param path */ function ensureComputedExpression(path) { if ((t.isObjectMember(path.parent) || t.isClassMethod(path.parent) || t.isClassProperty(path.parent)) && path.parent.key === path.node && !path.parent.computed) { path.parent.computed = true; } } /** * Retrieves a function name from debugging purposes. * - Function Declaration / Expression * - Variable Declaration * - Object property / method * - Class property / method * - Program returns "[Program]" * - Default returns "anonymous" * @param path * @returns */ function getFunctionName(path) { var _path$parentPath, _path$parentPath2, _path$parentPath3; if (!path) return "null"; if (path.isProgram()) return "[Program]"; // Check function declaration/expression ID if ((t.isFunctionDeclaration(path.node) || t.isFunctionExpression(path.node)) && path.node.id) { return path.node.id.name; } // Check for containing variable declaration if ((_path$parentPath = path.parentPath) !== null && _path$parentPath !== void 0 && _path$parentPath.isVariableDeclarator() && t.isIdentifier(path.parentPath.node.id)) { return path.parentPath.node.id.name; } if (path.isObjectMethod() || path.isClassMethod()) { var property = getObjectPropertyAsString(path.node); if (property) return property; } // Check for containing property in an object if ((_path$parentPath2 = path.parentPath) !== null && _path$parentPath2 !== void 0 && _path$parentPath2.isObjectProperty() || (_path$parentPath3 = path.parentPath) !== null && _path$parentPath3 !== void 0 && _path$parentPath3.isClassProperty()) { var property = getObjectPropertyAsString(path.parentPath.node); if (property) return property; } var output = "anonymous"; if (path.isFunction()) { if (path.node.generator) { output += "*"; } else if (path.node.async) { output = "async " + output; } } return output; } function isModuleImport(path) { // Import Declaration if (path.parentPath.isImportDeclaration()) { return true; } // Dynamic Import / require() call if (t.isCallExpression(path.parent) && (t.isIdentifier(path.parent.callee, { name: "require" }) || t.isImport(path.parent.callee)) && path.node === path.parent.arguments[0]) { return true; } return false; } function getBlock(path) { return path.find(function (p) { return p.isBlock(); }); } function getParentFunctionOrProgram(path) { if (path.isProgram()) return path; // Find the nearest function-like parent var functionOrProgramPath = path.findParent(function (parentPath) { return parentPath.isFunction() || parentPath.isProgram(); }); (0, _assert.ok)(functionOrProgramPath); return functionOrProgramPath; } function getObjectPropertyAsString(property) { (0, _assert.ok)(t.isObjectMember(property) || t.isClassProperty(property) || t.isClassMethod(property)); if (!property.computed && t.isIdentifier(property.key)) { return property.key.name; } if (t.isStringLiteral(property.key)) { return property.key.value; } if (t.isNumericLiteral(property.key)) { return property.key.value.toString(); } return null; } /** * Gets the property of a MemberExpression as a string. * * @param memberPath - The path of the MemberExpression node. * @returns The property as a string or null if it cannot be determined. */ function getMemberExpressionPropertyAsString(member) { t.assertMemberExpression(member); var property = member.property; if (!member.computed && t.isIdentifier(property)) { return property.name; } if (t.isStringLiteral(property)) { return property.value; } if (t.isNumericLiteral(property)) { return property.value.toString(); } return null; // If the property cannot be determined } function registerPaths(paths) { var _iterator3 = _createForOfIteratorHelper(paths), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var path = _step3.value; if (path.isVariableDeclaration() && path.node.kind === "var") { getParentFunctionOrProgram(path).scope.registerDeclaration(path); } path.scope.registerDeclaration(path); } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return paths; } function nodeListToNodes(nodesIn) { var nodes = []; if (Array.isArray(nodesIn[0])) { (0, _assert.ok)(nodesIn.length === 1); nodes = nodesIn[0]; } else { nodes = nodesIn; } return nodes; } /** * Appends to the bottom of a block. Preserving last expression for the top level. */ function append(path) { for (var _len = arguments.length, nodesIn = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { nodesIn[_key - 1] = arguments[_key]; } var nodes = nodeListToNodes(nodesIn); var listParent = path.find(function (p) { return p.isFunction() || p.isBlock() || p.isSwitchCase(); }); if (!listParent) { throw new Error("Could not find a suitable parent to prepend to"); } if (listParent.isProgram()) { var lastExpression = listParent.get("body").at(-1); if (lastExpression.isExpressionStatement()) { return registerPaths(lastExpression.insertBefore(nodes)); } } if (listParent.isSwitchCase()) { return registerPaths(listParent.pushContainer("consequent", nodes)); } if (listParent.isFunction()) { var body = listParent.get("body"); if (listParent.isArrowFunctionExpression() && listParent.node.expression) { if (!body.isBlockStatement()) { body.replaceWith(t.blockStatement([t.returnStatement(body.node)])); } } (0, _assert.ok)(body.isBlockStatement()); return registerPaths(body.pushContainer("body", nodes)); } (0, _assert.ok)(listParent.isBlock()); return registerPaths(listParent.pushContainer("body", nodes)); } /** * Prepends and registers a list of nodes to the beginning of a block. * * - Preserves import declarations by inserting after the last import declaration. * - Handles arrow functions * - Handles switch cases * @param path * @param nodes * @returns */ function prepend(path) { for (var _len2 = arguments.length, nodesIn = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { nodesIn[_key2 - 1] = arguments[_key2]; } var nodes = nodeListToNodes(nodesIn); var listParent = path.find(function (p) { return p.isFunction() || p.isBlock() || p.isSwitchCase(); }); if (!listParent) { throw new Error("Could not find a suitable parent to prepend to"); } if (listParent.isProgram()) { // Preserve import declarations // Filter out import declarations var _body = listParent.get("body"); var afterImport = 0; var _iterator4 = _createForOfIteratorHelper(_body), _step4; try { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { var stmt = _step4.value; if (!stmt.isImportDeclaration()) { break; } afterImport++; } } catch (err) { _iterator4.e(err); } finally { _iterator4.f(); } if (afterImport === 0) { // No import declarations, so we can safely unshift everything return registerPaths(listParent.unshiftContainer("body", nodes)); } // Insert the nodes after the last import declaration return registerPaths(_body[afterImport - 1].insertAfter(nodes)); } if (listParent.isFunction()) { var body = listParent.get("body"); if (listParent.isArrowFunctionExpression() && listParent.node.expression) { if (!body.isBlockStatement()) { body = body.replaceWith(t.blockStatement([t.returnStatement(body.node)]))[0]; } } (0, _assert.ok)(body.isBlockStatement()); return registerPaths(body.unshiftContainer("body", nodes)); } if (listParent.isBlock()) { return registerPaths(listParent.unshiftContainer("body", nodes)); } if (listParent.isSwitchCase()) { return registerPaths(listParent.unshiftContainer("consequent", nodes)); } (0, _assert.ok)(false); } function prependProgram(path) { var program = path.find(function (p) { return p.isProgram(); }); (0, _assert.ok)(program); (0, _assert.ok)(program.isProgram()); for (var _len3 = arguments.length, nodes = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { nodes[_key3 - 1] = arguments[_key3]; } return prepend.apply(void 0, [program].concat(nodes)); } /** * A referenced or binding identifier, only names that reflect variables. * * - Excludes labels * * @param path * @returns */ function isVariableIdentifier(path) { var _path$parentPath4; if (!path.isReferencedIdentifier() && !path.isBindingIdentifier()) return false; // abc: {} // not a variable identifier if (path.key === "label" && (_path$parentPath4 = path.parentPath) !== null && _path$parentPath4 !== void 0 && _path$parentPath4.isLabeledStatement()) return false; return true; } /** * Subset of BindingIdentifier, excluding non-defined assignment expressions. * * @example * var a = 1; // true * var {c} = {} // true * function b() {} // true * function d([e] = [], ...f) {} // true * * f = 0; // false * f(); // false * @param path * @returns */ function isDefiningIdentifier(path) { if (path.key === "id" && path.parentPath.isFunction()) return true; if (path.key === "id" && path.parentPath.isClassDeclaration) return true; if (path.key === "local" && (path.parentPath.isImportSpecifier() || path.parentPath.isImportDefaultSpecifier() || path.parentPath.isImportNamespaceSpecifier())) return true; var maxTraversalPath = path.find(function (p) { var _p$parentPath, _p$parentPath2, _p$parentPath3; return p.key === "id" && ((_p$parentPath = p.parentPath) === null || _p$parentPath === void 0 ? void 0 : _p$parentPath.isVariableDeclarator()) || p.listKey === "params" && ((_p$parentPath2 = p.parentPath) === null || _p$parentPath2 === void 0 ? void 0 : _p$parentPath2.isFunction()) || p.key === "param" && ((_p$parentPath3 = p.parentPath) === null || _p$parentPath3 === void 0 ? void 0 : _p$parentPath3.isCatchClause()); }); if (!maxTraversalPath) return false; var cursor = path; while (cursor && cursor !== maxTraversalPath) { var _cursor$parentPath$pa; if (cursor.parentPath.isObjectProperty() && (_cursor$parentPath$pa = cursor.parentPath.parentPath) !== null && _cursor$parentPath$pa !== void 0 && _cursor$parentPath$pa.isObjectPattern()) { if (cursor.key !== "value") { return false; } } else if (cursor.parentPath.isArrayPattern()) { if (cursor.listKey !== "elements") { return false; } } else if (cursor.parentPath.isRestElement()) { if (cursor.key !== "argument") { return false; } } else if (cursor.parentPath.isAssignmentPattern()) { if (cursor.key !== "left") { return false; } } else if (cursor.parentPath.isObjectPattern()) {} else return false; cursor = cursor.parentPath; } return true; } /** * @example * function id() {} // true * class id {} // true * var id; // false * @param path * @returns */ function isStrictIdentifier(path) { if (path.key === "id" && (path.parentPath.isFunction() || path.parentPath.isClass())) return true; return false; } function isExportedIdentifier(path) { // Check if the identifier is directly inside an ExportNamedDeclaration if (path.parentPath.isExportNamedDeclaration()) { return true; } // Check if the identifier is in an ExportDefaultDeclaration if (path.parentPath.isExportDefaultDeclaration()) { return true; } // Check if the identifier is within an ExportSpecifier if (path.parentPath.isExportSpecifier() && path.parentPath.parentPath.isExportNamedDeclaration()) { return true; } // Check if it's part of an exported variable declaration (e.g., export const a = 1;) if (path.parentPath.isVariableDeclarator() && path.parentPath.parentPath.parentPath.isExportNamedDeclaration()) { return true; } // Check if it's part of an exported function declaration (e.g., export function abc() {}) if ((path.parentPath.isFunctionDeclaration() || path.parentPath.isClassDeclaration()) && path.parentPath.parentPath.isExportNamedDeclaration()) { return true; } return false; } /** * @example * function abc() { * "use strict"; * } // true * @param path * @returns */ function isStrictMode(path) { // Classes are always in strict mode if (path.isClass()) return true; if (path.isBlock()) { if (path.isTSModuleBlock()) return false; return path.node.directives.some(function (directive) { return directive.value.value === "use strict"; }); } if (path.isFunction()) { var fnBody = path.get("body"); if (fnBody.isBlock()) { return isStrictMode(fnBody); } } return false; } /** * A modified identifier is an identifier that is assigned to or updated. * * - Assignment Expression * - Update Expression * * @param identifierPath */ function isModifiedIdentifier(identifierPath) { var isModification = false; if (identifierPath.parentPath.isUpdateExpression()) { isModification = true; } if (identifierPath.find(function (p) { var _p$parentPath4; return p.key === "left" && ((_p$parentPath4 = p.parentPath) === null || _p$parentPath4 === void 0 ? void 0 : _p$parentPath4.isAssignmentExpression()); })) { isModification = true; } return isModification; } function replaceDefiningIdentifierToMemberExpression(path, memberExpression) { // function id(){} -> var id = function() {} if (path.key === "id" && path.parentPath.isFunctionDeclaration()) { var asFunctionExpression = (0, _node.deepClone)(path.parentPath.node); asFunctionExpression.type = "FunctionExpression"; path.parentPath.replaceWith(t.expressionStatement(t.assignmentExpression("=", memberExpression, asFunctionExpression))); return; } // class id{} -> var id = class {} if (path.key === "id" && path.parentPath.isClassDeclaration()) { var asClassExpression = (0, _node.deepClone)(path.parentPath.node); asClassExpression.type = "ClassExpression"; path.parentPath.replaceWith(t.expressionStatement(t.assignmentExpression("=", memberExpression, asClassExpression))); return; } // var id = 1 -> id = 1 var variableDeclaratorChild = path.find(function (p) { var _p$parentPath5, _p$parentPath6; return p.key === "id" && ((_p$parentPath5 = p.parentPath) === null || _p$parentPath5 === void 0 ? void 0 : _p$parentPath5.isVariableDeclarator()) && ((_p$parentPath6 = p.parentPath) === null || _p$parentPath6 === void 0 || (_p$parentPath6 = _p$parentPath6.parentPath) === null || _p$parentPath6 === void 0 ? void 0 : _p$parentPath6.isVariableDeclaration()); }); if (variableDeclaratorChild) { var variableDeclarator = variableDeclaratorChild.parentPath; var variableDeclaration = variableDeclarator.parentPath; if (variableDeclaration.type === "VariableDeclaration") { (0, _assert.ok)(variableDeclaration.node.declarations.length === 1, "Multiple declarations not supported"); } var id = variableDeclarator.get("id"); var init = variableDeclarator.get("init"); var newExpression = id.node; var isForInitializer = (variableDeclaration.key === "init" || variableDeclaration.key === "left") && variableDeclaration.parentPath.isFor(); if (init.node || !isForInitializer) { newExpression = t.assignmentExpression("=", id.node, init.node || t.identifier("undefined")); } if (!isForInitializer) { newExpression = t.expressionStatement(newExpression); } path.replaceWith(memberExpression); if (variableDeclaration.isVariableDeclaration()) { variableDeclaration.replaceWith(newExpression); } return; } // Safely replace the identifier with the member expression // ensureComputedExpression(path); // path.replaceWith(memberExpression); } /** * @example * undefined // true * void 0 // true */ function isUndefined(path) { if (path.isIdentifier() && path.node.name === "undefined") { return true; } if (path.isUnaryExpression() && path.node.operator === "void" && path.node.argument.type === "NumericLiteral" && path.node.argument.value === 0) { return true; } return false; }