Bläddra i källkod

selfDefending test implementation

sanex3339 9 år sedan
förälder
incheckning
69b624ecdf
37 ändrade filer med 347 tillägg och 70 borttagningar
  1. 4 0
      dist/src/Obfuscator.js
  2. 11 0
      dist/src/OptionsNormalizer.js
  3. 1 1
      dist/src/Utils.js
  4. 2 1
      dist/src/custom-nodes/Node.js
  5. 2 2
      dist/src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.js
  6. 2 2
      dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.js
  7. 2 2
      dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.js
  8. 2 2
      dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.js
  9. 25 0
      dist/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.js
  10. 24 6
      dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayCallsWrapper.js
  11. 22 4
      dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayDecodeNode.js
  12. 2 2
      dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayNode.js
  13. 2 2
      dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayRotateFunctionNode.js
  14. 3 3
      dist/src/node-groups/DebugProtectionNodesGroup.js
  15. 10 0
      dist/src/node-groups/SelfDefendingNodesGroup.js
  16. 4 4
      dist/src/node-groups/UnicodeArrayNodesGroup.js
  17. 1 0
      dist/src/preset-options/DefaultPreset.js
  18. 1 0
      dist/src/preset-options/NoCustomNodesPreset.js
  19. 5 0
      src/Obfuscator.ts
  20. 24 3
      src/OptionsNormalizer.ts
  21. 1 1
      src/Utils.ts
  22. 12 1
      src/custom-nodes/Node.ts
  23. 6 2
      src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts
  24. 5 3
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts
  25. 4 2
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts
  26. 4 4
      src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts
  27. 52 0
      src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts
  28. 33 7
      src/custom-nodes/unicode-array-nodes/UnicodeArrayCallsWrapper.ts
  29. 30 4
      src/custom-nodes/unicode-array-nodes/UnicodeArrayDecodeNode.ts
  30. 8 2
      src/custom-nodes/unicode-array-nodes/UnicodeArrayNode.ts
  31. 5 2
      src/custom-nodes/unicode-array-nodes/UnicodeArrayRotateFunctionNode.ts
  32. 2 1
      src/interfaces/IOptions.d.ts
  33. 3 3
      src/node-groups/DebugProtectionNodesGroup.ts
  34. 23 0
      src/node-groups/SelfDefendingNodesGroup.ts
  35. 8 4
      src/node-groups/UnicodeArrayNodesGroup.ts
  36. 1 0
      src/preset-options/DefaultPreset.ts
  37. 1 0
      src/preset-options/NoCustomNodesPreset.ts

+ 4 - 0
dist/src/Obfuscator.js

