js-confuser
Version:
JavaScript Obfuscation Tool.
303 lines (285 loc) • 17 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 _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
};
}
}
}
};
};
;