js-confuser
Version:
JavaScript Obfuscation Tool.
575 lines (548 loc) • 21.6 kB
JavaScript
"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;
}