UNPKG

js-confuser

Version:

JavaScript Obfuscation Tool.

303 lines (285 loc) 17 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 _randomUtils = require("../../utils/random-utils"); var _template = _interopRequireDefault(require("../../templates/template")); var t = _interopRequireWildcard(require("@babel/types")); var _astUtils = require("../../utils/ast-utils"); var _integrity = require("./integrity"); var _integrityTemplate = require("../../templates/integrityTemplate"); var _constants = require("../../constants"); var _tamperProtectionTemplates = require("../../templates/tamperProtectionTemplates"); 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 _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 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 _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.Lock, { changeData: { locksInserted: 0 } }); if (me.options.lock.startDate instanceof Date) { // Ensure date is in the past if (me.options.lock.startDate.getTime() > Date.now()) { me.warn("lock.startDate is detected to be in the future"); } me.options.lock.customLocks.push({ code: ["\n if(Date.now()<".concat(me.options.lock.startDate.getTime(), ") {\n {countermeasures}\n }\n "), "\n if((new Date()).getTime()<".concat(me.options.lock.startDate.getTime(), ") {\n {countermeasures}\n }\n ")], percentagePerBlock: 0.5 }); } if (me.options.lock.endDate instanceof Date) { // Ensure date is in the future if (me.options.lock.endDate.getTime() < Date.now()) { me.warn("lock.endDate is detected to be in the past"); } me.options.lock.customLocks.push({ code: ["\n if(Date.now()>".concat(me.options.lock.endDate.getTime(), ") {\n {countermeasures}\n }\n "), "\n if((new Date()).getTime()>".concat(me.options.lock.endDate.getTime(), ") {\n {countermeasures}\n }\n ")], percentagePerBlock: 0.5 }); } if (me.options.lock.domainLock) { var domainArray = Array.isArray(me.options.lock.domainLock) ? me.options.lock.domainLock : [me.options.lock.domainLock]; var _iterator = _createForOfIteratorHelper(domainArray), _step; try { var _loop = function _loop() { var _regexString = _step.value; me.options.lock.customLocks.push({ code: new _template["default"]("\n if(!new RegExp({regexString}).test(window.location.href)) {\n {countermeasures}\n }\n ").setDefaultVariables({ regexString: function regexString() { return t.stringLiteral(_regexString.toString()); } }), percentagePerBlock: 0.5 }); }; for (_iterator.s(); !(_step = _iterator.n()).done;) { _loop(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } if (me.options.lock.selfDefending) { me.options.lock.customLocks.push({ code: "\n (\n function(){\n // Breaks any code formatter\n var namedFunction = function(){\n const test = function(){\n const regExp=new RegExp('\\n');\n return regExp['test'](namedFunction)\n };\n\n if(test()) {\n {countermeasures}\n }\n }\n\n return namedFunction();\n }\n )();\n ", percentagePerBlock: 0.5 }); } if (me.options.lock.antiDebug) { me.options.lock.customLocks.push({ code: "\n debugger;\n ", percentagePerBlock: 0.5 }); } var timesMap = new WeakMap(); var countermeasuresNode; var invokeCountermeasuresFnName; if (me.options.lock.countermeasures) { invokeCountermeasuresFnName = me.getPlaceholder("invokeCountermeasures"); me.globalState.internals.invokeCountermeasuresFnName = invokeCountermeasuresFnName; } var createCountermeasuresCode = function createCountermeasuresCode() { if (invokeCountermeasuresFnName) { return new _template["default"]("".concat(invokeCountermeasuresFnName, "()")).compile(); } if (me.options.lock.countermeasures === false) { return []; } return new _template["default"]("while(true){}").compile(); }; me.globalState.lock.createCountermeasuresCode = createCountermeasuresCode; function applyLockToBlock(path, customLock) { var times = timesMap.get(customLock); if (typeof times === "undefined") { times = 0; } var maxCount = customLock.maxCount || 100; // 100 is default max count var minCount = customLock.minCount || 1; // 1 is default min count if (maxCount >= 0 && times > maxCount) { // Limit creation, allowing -1 to disable the limit entirely return; } // The Program always gets a lock // Else based on the percentage // Try to reach the minimum count if (!path.isProgram() && !(0, _randomUtils.chance)(customLock.percentagePerBlock * 100) && times >= minCount) { return; } // Increment the times timesMap.set(customLock, times + 1); var lockCode = Array.isArray(customLock.code) ? (0, _randomUtils.choice)(customLock.code) : customLock.code; var template = typeof lockCode === "string" ? new _template["default"](lockCode) : lockCode; var lockNodes = template.compile({ countermeasures: function countermeasures() { return createCountermeasuresCode(); } }); var p = path.unshiftContainer("body", lockNodes); p.forEach(function (p) { return p.skip(); }); me.changeData.locksInserted++; } return { visitor: { BindingIdentifier: function BindingIdentifier(path) { if (path.node.name !== me.options.lock.countermeasures) { return; } // Exclude labels if (!(0, _astUtils.isVariableIdentifier)(path)) return; if (!(0, _astUtils.isDefiningIdentifier)(path)) { // Reassignments are not allowed me.error("Countermeasures function cannot be reassigned"); } if (countermeasuresNode) { // Disallow multiple countermeasures functions me.error("Countermeasures function was already defined, it must have a unique name from the rest of your code"); } if (path.scope.getBinding(path.node.name).scope !== path.scope.getProgramParent()) { me.error("Countermeasures function must be defined at the global level"); } countermeasuresNode = path; }, Block: { exit: function exit(path) { var customLock = (0, _randomUtils.choice)(me.options.lock.customLocks); if (customLock) { applyLockToBlock(path, customLock); } } }, Program: { exit: function exit(path) { // Insert nativeFunctionCheck if (me.options.lock.tamperProtection) { // Disallow strict mode // Tamper Protection uses non-strict mode features: // - eval() with local scope assignments var directives = path.get("directives"); var _iterator2 = _createForOfIteratorHelper(directives), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var directive = _step2.value; if (directive.node.value.value === "use strict") { me.error("Tamper Protection cannot be applied to code in strict mode. Disable strict mode by removing the 'use strict' directive, or disable Tamper Protection."); } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } var nativeFunctionName = me.getPlaceholder() + "_nativeFunctionCheck"; me.obfuscator.globalState.internals.nativeFunctionName = nativeFunctionName; // Ensure program is not in strict mode // Tamper Protection forces non-strict mode (0, _astUtils.prependProgram)(path, _tamperProtectionTemplates.StrictModeTemplate.compile({ nativeFunctionName: nativeFunctionName, countermeasures: createCountermeasuresCode() })); var nativeFunctionDeclaration = _tamperProtectionTemplates.NativeFunctionTemplate.single({ nativeFunctionName: nativeFunctionName, countermeasures: createCountermeasuresCode(), IndexOfTemplate: _tamperProtectionTemplates.IndexOfTemplate }); // Checks function's toString() value for [native code] signature (0, _astUtils.prependProgram)(path, nativeFunctionDeclaration); } // Insert invokeCountermeasures function if (invokeCountermeasuresFnName) { if (!countermeasuresNode) { me.error("Countermeasures function named '" + me.options.lock.countermeasures + "' was not found."); } var hasInvoked = me.getPlaceholder("hasInvoked"); var statements = new _template["default"]("\n var ".concat(hasInvoked, " = false;\n function ").concat(invokeCountermeasuresFnName, "(){\n if(").concat(hasInvoked, ") return;\n ").concat(hasInvoked, " = true;\n ").concat(me.options.lock.countermeasures, "();\n }\n ")).addSymbols(_constants.MULTI_TRANSFORM).compile(); (0, _astUtils.prependProgram)(path, statements).forEach(function (p) { return p.skip(); }); } if (me.options.lock.integrity) { var hashFnName = me.getPlaceholder() + "_hash"; var imulFnName = me.getPlaceholder() + "_imul"; var _sensitivityRegex = me.globalState.lock.integrity.sensitivityRegex; me.globalState.internals.integrityHashName = hashFnName; var hashCode = _integrityTemplate.HashTemplate.compile({ imul: imulFnName, name: hashFnName, hashingUtilFnName: me.getPlaceholder(), sensitivityRegex: function sensitivityRegex() { return t.newExpression(t.identifier("RegExp"), [t.stringLiteral(_sensitivityRegex.source), t.stringLiteral(_sensitivityRegex.flags)]); } }); (0, _astUtils.prependProgram)(path, hashCode); } } }, // Integrity first pass // Functions are prepared for Integrity by simply extracting the function body // The extracted function is hashed in the 'integrity' plugin FunctionDeclaration: { exit: function exit(funcDecPath) { if (!me.options.lock.integrity) return; // Mark functions for integrity // Don't apply to async or generator functions if (funcDecPath.node.async || funcDecPath.node.generator) return; if (funcDecPath.find(function (p) { return !!p.node[_constants.SKIP]; })) return; var program = (0, _astUtils.getParentFunctionOrProgram)(funcDecPath); // Only top-level functions if (!program.isProgram()) return; // Check user's custom implementation var functionName = (0, _astUtils.getFunctionName)(funcDecPath); // Don't apply to the countermeasures function (Intended) if (me.options.lock.countermeasures && functionName === me.options.lock.countermeasures) return; // Don't apply to invokeCountermeasures function (Intended) if (me.obfuscator.isInternalVariable(functionName)) return; if (!me.computeProbabilityMap(me.options.lock.integrity, functionName)) return; var newFnName = me.getPlaceholder(); var newFunctionDeclaration = t.functionDeclaration(t.identifier(newFnName), funcDecPath.node.params, funcDecPath.node.body); // Clone semantic symbols like (UNSAFE, PREDICTABLE, MULTI_TRANSFORM, etc) var source = funcDecPath.node; Object.getOwnPropertySymbols(source).forEach(function (symbol) { newFunctionDeclaration[symbol] = source[symbol]; }); newFunctionDeclaration[_constants.SKIP] = true; var _program$unshiftConta = program.unshiftContainer("body", newFunctionDeclaration), _program$unshiftConta2 = _slicedToArray(_program$unshiftConta, 1), newFnPath = _program$unshiftConta2[0]; // Function simply calls the new function // In the case Integrity cannot transform the function, the original behavior is preserved funcDecPath.node.body = t.blockStatement(new _template["default"]("\n return ".concat(newFnName, "(...arguments);\n ")).compile(), funcDecPath.node.body.directives); // Parameters no longer needed, using 'arguments' instead funcDecPath.node.params = []; // Mark the function as unsafe - use of 'arguments' is unsafe funcDecPath.node[_constants.UNSAFE] = true; // Params changed - function is no longer predictable funcDecPath.node[_constants.PREDICTABLE] = false; // Mark the function for integrity funcDecPath.node[_integrity.INTEGRITY] = { fnPath: newFnPath, fnName: newFnName }; } } } }; };