@@ -12,6 +12,7 @@ const MemberExpressionObfuscator_1 = require('./node-obfuscators/MemberExpressio
 const MethodDefinitionObfuscator_1 = require('./node-obfuscators/MethodDefinitionObfuscator');
 const NodeUtils_1 = require("./NodeUtils");
 const ObjectExpressionObfuscator_1 = require('./node-obfuscators/ObjectExpressionObfuscator');
+const SelfDefendingNodesGroup_1 = require("./node-groups/SelfDefendingNodesGroup");
 const UnicodeArrayNodesGroup_1 = require('./node-groups/UnicodeArrayNodesGroup');
 const VariableDeclarationObfuscator_1 = require('./node-obfuscators/VariableDeclarationObfuscator');
 class Obfuscator {
@@ -82,6 +83,9 @@ class Obfuscator {
         });
     }
     setNewNodes() {
+        if (this.options['selfDefending']) {
+            this.setNodesGroup(new SelfDefendingNodesGroup_1.SelfDefendingNodesGroup(this.options));
+        }
         if (this.options['disableConsoleOutput']) {
             this.setNode('consoleOutputDisableExpressionNode', new ConsoleOutputDisableExpressionNode_1.ConsoleOutputDisableExpressionNode());
         }

+ 11 - 0
dist/src/OptionsNormalizer.js

@@ -3,8 +3,15 @@ class OptionsNormalizer {
     static normalize(options) {
         let normalizedOptions = Object.assign({}, options);
         normalizedOptions = OptionsNormalizer.unicodeArrayRule(normalizedOptions);
+        normalizedOptions = OptionsNormalizer.selfDefendingRule(normalizedOptions);
         return normalizedOptions;
     }
+    static selfDefendingRule(options) {
+        if (options['selfDefending']) {
+            Object.assign(options, OptionsNormalizer.SELF_DEFENDING_OPTIONS);
+        }
+        return options;
+    }
     static unicodeArrayRule(options) {
         if (!options['unicodeArray']) {
             Object.assign(options, OptionsNormalizer.DISABLED_UNICODE_ARRAY_OPTIONS);
@@ -18,4 +25,8 @@ OptionsNormalizer.DISABLED_UNICODE_ARRAY_OPTIONS = {
     unicodeArray: false,
     wrapUnicodeArrayCalls: false
 };
+OptionsNormalizer.SELF_DEFENDING_OPTIONS = {
+    compact: true,
+    selfDefending: true
+};
 exports.OptionsNormalizer = OptionsNormalizer;

+ 1 - 1
dist/src/Utils.js

@@ -28,7 +28,7 @@ class Utils {
             .replace(Utils.hexRepetitiveZerosRegExp, '');
     }
     static getRandomInteger(min, max) {
-        return Math.floor(Math.random() * (max - min + 1)) + min;
+        return Math.round(Math.floor(Math.random() * (max - min + 1)) + min);
     }
     static getRandomVariableName(length = 6) {
         const rangeMinInteger = 10000, rangeMaxInteger = 99999999, prefix = '_0x';

+ 2 - 1
dist/src/custom-nodes/Node.js

@@ -2,8 +2,9 @@
 const AppendState_1 = require('../enums/AppendState');
 const NodeUtils_1 = require("../NodeUtils");
 class Node {
-    constructor() {
+    constructor(options = {}) {
         this.appendState = AppendState_1.AppendState.BeforeObfuscation;
+        this.options = options;
     }
     getAppendState() {
         return this.appendState;

+ 2 - 2
dist/src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.js

@@ -3,8 +3,8 @@ const esprima = require('esprima');
 const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 class ConsoleOutputDisableExpressionNode extends Node_1.Node {
-    constructor() {
-        super();
+    constructor(options = {}) {
+        super(options);
         this.node = this.getNodeStructure();
     }
     appendNode(blockScopeNode) {

+ 2 - 2
dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.js

@@ -3,8 +3,8 @@ const NodeType_1 = require("../../enums/NodeType");
 const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 class DebugProtectionFunctionCallNode extends Node_1.Node {
-    constructor(debugProtectionFunctionName) {
-        super();
+    constructor(debugProtectionFunctionName, options = {}) {
+        super(options);
         this.debugProtectionFunctionName = debugProtectionFunctionName;
         this.node = this.getNodeStructure();
     }

+ 2 - 2
dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.js

@@ -3,8 +3,8 @@ const NodeType_1 = require('../../enums/NodeType');
 const Node_1 = require('../Node');
 const NodeUtils_1 = require('../../NodeUtils');
 class DebugProtectionFunctionIntervalNode extends Node_1.Node {
-    constructor(debugProtectionFunctionName) {
-        super();
+    constructor(debugProtectionFunctionName, options = {}) {
+        super(options);
         this.debugProtectionFunctionName = debugProtectionFunctionName;
         this.node = this.getNodeStructure();
     }

+ 2 - 2
dist/src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.js

@@ -4,8 +4,8 @@ const Node_1 = require('../Node');
 const NodeUtils_1 = require('../../NodeUtils');
 const Utils_1 = require("../../Utils");
 class DebugProtectionFunctionNode extends Node_1.Node {
-    constructor(debugProtectionFunctionName) {
-        super();
+    constructor(debugProtectionFunctionName, options = {}) {
+        super(options);
         this.debugProtectionFunctionName = debugProtectionFunctionName;
         this.node = this.getNodeStructure();
     }

+ 25 - 0
dist/src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.js

@@ -0,0 +1,25 @@
+"use strict";
+const esprima = require('esprima');
+const AppendState_1 = require("../../enums/AppendState");
+const NoCustomNodesPreset_1 = require("../../preset-options/NoCustomNodesPreset");
+const JavaScriptObfuscator_1 = require("../../JavaScriptObfuscator");
+const Node_1 = require('../Node');
+const NodeUtils_1 = require("../../NodeUtils");
+class SelfDefendingUnicodeNode extends Node_1.Node {
+    constructor(options = {}) {
+        super(options);
+        this.appendState = AppendState_1.AppendState.AfterObfuscation;
+        this.node = this.getNodeStructure();
+    }
+    appendNode(blockScopeNode) {
+        NodeUtils_1.NodeUtils.prependNode(blockScopeNode.body, this.getNode());
+    }
+    getNodeStructure() {
+        let node = esprima.parse(JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(`
+               
+            `, NoCustomNodesPreset_1.NO_CUSTOM_NODES_PRESET));
+        NodeUtils_1.NodeUtils.addXVerbatimPropertyToLiterals(node);
+        return NodeUtils_1.NodeUtils.getBlockScopeNodeByIndex(node);
+    }
+}
+exports.SelfDefendingUnicodeNode = SelfDefendingUnicodeNode;

+ 24 - 6
dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayCallsWrapper.js

@@ -5,8 +5,8 @@ const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 const Utils_1 = require("../../Utils");
 class UnicodeArrayCallsWrapper extends Node_1.Node {
-    constructor(unicodeArrayCallsWrapperName, unicodeArrayName, unicodeArray) {
-        super();
+    constructor(unicodeArrayCallsWrapperName, unicodeArrayName, unicodeArray, options = {}) {
+        super(options);
         this.appendState = AppendState_1.AppendState.AfterObfuscation;
         this.unicodeArrayCallsWrapperName = unicodeArrayCallsWrapperName;
         this.unicodeArrayName = unicodeArrayName;
@@ -24,14 +24,32 @@ class UnicodeArrayCallsWrapper extends Node_1.Node {
         if (!this.unicodeArray.length) {
             return;
         }
+        this.updateNode();
         return super.getNode();
     }
     getNodeStructure() {
-        let keyName = Utils_1.Utils.getRandomVariableName(), node = esprima.parse(`
-                var ${this.unicodeArrayCallsWrapperName} = function (${keyName}) {
+        let environmentName = Utils_1.Utils.getRandomVariableName(), keyName = Utils_1.Utils.getRandomVariableName(), selfDefendingCode = '', node;
+        if (this.options['selfDefending']) {
+            selfDefendingCode = `
+                var ${environmentName} = function(){return ${Utils_1.Utils.stringToUnicode('production')};};
+                                                                      
+                if (
+                    ${keyName} % ${Utils_1.Utils.getRandomInteger(this.unicodeArray.length / 8, this.unicodeArray.length / 2)} === 0 &&
+                    /\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}/.test(
+                        ${environmentName}.toString()
+                    ) !== true && ${keyName}++
+                ) {
                     return ${this.unicodeArrayName}[parseInt(${keyName}, 16)]
-                };
-            `);
+                }
+            `;
+        }
+        node = esprima.parse(`
+            var ${this.unicodeArrayCallsWrapperName} = function (${keyName}) {
+                ${selfDefendingCode}
+                
+                return ${this.unicodeArrayName}[parseInt(${keyName}, 16)]
+            };
+        `);
         NodeUtils_1.NodeUtils.addXVerbatimPropertyToLiterals(node);
         return NodeUtils_1.NodeUtils.getBlockScopeNodeByIndex(node);
     }

+ 22 - 4
dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayDecodeNode.js

@@ -7,8 +7,8 @@ const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 const Utils_1 = require("../../Utils");
 class UnicodeArrayDecodeNode extends Node_1.Node {
-    constructor(unicodeArrayName, unicodeArray) {
-        super();
+    constructor(unicodeArrayName, unicodeArray, options = {}) {
+        super(options);
         this.appendState = AppendState_1.AppendState.AfterObfuscation;
         this.unicodeArrayName = unicodeArrayName;
         this.unicodeArray = unicodeArray;
@@ -21,11 +21,27 @@ class UnicodeArrayDecodeNode extends Node_1.Node {
         if (!this.unicodeArray.length) {
             return;
         }
+        this.updateNode();
         return super.getNode();
     }
     getNodeStructure() {
-        const indexVariableName = Utils_1.Utils.getRandomVariableName(), tempArrayName = Utils_1.Utils.getRandomVariableName();
-        let node = esprima.parse(`
+        const environmentName = Utils_1.Utils.getRandomVariableName(), indexVariableName = Utils_1.Utils.getRandomVariableName(), tempArrayName = Utils_1.Utils.getRandomVariableName();
+        let node, selfDefendingCode = '';
+        if (this.options['selfDefending']) {
+            selfDefendingCode = `
+                var ${environmentName} = function(){return ${Utils_1.Utils.stringToUnicode('dev')};};
+                                        
+                if (
+                    ${indexVariableName} % ${Utils_1.Utils.getRandomInteger(this.unicodeArray.length / 8, this.unicodeArray.length / 2)} === 0 &&
+                    /\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}/.test(
+                        ${environmentName}.toString()
+                    ) !== true && ${indexVariableName}++
+                ) {
+                    continue;
+                }
+            `;
+        }
+        node = esprima.parse(`
             (function () {
                 ${JavaScriptObfuscator_1.JavaScriptObfuscator.obfuscate(`
                     (function () {
@@ -51,6 +67,8 @@ class UnicodeArrayDecodeNode extends Node_1.Node {
                 var ${tempArrayName} = [];
                 
                 for (var ${indexVariableName} in ${this.unicodeArrayName}) {
+                    ${selfDefendingCode}
+                
                     ${tempArrayName}[${Utils_1.Utils.stringToUnicode('push')}](decodeURI(atob(${this.unicodeArrayName}[${indexVariableName}])));
                 }
                 

+ 2 - 2
dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayNode.js

@@ -6,8 +6,8 @@ const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 const Utils_1 = require('../../Utils');
 class UnicodeArrayNode extends Node_1.Node {
-    constructor(unicodeArrayName, unicodeArrayRotateValue = 0) {
-        super();
+    constructor(unicodeArrayName, unicodeArrayRotateValue = 0, options = {}) {
+        super(options);
         this.appendState = AppendState_1.AppendState.AfterObfuscation;
         this.unicodeArray = [];
         this.unicodeArrayName = unicodeArrayName;

+ 2 - 2
dist/src/custom-nodes/unicode-array-nodes/UnicodeArrayRotateFunctionNode.js

@@ -5,8 +5,8 @@ const Node_1 = require('../Node');
 const NodeUtils_1 = require("../../NodeUtils");
 const Utils_1 = require("../../Utils");
 class UnicodeArrayRotateFunctionNode extends Node_1.Node {
-    constructor(unicodeArrayName, unicodeArray, unicodeArrayRotateValue) {
-        super();
+    constructor(unicodeArrayName, unicodeArray, unicodeArrayRotateValue, options = {}) {
+        super(options);
         this.appendState = AppendState_1.AppendState.AfterObfuscation;
         this.unicodeArrayName = unicodeArrayName;
         this.unicodeArray = unicodeArray;

+ 3 - 3
dist/src/node-groups/DebugProtectionNodesGroup.js

@@ -8,10 +8,10 @@ class DebugProtectionNodesGroup extends NodesGroup_1.NodesGroup {
     constructor(options = {}) {
         super(options);
         this.debugProtectionFunctionIdentifier = Utils_1.Utils.getRandomVariableName();
-        this.nodes.set('debugProtectionFunctionNode', new DebugProtectionFunctionNode_1.DebugProtectionFunctionNode(this.debugProtectionFunctionIdentifier));
-        this.nodes.set('debugProtectionFunctionCallNode', new DebugProtectionFunctionCallNode_1.DebugProtectionFunctionCallNode(this.debugProtectionFunctionIdentifier));
+        this.nodes.set('debugProtectionFunctionNode', new DebugProtectionFunctionNode_1.DebugProtectionFunctionNode(this.debugProtectionFunctionIdentifier, this.options));
+        this.nodes.set('debugProtectionFunctionCallNode', new DebugProtectionFunctionCallNode_1.DebugProtectionFunctionCallNode(this.debugProtectionFunctionIdentifier, this.options));
         if (this.options['debugProtectionInterval']) {
-            this.nodes.set('debugProtectionFunctionIntervalNode', new DebugProtectionFunctionIntervalNode_1.DebugProtectionFunctionIntervalNode(this.debugProtectionFunctionIdentifier));
+            this.nodes.set('debugProtectionFunctionIntervalNode', new DebugProtectionFunctionIntervalNode_1.DebugProtectionFunctionIntervalNode(this.debugProtectionFunctionIdentifier, this.options));
         }
     }
 }

+ 10 - 0
dist/src/node-groups/SelfDefendingNodesGroup.js

@@ -0,0 +1,10 @@
+"use strict";
+const NodesGroup_1 = require('./NodesGroup');
+const SelfDefendingUnicodeNode_1 = require("../custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode");
+class SelfDefendingNodesGroup extends NodesGroup_1.NodesGroup {
+    constructor(options = {}) {
+        super(options);
+        this.nodes.set('selfDefendingUnicodeNode', new SelfDefendingUnicodeNode_1.SelfDefendingUnicodeNode(this.options));
+    }
+}
+exports.SelfDefendingNodesGroup = SelfDefendingNodesGroup;

+ 4 - 4
dist/src/node-groups/UnicodeArrayNodesGroup.js

@@ -11,16 +11,16 @@ class UnicodeArrayNodesGroup extends NodesGroup_1.NodesGroup {
         this.unicodeArrayName = Utils_1.Utils.getRandomVariableName(UnicodeArrayNode_1.UnicodeArrayNode.UNICODE_ARRAY_RANDOM_LENGTH);
         this.unicodeArrayTranslatorName = Utils_1.Utils.getRandomVariableName(UnicodeArrayNode_1.UnicodeArrayNode.UNICODE_ARRAY_RANDOM_LENGTH);
         this.unicodeArrayRotateValue = this.options['rotateUnicodeArray'] ? Utils_1.Utils.getRandomInteger(100, 500) : 0;
-        let unicodeArrayNode = new UnicodeArrayNode_1.UnicodeArrayNode(this.unicodeArrayName, this.unicodeArrayRotateValue), unicodeArray = unicodeArrayNode.getNodeData();
+        let unicodeArrayNode = new UnicodeArrayNode_1.UnicodeArrayNode(this.unicodeArrayName, this.unicodeArrayRotateValue, this.options), unicodeArray = unicodeArrayNode.getNodeData();
         this.nodes.set('unicodeArrayNode', unicodeArrayNode);
         if (this.options['wrapUnicodeArrayCalls']) {
-            this.nodes.set('unicodeArrayCallsWrapper', new UnicodeArrayCallsWrapper_1.UnicodeArrayCallsWrapper(this.unicodeArrayTranslatorName, this.unicodeArrayName, unicodeArray));
+            this.nodes.set('unicodeArrayCallsWrapper', new UnicodeArrayCallsWrapper_1.UnicodeArrayCallsWrapper(this.unicodeArrayTranslatorName, this.unicodeArrayName, unicodeArray, this.options));
         }
         if (this.options['encodeUnicodeLiterals']) {
-            this.nodes.set('unicodeArrayDecodeNode', new UnicodeArrayDecodeNode_1.UnicodeArrayDecodeNode(this.unicodeArrayName, unicodeArray));
+            this.nodes.set('unicodeArrayDecodeNode', new UnicodeArrayDecodeNode_1.UnicodeArrayDecodeNode(this.unicodeArrayName, unicodeArray, this.options));
         }
         if (this.options['rotateUnicodeArray']) {
-            this.nodes.set('unicodeArrayRotateFunctionNode', new UnicodeArrayRotateFunctionNode_1.UnicodeArrayRotateFunctionNode(this.unicodeArrayName, unicodeArray, this.unicodeArrayRotateValue));
+            this.nodes.set('unicodeArrayRotateFunctionNode', new UnicodeArrayRotateFunctionNode_1.UnicodeArrayRotateFunctionNode(this.unicodeArrayName, unicodeArray, this.unicodeArrayRotateValue, this.options));
         }
     }
 }

+ 1 - 0
dist/src/preset-options/DefaultPreset.js

@@ -7,6 +7,7 @@ exports.DEFAULT_PRESET = Object.freeze({
     encodeUnicodeLiterals: false,
     reservedNames: [],
     rotateUnicodeArray: true,
+    selfDefending: true,
     unicodeArray: true,
     wrapUnicodeArrayCalls: true
 });

+ 1 - 0
dist/src/preset-options/NoCustomNodesPreset.js

@@ -7,6 +7,7 @@ exports.NO_CUSTOM_NODES_PRESET = Object.freeze({
     encodeUnicodeLiterals: false,
     reservedNames: [],
     rotateUnicodeArray: false,
+    selfDefending: false,
     unicodeArray: false,
     wrapUnicodeArrayCalls: false
 });

+ 5 - 0
src/Obfuscator.ts

@@ -20,6 +20,7 @@ import { MemberExpressionObfuscator } from './node-obfuscators/MemberExpressionO
 import { MethodDefinitionObfuscator } from './node-obfuscators/MethodDefinitionObfuscator';
 import { NodeUtils } from "./NodeUtils";
 import { ObjectExpressionObfuscator } from './node-obfuscators/ObjectExpressionObfuscator';
+import { SelfDefendingNodesGroup } from "./node-groups/SelfDefendingNodesGroup";
 import { UnicodeArrayNodesGroup } from './node-groups/UnicodeArrayNodesGroup';
 import { VariableDeclarationObfuscator } from './node-obfuscators/VariableDeclarationObfuscator';
 
@@ -144,6 +145,10 @@ export class Obfuscator {
     }
 
     private setNewNodes (): void {
+        if (this.options['selfDefending']) {
+            this.setNodesGroup(new SelfDefendingNodesGroup(this.options));
+        }
+
         if (this.options['disableConsoleOutput']) {
             this.setNode(
                 'consoleOutputDisableExpressionNode',

+ 24 - 3
src/OptionsNormalizer.ts

@@ -11,18 +11,39 @@ export class OptionsNormalizer {
         wrapUnicodeArrayCalls: false
     };
 
+    /**
+     * @type {IOptions}
+     */
+    public static SELF_DEFENDING_OPTIONS: IOptions = {
+        compact: true,
+        selfDefending: true
+    };
+
     /**
      * @param options
      * @returns {IOptions}
      */
     public static normalize (options: IOptions): IOptions {
         let normalizedOptions: IOptions = Object.assign({}, options);
-        
+
         normalizedOptions = OptionsNormalizer.unicodeArrayRule(normalizedOptions);
-        
+        normalizedOptions = OptionsNormalizer.selfDefendingRule(normalizedOptions);
+
         return normalizedOptions;
     }
 
+    /**
+     * @param options
+     * @returns {IOptions}
+     */
+    private static selfDefendingRule (options: IOptions): IOptions {
+        if (options['selfDefending']) {
+            Object.assign(options, OptionsNormalizer.SELF_DEFENDING_OPTIONS);
+        }
+
+        return options;
+    }
+
     /**
      * @param options
      * @returns {IOptions}
@@ -31,7 +52,7 @@ export class OptionsNormalizer {
         if (!options['unicodeArray']) {
             Object.assign(options, OptionsNormalizer.DISABLED_UNICODE_ARRAY_OPTIONS);
         }
-        
+
         return options;
     }
 }

+ 1 - 1
src/Utils.ts

@@ -61,7 +61,7 @@ export class Utils {
      * @returns {number}
      */
     public static getRandomInteger(min: number, max: number): number {
-        return Math.floor(Math.random() * (max - min + 1)) + min;
+        return Math.round(Math.floor(Math.random() * (max - min + 1)) + min);
     }
 
     /**

+ 12 - 1
src/custom-nodes/Node.ts

@@ -1,5 +1,6 @@
 import { ICustomNode } from '../interfaces/ICustomNode';
 import { INode } from '../interfaces/nodes/INode';
+import { IOptions } from "../interfaces/IOptions";
 
 import { AppendState } from '../enums/AppendState';
 
@@ -16,7 +17,17 @@ export abstract class Node implements ICustomNode {
      */
     protected node: INode;
 
-    constructor () {}
+    /**
+     * @type {IOptions}
+     */
+    protected options: IOptions;
+
+    /**
+     * @param options
+     */
+    constructor (options: IOptions = {}) {
+        this.options = options;
+    }
 
     public abstract appendNode (astTree: INode): void;
 

+ 6 - 2
src/custom-nodes/console-output-nodes/ConsoleOutputDisableExpressionNode.ts

@@ -1,6 +1,7 @@
 import * as esprima from 'esprima';
 
 import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -8,8 +9,11 @@ import { Node } from '../Node';
 import { NodeUtils } from "../../NodeUtils";
 
 export class ConsoleOutputDisableExpressionNode extends Node {
-    constructor () {
-        super();
+    /**
+     * @param options
+     */
+    constructor (options: IOptions = {}) {
+        super(options);
 
         this.node = this.getNodeStructure();
     }

+ 5 - 3
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode.ts

@@ -1,4 +1,5 @@
 import { IExpressionStatementNode } from "../../interfaces/nodes/IExpressionStatementNode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -15,12 +16,13 @@ export class DebugProtectionFunctionCallNode extends Node {
 
     /**
      * @param debugProtectionFunctionName
+     * @param options
      */
-    constructor (debugProtectionFunctionName: string) {
-        super();
+    constructor (debugProtectionFunctionName: string, options: IOptions = {}) {
+        super(options);
 
         this.debugProtectionFunctionName = debugProtectionFunctionName;
-        
+
         this.node = this.getNodeStructure();
     }
 

+ 4 - 2
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode.ts

@@ -1,4 +1,5 @@
 import { IExpressionStatementNode } from "../../interfaces/nodes/IExpressionStatementNode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -15,9 +16,10 @@ export class DebugProtectionFunctionIntervalNode extends Node {
 
     /**
      * @param debugProtectionFunctionName
+     * @param options
      */
-    constructor (debugProtectionFunctionName: string) {
-        super();
+    constructor (debugProtectionFunctionName: string, options: IOptions = {}) {
+        super(options);
 
         this.debugProtectionFunctionName = debugProtectionFunctionName;
 

+ 4 - 4
src/custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode.ts

@@ -1,6 +1,7 @@
 import * as esprima from 'esprima';
 
 import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -16,11 +17,10 @@ export class DebugProtectionFunctionNode extends Node {
 
     /**
      * @param debugProtectionFunctionName
+     * @param options
      */
-    constructor (
-        debugProtectionFunctionName: string
-    ) {
-        super();
+    constructor (debugProtectionFunctionName: string, options: IOptions = {}) {
+        super(options);
 
         this.debugProtectionFunctionName = debugProtectionFunctionName;
 

+ 52 - 0
src/custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode.ts

@@ -0,0 +1,52 @@
+import * as esprima from 'esprima';
+
+import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
+
+import { TBlockScopeNode } from "../../types/TBlockScopeNode";
+
+import { AppendState } from "../../enums/AppendState";
+
+import { NO_CUSTOM_NODES_PRESET } from "../../preset-options/NoCustomNodesPreset";
+
+import { JavaScriptObfuscator } from "../../JavaScriptObfuscator";
+import { Node } from '../Node';
+import { NodeUtils } from "../../NodeUtils";
+
+export class SelfDefendingUnicodeNode extends Node {
+    /**
+     * @type {AppendState}
+     */
+    protected appendState: AppendState = AppendState.AfterObfuscation;
+
+    /**
+     * @param options
+     */
+    constructor (options: IOptions = {}) {
+        super(options);
+
+        this.node = this.getNodeStructure();
+    }
+
+    /**
+     * @param blockScopeNode
+     */
+    public appendNode (blockScopeNode: TBlockScopeNode): void {
+        NodeUtils.prependNode(blockScopeNode.body, this.getNode());
+    }
+
+    /**
+     * @returns {INode}
+     */
+    protected getNodeStructure (): INode {
+        let node: INode = esprima.parse(
+             JavaScriptObfuscator.obfuscate(`
+               
+            `, NO_CUSTOM_NODES_PRESET)
+        );
+
+        NodeUtils.addXVerbatimPropertyToLiterals(node);
+
+        return NodeUtils.getBlockScopeNodeByIndex(node);
+    }
+}

+ 33 - 7
src/custom-nodes/unicode-array-nodes/UnicodeArrayCallsWrapper.ts

@@ -1,6 +1,7 @@
 import * as esprima from 'esprima';
 
 import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -35,13 +36,15 @@ export class UnicodeArrayCallsWrapper extends Node {
      * @param unicodeArrayCallsWrapperName
      * @param unicodeArrayName
      * @param unicodeArray
+     * @param options
      */
     constructor (
         unicodeArrayCallsWrapperName: string,
         unicodeArrayName: string,
-        unicodeArray: string[]
+        unicodeArray: string[],
+        options: IOptions = {}
     ) {
-        super();
+        super(options);
 
         this.unicodeArrayCallsWrapperName = unicodeArrayCallsWrapperName;
         this.unicodeArrayName = unicodeArrayName;
@@ -72,6 +75,8 @@ export class UnicodeArrayCallsWrapper extends Node {
             return;
         }
 
+        this.updateNode();
+
         return super.getNode();
     }
 
@@ -79,12 +84,33 @@ export class UnicodeArrayCallsWrapper extends Node {
      * @returns {INode}
      */
     protected getNodeStructure (): INode {
-        let keyName: string = Utils.getRandomVariableName(),
-            node: INode = esprima.parse(`
-                var ${this.unicodeArrayCallsWrapperName} = function (${keyName}) {
+        let environmentName: string = Utils.getRandomVariableName(),
+            keyName: string = Utils.getRandomVariableName(),
+            selfDefendingCode: string = '',
+            node: INode;
+
+        if (this.options['selfDefending']) {
+            selfDefendingCode = `
+                var ${environmentName} = function(){return ${Utils.stringToUnicode('production')};};
+                                                                      
+                if (
+                    ${keyName} % ${Utils.getRandomInteger(this.unicodeArray.length / 8, this.unicodeArray.length / 2)} === 0 &&
+                    /\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}/.test(
+                        ${environmentName}.toString()
+                    ) !== true && ${keyName}++
+                ) {
                     return ${this.unicodeArrayName}[parseInt(${keyName}, 16)]
-                };
-            `);
+                }
+            `;
+        }
+
+        node = esprima.parse(`
+            var ${this.unicodeArrayCallsWrapperName} = function (${keyName}) {
+                ${selfDefendingCode}
+                
+                return ${this.unicodeArrayName}[parseInt(${keyName}, 16)]
+            };
+        `);
 
         NodeUtils.addXVerbatimPropertyToLiterals(node);
 

+ 30 - 4
src/custom-nodes/unicode-array-nodes/UnicodeArrayDecodeNode.ts

@@ -2,6 +2,7 @@ import * as esprima from 'esprima';
 import { JavaScriptObfuscator } from '../../JavaScriptObfuscator';
 
 import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -32,12 +33,14 @@ export class UnicodeArrayDecodeNode extends Node {
     /**
      * @param unicodeArrayName
      * @param unicodeArray
+     * @param options
      */
     constructor (
         unicodeArrayName: string,
-        unicodeArray: string[]
+        unicodeArray: string[],
+        options: IOptions = {}
     ) {
-        super();
+        super(options);
 
         this.unicodeArrayName = unicodeArrayName;
         this.unicodeArray = unicodeArray;
@@ -60,6 +63,8 @@ export class UnicodeArrayDecodeNode extends Node {
             return;
         }
 
+        this.updateNode();
+
         return super.getNode();
     }
 
@@ -67,10 +72,29 @@ export class UnicodeArrayDecodeNode extends Node {
      * @returns {INode}
      */
     protected getNodeStructure (): INode {
-        const indexVariableName: string = Utils.getRandomVariableName(),
+        const environmentName: string = Utils.getRandomVariableName(),
+            indexVariableName: string = Utils.getRandomVariableName(),
             tempArrayName: string = Utils.getRandomVariableName();
 
-        let node: INode = esprima.parse(`
+        let node: INode,
+            selfDefendingCode: string = '';
+        
+        if (this.options['selfDefending']) {
+            selfDefendingCode = `
+                var ${environmentName} = function(){return ${Utils.stringToUnicode('dev')};};
+                                        
+                if (
+                    ${indexVariableName} % ${Utils.getRandomInteger(this.unicodeArray.length / 8, this.unicodeArray.length / 2)} === 0 &&
+                    /\\w+ *\\(\\) *{\\w+ *['|"].+['|"];? *}/.test(
+                        ${environmentName}.toString()
+                    ) !== true && ${indexVariableName}++
+                ) {
+                    continue;
+                }
+            `;
+        }
+
+        node = esprima.parse(`
             (function () {
                 ${JavaScriptObfuscator.obfuscate(`
                     (function () {
@@ -96,6 +120,8 @@ export class UnicodeArrayDecodeNode extends Node {
                 var ${tempArrayName} = [];
                 
                 for (var ${indexVariableName} in ${this.unicodeArrayName}) {
+                    ${selfDefendingCode}
+                
                     ${tempArrayName}[${Utils.stringToUnicode('push')}](decodeURI(atob(${this.unicodeArrayName}[${indexVariableName}])));
                 }
                 

+ 8 - 2
src/custom-nodes/unicode-array-nodes/UnicodeArrayNode.ts

@@ -1,6 +1,7 @@
 import * as escodegen from 'escodegen';
 
 import { INode } from '../../interfaces/nodes/INode';
+import { IOptions } from "../../interfaces/IOptions";
 import { IVariableDeclarationNode } from "../../interfaces/nodes/IVariableDeclarationNode";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
@@ -41,9 +42,14 @@ export class UnicodeArrayNode extends Node {
     /**
      * @param unicodeArrayName
      * @param unicodeArrayRotateValue
+     * @param options
      */
-    constructor (unicodeArrayName: string, unicodeArrayRotateValue: number = 0) {
-        super();
+    constructor (
+        unicodeArrayName: string,
+        unicodeArrayRotateValue: number = 0,
+        options: IOptions = {}
+    ) {
+        super(options);
 
         this.unicodeArrayName = unicodeArrayName;
         this.unicodeArrayRotateValue = unicodeArrayRotateValue;

+ 5 - 2
src/custom-nodes/unicode-array-nodes/UnicodeArrayRotateFunctionNode.ts

@@ -1,6 +1,7 @@
 import * as esprima from 'esprima';
 
 import { INode } from "../../interfaces/nodes/INode";
+import { IOptions } from "../../interfaces/IOptions";
 
 import { TBlockScopeNode } from "../../types/TBlockScopeNode";
 
@@ -35,13 +36,15 @@ export class UnicodeArrayRotateFunctionNode extends Node {
      * @param unicodeArrayName
      * @param unicodeArray
      * @param unicodeArrayRotateValue
+     * @param options
      */
     constructor (
         unicodeArrayName: string,
         unicodeArray: string[],
-        unicodeArrayRotateValue: number
+        unicodeArrayRotateValue: number,
+        options: IOptions = {}
     ) {
-        super();
+        super(options);
 
         this.unicodeArrayName = unicodeArrayName;
         this.unicodeArray = unicodeArray;

+ 2 - 1
src/interfaces/IOptions.d.ts

@@ -3,9 +3,10 @@ export interface IOptions {
     debugProtection?: boolean;
     debugProtectionInterval?: boolean;
     disableConsoleOutput?: boolean;
+    encodeUnicodeLiterals?: boolean;
     reservedNames?: string[];
     rotateUnicodeArray?: boolean;
-    encodeUnicodeLiterals?: boolean;
+    selfDefending?: boolean;
     unicodeArray?: boolean;
     wrapUnicodeArrayCalls?: boolean;
 }

+ 3 - 3
src/node-groups/DebugProtectionNodesGroup.ts

@@ -21,17 +21,17 @@ export class DebugProtectionNodesGroup extends NodesGroup {
 
         this.nodes.set(
             'debugProtectionFunctionNode',
-            new DebugProtectionFunctionNode(this.debugProtectionFunctionIdentifier)
+            new DebugProtectionFunctionNode(this.debugProtectionFunctionIdentifier, this.options)
         );
         this.nodes.set(
             'debugProtectionFunctionCallNode',
-            new DebugProtectionFunctionCallNode(this.debugProtectionFunctionIdentifier)
+            new DebugProtectionFunctionCallNode(this.debugProtectionFunctionIdentifier, this.options)
         );
 
         if (this.options['debugProtectionInterval']) {
             this.nodes.set(
                 'debugProtectionFunctionIntervalNode',
-                new DebugProtectionFunctionIntervalNode(this.debugProtectionFunctionIdentifier)
+                new DebugProtectionFunctionIntervalNode(this.debugProtectionFunctionIdentifier, this.options)
             );
         }
     }

+ 23 - 0
src/node-groups/SelfDefendingNodesGroup.ts

@@ -0,0 +1,23 @@
+import { IOptions } from "../interfaces/IOptions";
+
+import { DebugProtectionFunctionCallNode } from "../custom-nodes/debug-protection-nodes/DebugProtectionFunctionCallNode";
+import { DebugProtectionFunctionIntervalNode } from "../custom-nodes/debug-protection-nodes/DebugProtectionFunctionIntervalNode";
+import { DebugProtectionFunctionNode } from "../custom-nodes/debug-protection-nodes/DebugProtectionFunctionNode";
+
+import { NodesGroup } from './NodesGroup';
+import { Utils } from '../Utils';
+import {SelfDefendingUnicodeNode} from "../custom-nodes/self-defending-nodes/SelfDefendingUnicodeNode";
+
+export class SelfDefendingNodesGroup extends NodesGroup {
+    /**
+     * @param options
+     */
+    constructor (options: IOptions = {}) {
+        super(options);
+
+        this.nodes.set(
+            'selfDefendingUnicodeNode',
+            new SelfDefendingUnicodeNode(this.options)
+        );
+    }
+}

+ 8 - 4
src/node-groups/UnicodeArrayNodesGroup.ts

@@ -33,7 +33,8 @@ export class UnicodeArrayNodesGroup extends NodesGroup {
 
         let unicodeArrayNode: UnicodeArrayNode = new UnicodeArrayNode(
                 this.unicodeArrayName,
-                this.unicodeArrayRotateValue
+                this.unicodeArrayRotateValue,
+                this.options
             ),
             unicodeArray: string [] = unicodeArrayNode.getNodeData();
 
@@ -48,7 +49,8 @@ export class UnicodeArrayNodesGroup extends NodesGroup {
                 new UnicodeArrayCallsWrapper(
                     this.unicodeArrayTranslatorName,
                     this.unicodeArrayName,
-                    unicodeArray
+                    unicodeArray,
+                    this.options
                 )
             );
         }
@@ -58,7 +60,8 @@ export class UnicodeArrayNodesGroup extends NodesGroup {
                 'unicodeArrayDecodeNode',
                 new UnicodeArrayDecodeNode (
                     this.unicodeArrayName,
-                    unicodeArray
+                    unicodeArray,
+                    this.options
                 )
             );
         }
@@ -69,7 +72,8 @@ export class UnicodeArrayNodesGroup extends NodesGroup {
                 new UnicodeArrayRotateFunctionNode(
                     this.unicodeArrayName,
                     unicodeArray,
-                    this.unicodeArrayRotateValue
+                    this.unicodeArrayRotateValue,
+                    this.options
                 )
             );
         }

+ 1 - 0
src/preset-options/DefaultPreset.ts

@@ -8,6 +8,7 @@ export const DEFAULT_PRESET: IOptions = Object.freeze({
     encodeUnicodeLiterals: false,
     reservedNames: [],
     rotateUnicodeArray: true,
+    selfDefending: true,
     unicodeArray: true,
     wrapUnicodeArrayCalls: true
 });

+ 1 - 0
src/preset-options/NoCustomNodesPreset.ts

@@ -8,6 +8,7 @@ export const NO_CUSTOM_NODES_PRESET: IOptions = Object.freeze({
     encodeUnicodeLiterals: false,
     reservedNames: [],
     rotateUnicodeArray: false,
+    selfDefending: false,
     unicodeArray: false,
     wrapUnicodeArrayCalls: false
 });