diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2ef345..5ca99c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ Change Log === +v0.8.1 +--- +* New option `seed` sets seed for random generator. This is useful for creating repeatable results. +* IE8 runtime error fix. + v0.8.1 --- * `disableConsoleOutput` option now replaces `console.xxx` functions on empty function instead of infinity loop. diff --git a/README.md b/README.md index 3d77f5fe..800938d2 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Following options available for the JS Obfuscator: disableConsoleOutput: true, reservedNames: [], rotateStringArray: true, + seed: 0, selfDefending: true, sourceMap: false, sourceMapBaseUrl: '', @@ -148,6 +149,7 @@ Following options available for the JS Obfuscator: --disableConsoleOutput --reservedNames (comma separated) --rotateStringArray + --seed --selfDefending --sourceMap --sourceMapBaseUrl @@ -218,6 +220,10 @@ Shift the `stringArray` array by a fixed and random (generated at the code obfus This option is recommended if your original source code isn't small, as the helper function can attract attention. +### `seed` +Type: `number` Default: `0` + +This option sets seed for random generator. This is useful for creating repeatable results. ### `selfDefending` Type: `boolean` Default: `true` diff --git a/dist/index.js b/dist/index.js index 48c1c2c0..c632712d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -88,7 +88,7 @@ module.exports = /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 89); +/******/ return __webpack_require__(__webpack_require__.s = 88); /******/ }) /************************************************************************/ /******/ ([ @@ -167,11 +167,32 @@ var Utils = function () { domain = domain.split(':')[0]; return domain; } + }, { + key: 'getRandomFloat', + value: function getRandomFloat(min, max) { + return Utils.getRandomGenerator().floating({ + min: min, + max: max, + fixed: 7 + }); + } }, { key: 'getRandomGenerator', value: function getRandomGenerator() { + var randomGenerator = Utils.randomGenerator; + if (!randomGenerator) { + throw new Error('`randomGenerator` static property is undefined'); + } return Utils.randomGenerator; } + }, { + key: 'getRandomInteger', + value: function getRandomInteger(min, max) { + return Utils.getRandomGenerator().integer({ + min: min, + max: max + }); + } }, { key: 'getRandomVariableName', value: function getRandomVariableName() { @@ -180,10 +201,7 @@ var Utils = function () { var rangeMinInteger = 10000, rangeMaxInteger = 99999999, prefix = '_0x'; - return '' + prefix + Utils.decToHex(Utils.getRandomGenerator().integer({ - min: rangeMinInteger, - max: rangeMaxInteger - })).substr(0, length); + return '' + prefix + Utils.decToHex(Utils.getRandomInteger(rangeMinInteger, rangeMaxInteger)).substr(0, length); } }, { key: 'hideString', @@ -196,7 +214,7 @@ var Utils = function () { i2 = -1, result = ''; while (i1 < s1.length || i2 < s2.length) { - if (Math.random() < 0.5 && i2 < s2.length) { + if (Utils.getRandomFloat(0, 1) < 0.5 && i2 < s2.length) { result += s2.charAt(++i2); } else { result += s1.charAt(++i1); @@ -245,6 +263,11 @@ var Utils = function () { } return result; } + }, { + key: 'setRandomGenerator', + value: function setRandomGenerator(randomGenerator) { + Utils.randomGenerator = randomGenerator; + } }, { key: 'strEnumify', value: function strEnumify(obj) { @@ -640,10 +663,7 @@ var NodeAppender = function () { }, { key: 'getRandomStackTraceIndex', value: function getRandomStackTraceIndex(stackTraceRootLength) { - return Utils_1.Utils.getRandomGenerator().integer({ - min: 0, - max: Math.max(0, Math.round(stackTraceRootLength - 1)) - }); + return Utils_1.Utils.getRandomInteger(0, Math.max(0, Math.round(stackTraceRootLength - 1))); } }, { key: 'insertNodeAtIndex', @@ -1003,6 +1023,7 @@ exports.NO_CUSTOM_NODES_PRESET = Object.freeze({ domainLock: [], reservedNames: [], rotateStringArray: false, + seed: 0, selfDefending: false, sourceMap: false, sourceMapBaseUrl: '', @@ -1070,7 +1091,9 @@ var NodeCallsControllerFunctionNode = function (_AbstractCustomNode_) { if (this.appendState === AppendState_1.AppendState.AfterObfuscation) { return NodeUtils_1.NodeUtils.convertCodeToStructure(JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate().formatUnicorn({ singleNodeCallControllerFunctionName: this.callsControllerFunctionName - }), NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET).getObfuscatedCode()); + }), Object.assign({}, NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + })).getObfuscatedCode()); } return NodeUtils_1.NodeUtils.convertCodeToStructure(SingleNodeCallControllerTemplate_1.SingleNodeCallControllerTemplate().formatUnicorn({ singleNodeCallControllerFunctionName: this.callsControllerFunctionName @@ -1127,7 +1150,7 @@ var StringLiteralReplacer = function (_AbstractReplacer_1$A) { _createClass(StringLiteralReplacer, [{ key: 'replace', value: function replace(nodeValue) { - var replaceWithStringArrayFlag = nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray && Math.random() <= this.options.stringArrayThreshold; + var replaceWithStringArrayFlag = nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray && Utils_1.Utils.getRandomFloat(0, 1) <= this.options.stringArrayThreshold; if (this.options.stringArray && replaceWithStringArrayFlag) { return this.replaceStringLiteralWithStringArrayCall(nodeValue); } @@ -1233,7 +1256,6 @@ exports.ObfuscationResult = ObfuscationResult; "use strict"; exports.JSFuck = { - Window: '[]["filter"]["constructor"]("return this")()', False: '![]', True: '!![]', a: '(false+"")[1]', @@ -1349,6 +1371,7 @@ exports.DEFAULT_PRESET = Object.freeze({ domainLock: [], reservedNames: [], rotateStringArray: true, + seed: 0, selfDefending: true, sourceMap: false, sourceMapBaseUrl: '', @@ -1391,10 +1414,12 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons var esprima = __webpack_require__(24); var escodegen = __webpack_require__(13); +var chance_1 = __webpack_require__(82); var ObfuscationResult_1 = __webpack_require__(20); var Obfuscator_1 = __webpack_require__(28); var Options_1 = __webpack_require__(57); var SourceMapCorrector_1 = __webpack_require__(29); +var Utils_1 = __webpack_require__(0); var JavaScriptObfuscatorInternal = function () { function JavaScriptObfuscatorInternal(sourceCode) { @@ -1417,6 +1442,9 @@ var JavaScriptObfuscatorInternal = function () { var astTree = esprima.parse(this.sourceCode, { loc: true }); + if (this.options.seed !== 0) { + Utils_1.Utils.setRandomGenerator(new chance_1.Chance(this.options.seed)); + } astTree = new Obfuscator_1.Obfuscator(this.options).obfuscateNode(astTree); this.generatorOutput = JavaScriptObfuscatorInternal.generateCode(this.sourceCode, astTree, this.options); } @@ -1842,7 +1870,7 @@ var JavaScriptObfuscatorCLI = function () { return val.split(','); }).option('--reservedNames ', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', function (val) { return val.split(','); - }).option('--rotateStringArray ', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean).option('--selfDefending ', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMap ', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMapBaseUrl ', 'Sets base url to the source map import url when `--sourceMapMode=separate`').option('--sourceMapFileName ', 'Sets file name for output source map when `--sourceMapMode=separate`').option('--sourceMapMode [inline, separate]', 'Specify source map output mode', JavaScriptObfuscatorCLI.parseSourceMapMode).option('--stringArray ', 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', JavaScriptObfuscatorCLI.parseBoolean).option('--stringArrayEncoding [true, false, base64, rc4]', 'Encodes all strings in strings array using base64 or rc4 (this option can slow down your code speed', JavaScriptObfuscatorCLI.parseStringArrayEncoding).option('--stringArrayThreshold ', 'The probability that the literal string will be inserted into stringArray (Default: 0.8, Min: 0, Max: 1)', parseFloat).option('--unicodeEscapeSequence ', 'Allows to enable/disable string conversion to unicode escape sequence', JavaScriptObfuscatorCLI.parseBoolean).parse(this.rawArguments); + }).option('--rotateStringArray ', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean).option('--seed ', 'Sets seed for random generator. This is useful for creating repeatable results.', parseFloat).option('--selfDefending ', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMap ', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean).option('--sourceMapBaseUrl ', 'Sets base url to the source map import url when `--sourceMapMode=separate`').option('--sourceMapFileName ', 'Sets file name for output source map when `--sourceMapMode=separate`').option('--sourceMapMode [inline, separate]', 'Specify source map output mode', JavaScriptObfuscatorCLI.parseSourceMapMode).option('--stringArray ', 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', JavaScriptObfuscatorCLI.parseBoolean).option('--stringArrayEncoding [true, false, base64, rc4]', 'Encodes all strings in strings array using base64 or rc4 (this option can slow down your code speed', JavaScriptObfuscatorCLI.parseStringArrayEncoding).option('--stringArrayThreshold ', 'The probability that the literal string will be inserted into stringArray (Default: 0.8, Min: 0, Max: 1)', parseFloat).option('--unicodeEscapeSequence ', 'Allows to enable/disable string conversion to unicode escape sequence', JavaScriptObfuscatorCLI.parseBoolean).parse(this.rawArguments); this.commands.on('--help', function () { console.log(' Examples:\n'); console.log(' %> javascript-obfuscator in.js --compact true --selfDefending false'); @@ -2131,10 +2159,7 @@ var DebugProtectionFunctionNode = function (_AbstractCustomNode_) { key: 'appendNode', value: function appendNode(blockScopeNode) { var programBodyLength = blockScopeNode.body.length, - randomIndex = Utils_1.Utils.getRandomGenerator().integer({ - min: 0, - max: programBodyLength - }); + randomIndex = Utils_1.Utils.getRandomInteger(0, programBodyLength); NodeAppender_1.NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), randomIndex); } }, { @@ -2273,7 +2298,9 @@ var SelfDefendingUnicodeNode = function (_AbstractCustomNode_) { return NodeUtils_1.NodeUtils.convertCodeToStructure(JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(SelfDefendingTemplate_1.SelfDefendingTemplate().formatUnicorn({ selfDefendingFunctionName: Utils_1.Utils.getRandomVariableName(), singleNodeCallControllerFunctionName: this.callsControllerFunctionName - }), NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET).getObfuscatedCode()); + }), Object.assign({}, NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + })).getObfuscatedCode()); } }]); @@ -2385,7 +2412,9 @@ var StringArrayCallsWrapper = function (_AbstractCustomNode_) { decodeNodeTemplate: decodeNodeTemplate, stringArrayCallsWrapperName: this.stringArrayCallsWrapperName, stringArrayName: this.stringArrayName - }), NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET).getObfuscatedCode()); + }), Object.assign({}, NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + })).getObfuscatedCode()); } }]); @@ -2557,7 +2586,9 @@ var StringArrayRotateFunctionNode = function (_AbstractCustomNode_) { stringArrayName: this.stringArrayName, stringArrayRotateValue: Utils_1.Utils.decToHex(this.stringArrayRotateValue), whileFunctionName: whileFunctionName - }), NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET).getObfuscatedCode()); + }), Object.assign({}, NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + })).getObfuscatedCode()); } }]); @@ -2805,10 +2836,7 @@ var StringArrayNodesGroup = function (_AbstractNodesGroup_) { return; } if (this.options.rotateStringArray) { - this.stringArrayRotateValue = Utils_1.Utils.getRandomGenerator().integer({ - min: 100, - max: 500 - }); + this.stringArrayRotateValue = Utils_1.Utils.getRandomInteger(100, 500); } else { this.stringArrayRotateValue = 0; } @@ -3593,6 +3621,7 @@ __decorate([class_validator_1.IsArray(), class_validator_1.ArrayUnique(), class_ each: true }), __metadata('design:type', Array)], Options.prototype, "reservedNames", void 0); __decorate([class_validator_1.IsBoolean(), __metadata('design:type', Boolean)], Options.prototype, "rotateStringArray", void 0); +__decorate([class_validator_1.IsNumber(), __metadata('design:type', Number)], Options.prototype, "seed", void 0); __decorate([class_validator_1.IsBoolean(), __metadata('design:type', Boolean)], Options.prototype, "selfDefending", void 0); __decorate([class_validator_1.IsBoolean(), __metadata('design:type', Boolean)], Options.prototype, "sourceMap", void 0); __decorate([class_validator_1.IsString(), class_validator_1.ValidateIf(function (options) { @@ -4271,7 +4300,7 @@ exports.DebugProtectionFunctionIntervalTemplate = DebugProtectionFunctionInterva var Utils_1 = __webpack_require__(0); function DebugProtectionFunctionTemplate() { - return '\n var {debugProtectionFunctionName} = function () {\n function debuggerProtection (counter) {\n if ((\'\' + counter / counter)[\'length\'] !== 1 || counter % 20 === 0) {\n (function () {}.constructor(\'debugger\')());\n } else {\n [].filter.constructor(' + Utils_1.Utils.stringToJSFuck('debugger') + ')();\n }\n \n debuggerProtection(++counter);\n }\n \n try {\n debuggerProtection(0);\n } catch (y) {}\n };\n '; + return '\n var {debugProtectionFunctionName} = function () {\n function debuggerProtection (counter) {\n if ((\'\' + counter / counter)[\'length\'] !== 1 || counter % 20 === 0) {\n (function () {}.constructor(' + Utils_1.Utils.stringToJSFuck('debugger') + ')());\n } else {\n (function () {}.constructor(' + Utils_1.Utils.stringToJSFuck('debugger') + ')());\n }\n \n debuggerProtection(++counter);\n }\n \n try {\n debuggerProtection(0);\n } catch (y) {}\n };\n '; } exports.DebugProtectionFunctionTemplate = DebugProtectionFunctionTemplate; @@ -4432,8 +4461,7 @@ module.exports = require("mkdirp"); /***/ }, /* 87 */, -/* 88 */, -/* 89 */ +/* 88 */ /***/ function(module, exports, __webpack_require__) { "use strict"; diff --git a/package.json b/package.json index 09a4eeeb..5639b725 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "javascript-obfuscator", - "version": "0.8.1", + "version": "0.8.2", "description": "JavaScript obfuscator", "keywords": [ "obfuscator", diff --git a/src/JavaScriptObfuscatorInternal.ts b/src/JavaScriptObfuscatorInternal.ts index 4c32e958..216ff93a 100644 --- a/src/JavaScriptObfuscatorInternal.ts +++ b/src/JavaScriptObfuscatorInternal.ts @@ -2,6 +2,8 @@ import * as esprima from 'esprima'; import * as escodegen from 'escodegen'; import * as ESTree from 'estree'; +import { Chance } from 'chance'; + import { IObfuscatorOptions } from './interfaces/IObfuscatorOptions'; import { IGeneratorOutput } from './interfaces/IGeneratorOutput'; import { IObfuscationResult } from './interfaces/IObfuscationResult'; @@ -11,6 +13,7 @@ import { ObfuscationResult } from './ObfuscationResult'; import { Obfuscator } from './Obfuscator'; import { Options } from './options/Options'; import { SourceMapCorrector } from './SourceMapCorrector'; +import { Utils } from './Utils'; export class JavaScriptObfuscatorInternal { /** @@ -91,6 +94,10 @@ export class JavaScriptObfuscatorInternal { loc: true }); + if (this.options.seed !== 0) { + Utils.setRandomGenerator(new Chance(this.options.seed)); + } + astTree = new Obfuscator(this.options).obfuscateNode(astTree); this.generatorOutput = JavaScriptObfuscatorInternal.generateCode(this.sourceCode, astTree, this.options); diff --git a/src/Utils.ts b/src/Utils.ts index 02d14e1c..45bdf93f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -4,9 +4,9 @@ import { JSFuck } from './enums/JSFuck'; export class Utils { /** - * @type {Chance.Chance} + * @type {Chance.Chance | Chance.SeededChance} */ - private static randomGenerator: Chance.Chance = new Chance(); + private static randomGenerator: Chance.Chance | Chance.SeededChance = new Chance(); /** * @param array @@ -99,13 +99,44 @@ export class Utils { return domain; } + /** + * @param min + * @param max + * @returns {number} + */ + public static getRandomFloat (min: number, max: number): number { + return Utils.getRandomGenerator().floating({ + min: min, + max: max, + fixed: 7 + }); + } + /** * @returns {Chance.Chance} */ public static getRandomGenerator (): Chance.Chance { + const randomGenerator: Chance.Chance = Utils.randomGenerator; + + if (!randomGenerator) { + throw new Error(`\`randomGenerator\` static property is undefined`); + } + return Utils.randomGenerator; } + /** + * @param min + * @param max + * @returns {number} + */ + public static getRandomInteger (min: number, max: number): number { + return Utils.getRandomGenerator().integer({ + min: min, + max: max + }); + } + /** * @param length * @returns {string} @@ -117,10 +148,7 @@ export class Utils { return `${prefix}${( Utils.decToHex( - Utils.getRandomGenerator().integer({ - min: rangeMinInteger, - max: rangeMaxInteger - }) + Utils.getRandomInteger(rangeMinInteger, rangeMaxInteger) ) ).substr(0, length)}`; } @@ -140,7 +168,7 @@ export class Utils { result: string = ''; while (i1 < s1.length || i2 < s2.length) { - if (Math.random() < 0.5 && i2 < s2.length) { + if (Utils.getRandomFloat(0, 1) < 0.5 && i2 < s2.length) { result += s2.charAt(++i2); } else { result += s1.charAt(++i1); @@ -213,6 +241,13 @@ export class Utils { return result; } + /** + * @param randomGenerator + */ + public static setRandomGenerator (randomGenerator: Chance.Chance | Chance.SeededChance): void { + Utils.randomGenerator = randomGenerator; + } + /** * @param obj * @returns {T} diff --git a/src/cli/JavaScriptObfuscatorCLI.ts b/src/cli/JavaScriptObfuscatorCLI.ts index 6776daba..163d47bf 100644 --- a/src/cli/JavaScriptObfuscatorCLI.ts +++ b/src/cli/JavaScriptObfuscatorCLI.ts @@ -151,6 +151,7 @@ export class JavaScriptObfuscatorCLI { .option('--domainLock ', 'Blocks the execution of the code in domains that do not match the passed RegExp patterns (comma separated)', (val: string) => val.split(',')) .option('--reservedNames ', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', (val: string) => val.split(',')) .option('--rotateStringArray ', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean) + .option('--seed ', 'Sets seed for random generator. This is useful for creating repeatable results.', parseFloat) .option('--selfDefending ', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean) .option('--sourceMap ', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean) .option('--sourceMapBaseUrl ', 'Sets base url to the source map import url when `--sourceMapMode=separate`') diff --git a/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts b/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts index e5444285..4a2b0b7f 100644 --- a/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts +++ b/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts @@ -40,10 +40,7 @@ export class DebugProtectionFunctionNode extends AbstractCustomNode { */ public appendNode (blockScopeNode: TNodeWithBlockStatement): void { let programBodyLength: number = blockScopeNode.body.length, - randomIndex: number = Utils.getRandomGenerator().integer({ - min: 0, - max: programBodyLength - }); + randomIndex: number = Utils.getRandomInteger(0, programBodyLength); NodeAppender.insertNodeAtIndex(blockScopeNode, this.getNode(), randomIndex); } diff --git a/src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts b/src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts index 338d4122..09db5591 100644 --- a/src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts +++ b/src/custom-nodes/node-calls-controller-nodes/NodeCallsControllerFunctionNode.ts @@ -79,9 +79,14 @@ export class NodeCallsControllerFunctionNode extends AbstractCustomNode { protected getNodeStructure (): TStatement[] { if (this.appendState === AppendState.AfterObfuscation) { return NodeUtils.convertCodeToStructure( - JavaScriptObfuscator.obfuscate(SingleNodeCallControllerTemplate().formatUnicorn({ - singleNodeCallControllerFunctionName: this.callsControllerFunctionName - }), NO_CUSTOM_NODES_PRESET).getObfuscatedCode() + JavaScriptObfuscator.obfuscate( + SingleNodeCallControllerTemplate().formatUnicorn({ + singleNodeCallControllerFunctionName: this.callsControllerFunctionName + }), + Object.assign({}, NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + }) + ).getObfuscatedCode() ); } diff --git a/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts b/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts index 0562a13e..c90dae5b 100644 --- a/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts +++ b/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts @@ -78,7 +78,9 @@ export class SelfDefendingUnicodeNode extends AbstractCustomNode { selfDefendingFunctionName: Utils.getRandomVariableName(), singleNodeCallControllerFunctionName: this.callsControllerFunctionName }), - NO_CUSTOM_NODES_PRESET + Object.assign({}, NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + }) ).getObfuscatedCode() ); } diff --git a/src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts b/src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts index 849dfab5..3d1b0ab5 100644 --- a/src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts +++ b/src/custom-nodes/string-array-nodes/StringArrayCallsWrapper.ts @@ -139,7 +139,9 @@ export class StringArrayCallsWrapper extends AbstractCustomNode { stringArrayCallsWrapperName: this.stringArrayCallsWrapperName, stringArrayName: this.stringArrayName }), - NO_CUSTOM_NODES_PRESET + Object.assign({}, NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + }) ).getObfuscatedCode() ); } diff --git a/src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts b/src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts index 9f00be66..822e07a2 100644 --- a/src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts +++ b/src/custom-nodes/string-array-nodes/StringArrayRotateFunctionNode.ts @@ -103,7 +103,9 @@ export class StringArrayRotateFunctionNode extends AbstractCustomNode { stringArrayRotateValue: Utils.decToHex(this.stringArrayRotateValue), whileFunctionName }), - NO_CUSTOM_NODES_PRESET + Object.assign({}, NO_CUSTOM_NODES_PRESET, { + seed: this.options.seed + }) ).getObfuscatedCode() ); } diff --git a/src/enums/JSFuck.ts b/src/enums/JSFuck.ts index f211463f..6fe31a6c 100644 --- a/src/enums/JSFuck.ts +++ b/src/enums/JSFuck.ts @@ -1,6 +1,4 @@ export const JSFuck: any = { - Window: '[]["filter"]["constructor"]("return this")()', - False: '![]', True: '!![]', diff --git a/src/interfaces/IObfuscatorOptions.d.ts b/src/interfaces/IObfuscatorOptions.d.ts index eef2e4b1..e305307b 100644 --- a/src/interfaces/IObfuscatorOptions.d.ts +++ b/src/interfaces/IObfuscatorOptions.d.ts @@ -9,6 +9,7 @@ export interface IObfuscatorOptions { domainLock?: string[]; reservedNames?: string[]; rotateStringArray?: boolean; + seed?: number; selfDefending?: boolean; sourceMap?: boolean; sourceMapBaseUrl?: string; diff --git a/src/interfaces/IOptions.d.ts b/src/interfaces/IOptions.d.ts index 14757a3b..1e499157 100644 --- a/src/interfaces/IOptions.d.ts +++ b/src/interfaces/IOptions.d.ts @@ -9,6 +9,7 @@ export interface IOptions { readonly domainLock: string[]; readonly reservedNames: string[]; readonly rotateStringArray: boolean; + readonly seed: number; readonly selfDefending: boolean; readonly sourceMap: boolean; readonly sourceMapBaseUrl: string; diff --git a/src/node-groups/StringArrayNodesGroup.ts b/src/node-groups/StringArrayNodesGroup.ts index 242e084c..3cb42f68 100644 --- a/src/node-groups/StringArrayNodesGroup.ts +++ b/src/node-groups/StringArrayNodesGroup.ts @@ -40,10 +40,7 @@ export class StringArrayNodesGroup extends AbstractNodesGroup { } if (this.options.rotateStringArray) { - this.stringArrayRotateValue = Utils.getRandomGenerator().integer({ - min: 100, - max: 500 - }); + this.stringArrayRotateValue = Utils.getRandomInteger(100, 500); } else { this.stringArrayRotateValue = 0; } diff --git a/src/node-obfuscators/replacers/StringLiteralReplacer.ts b/src/node-obfuscators/replacers/StringLiteralReplacer.ts index 266337b4..118e3bfc 100644 --- a/src/node-obfuscators/replacers/StringLiteralReplacer.ts +++ b/src/node-obfuscators/replacers/StringLiteralReplacer.ts @@ -27,7 +27,7 @@ export class StringLiteralReplacer extends AbstractReplacer { public replace (nodeValue: string): string { const replaceWithStringArrayFlag: boolean = ( nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray - && Math.random() <= this.options.stringArrayThreshold + && Utils.getRandomFloat(0, 1) <= this.options.stringArrayThreshold ); if (this.options.stringArray && replaceWithStringArrayFlag) { diff --git a/src/node/NodeAppender.ts b/src/node/NodeAppender.ts index b5abe4a6..af01e760 100644 --- a/src/node/NodeAppender.ts +++ b/src/node/NodeAppender.ts @@ -101,10 +101,7 @@ export class NodeAppender { * @param stackTraceRootLength */ public static getRandomStackTraceIndex (stackTraceRootLength: number): number { - return Utils.getRandomGenerator().integer({ - min: 0, - max: Math.max(0, Math.round(stackTraceRootLength - 1)) - }); + return Utils.getRandomInteger(0, Math.max(0, Math.round(stackTraceRootLength - 1))); } /** diff --git a/src/options/Options.ts b/src/options/Options.ts index 49eec25d..20ad89a2 100644 --- a/src/options/Options.ts +++ b/src/options/Options.ts @@ -85,6 +85,12 @@ export class Options implements IOptions { @IsBoolean() public readonly rotateStringArray: boolean; + /** + * @type {number} + */ + @IsNumber() + public readonly seed: number; + /** * @type {boolean} */ diff --git a/src/preset-options/DefaultPreset.ts b/src/preset-options/DefaultPreset.ts index 1d6a5fe1..3f0a4d04 100644 --- a/src/preset-options/DefaultPreset.ts +++ b/src/preset-options/DefaultPreset.ts @@ -10,6 +10,7 @@ export const DEFAULT_PRESET: IObfuscatorOptions = Object.freeze({ domainLock: [], reservedNames: [], rotateStringArray: true, + seed: 0, selfDefending: true, sourceMap: false, sourceMapBaseUrl: '', diff --git a/src/preset-options/NoCustomNodesPreset.ts b/src/preset-options/NoCustomNodesPreset.ts index f8f000a3..b5bcffac 100644 --- a/src/preset-options/NoCustomNodesPreset.ts +++ b/src/preset-options/NoCustomNodesPreset.ts @@ -10,6 +10,7 @@ export const NO_CUSTOM_NODES_PRESET: IObfuscatorOptions = Object.freeze({ domainLock: [], reservedNames: [], rotateStringArray: false, + seed: 0, selfDefending: false, sourceMap: false, sourceMapBaseUrl: '', diff --git a/src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts b/src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts index 4a8627a3..e62251c6 100644 --- a/src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts +++ b/src/templates/custom-nodes/debug-protection-nodes/debug-protection-function-node/DebugProtectionFunctionTemplate.ts @@ -8,9 +8,9 @@ export function DebugProtectionFunctionTemplate (): string { var {debugProtectionFunctionName} = function () { function debuggerProtection (counter) { if (('' + counter / counter)['length'] !== 1 || counter % 20 === 0) { - (function () {}.constructor('debugger')()); + (function () {}.constructor(${Utils.stringToJSFuck('debugger')})()); } else { - [].filter.constructor(${Utils.stringToJSFuck('debugger')})(); + (function () {}.constructor(${Utils.stringToJSFuck('debugger')})()); } debuggerProtection(++counter); diff --git a/test/functional-tests/JavaScriptObfuscator.spec.ts b/test/functional-tests/JavaScriptObfuscator.spec.ts index 1ee8cb00..4dc1f763 100644 --- a/test/functional-tests/JavaScriptObfuscator.spec.ts +++ b/test/functional-tests/JavaScriptObfuscator.spec.ts @@ -3,6 +3,7 @@ import { IObfuscationResult } from '../../src/interfaces/IObfuscationResult'; import { JavaScriptObfuscator } from '../../src/JavaScriptObfuscator'; import { NO_CUSTOM_NODES_PRESET } from '../../src/preset-options/NoCustomNodesPreset'; +import { readFileAsString } from '../helpers/readFileAsString'; const assert: Chai.AssertStatic = require('chai').assert; @@ -125,5 +126,40 @@ describe('JavaScriptObfuscator', () => { assert.match(obfuscatedCode2, pattern1); assert.match(obfuscatedCode2, pattern2); }); + + it('should returns same code every time with same `seed`', () => { + const code: string = readFileAsString('./test/fixtures/sample.js'); + const seed: number = 12345; + + const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: seed } + ); + const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: seed } + ); + + assert.equal(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode()); + }); + + it('should returns different code with different `seed` option value', () => { + const code: string = readFileAsString('./test/fixtures/sample.js'); + + const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: 12345 } + ); + const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: 12346 } + ); + + const obfuscationResult3: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: 0 } + ); + const obfuscationResult4: IObfuscationResult = JavaScriptObfuscator.obfuscate( + code, { seed: 0 } + ); + + assert.notEqual(obfuscationResult1.getObfuscatedCode(), obfuscationResult2.getObfuscatedCode()); + assert.notEqual(obfuscationResult3.getObfuscatedCode(), obfuscationResult4.getObfuscatedCode()); + }); }); });