浏览代码

Added correct `self-defending` code for `target: 'browser-no-eval'` (#617)

https://github.com/javascript-obfuscator/javascript-obfuscator/issues/610

Co-authored-by: sanex3339 <yarabotayuvyandex3339>
Timofey Kachalov 5 年之前
父节点
当前提交
7135b09f28

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v0.28.4
+---
+* Added correct `self-defending` code for `target: 'browser-no-eval'`. https://github.com/javascript-obfuscator/javascript-obfuscator/issues/610
+
 v0.28.3
 ---
 * Removed memory leak with `identifierNamesGenerator: 'mangled'`

文件差异内容过多而无法显示
+ 0 - 0
dist/index.browser.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.cli.js


文件差异内容过多而无法显示
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.28.3",
+  "version": "0.28.4",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 15 - 13
src/custom-code-helpers/self-defending/SelfDefendingUnicodeCodeHelper.ts

@@ -6,16 +6,19 @@ import { TStatement } from '../../types/node/TStatement';
 
 import { ICustomCodeHelperFormatter } from '../../interfaces/custom-code-helpers/ICustomCodeHelperFormatter';
 import { ICustomCodeHelperObfuscator } from '../../interfaces/custom-code-helpers/ICustomCodeHelperObfuscator';
-import { IEscapeSequenceEncoder } from '../../interfaces/utils/IEscapeSequenceEncoder';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 
+import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
+
 import { initializable } from '../../decorators/Initializable';
 
 import { SelfDefendingTemplate } from './templates/SelfDefendingTemplate';
+import { SelfDefendingNoEvalTemplate } from './templates/SelfDefendingNoEvalTemplate';
 
 import { AbstractCustomCodeHelper } from '../AbstractCustomCodeHelper';
 import { NodeUtils } from '../../node/NodeUtils';
+import { GlobalVariableNoEvalTemplate } from '../common/templates/GlobalVariableNoEvalTemplate';
 
 @injectable()
 export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
@@ -31,18 +34,12 @@ export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
     @initializable()
     private selfDefendingFunctionName!: string;
 
-    /**
-     * @type {IEscapeSequenceEncoder}
-     */
-    private readonly escapeSequenceEncoder: IEscapeSequenceEncoder;
-
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
      * @param {ICustomCodeHelperObfuscator} customCodeHelperObfuscator
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
-     * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
      */
     public constructor (
         @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
@@ -50,8 +47,7 @@ export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
         @inject(ServiceIdentifiers.ICustomCodeHelperFormatter) customCodeHelperFormatter: ICustomCodeHelperFormatter,
         @inject(ServiceIdentifiers.ICustomCodeHelperObfuscator) customCodeHelperObfuscator: ICustomCodeHelperObfuscator,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
-        @inject(ServiceIdentifiers.IOptions) options: IOptions,
-        @inject(ServiceIdentifiers.IEscapeSequenceEncoder) escapeSequenceEncoder: IEscapeSequenceEncoder
+        @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(
             identifierNamesGeneratorFactory,
@@ -60,8 +56,6 @@ export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
             randomGenerator,
             options
         );
-
-        this.escapeSequenceEncoder = escapeSequenceEncoder;
     }
 
     /**
@@ -85,9 +79,17 @@ export class SelfDefendingUnicodeCodeHelper extends AbstractCustomCodeHelper {
      * @returns {string}
      */
     protected getCodeHelperTemplate (): string {
-        return this.customCodeHelperFormatter.formatTemplate(SelfDefendingTemplate(this.escapeSequenceEncoder), {
+        const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
+            ? this.getGlobalVariableTemplate()
+            : GlobalVariableNoEvalTemplate();
+        const selfDefendingTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
+            ? SelfDefendingTemplate()
+            : SelfDefendingNoEvalTemplate();
+
+        return this.customCodeHelperFormatter.formatTemplate(selfDefendingTemplate, {
             callControllerFunctionName: this.callsControllerFunctionName,
-            selfDefendingFunctionName: this.selfDefendingFunctionName
+            selfDefendingFunctionName: this.selfDefendingFunctionName,
+            globalVariableTemplate
         });
     }
 }

+ 22 - 0
src/custom-code-helpers/self-defending/templates/SelfDefendingNoEvalTemplate.ts

@@ -0,0 +1,22 @@
+/**
+ * Notice, that second and third call to recursiveFunc1('indexOf') has cyrillic `е` character instead latin
+ *
+ * @returns {string}
+ */
+export function SelfDefendingNoEvalTemplate (): string {
+    return `
+        const {selfDefendingFunctionName} = {callControllerFunctionName}(this, function () {
+            {globalVariableTemplate}
+        
+            const test = function () {
+                const regExp = new that.RegExp('^([^ ]+( +[^ ]+)+)+[^ ]}');
+                
+                return !regExp.test({selfDefendingFunctionName});
+            };
+            
+            return test();
+        });
+        
+        {selfDefendingFunctionName}();
+    `;
+}

+ 1 - 5
src/custom-code-helpers/self-defending/templates/SelfDefendingTemplate.ts

@@ -1,13 +1,9 @@
-import { IEscapeSequenceEncoder } from '../../../interfaces/utils/IEscapeSequenceEncoder';
-
 /**
  * SelfDefendingTemplate. Enters code in infinity loop.
- * Notice, that second and third call to recursiveFunc1('indexOf') has cyrillic `е` character instead latin
  *
- * @param {IEscapeSequenceEncoder} escapeSequenceEncoder
  * @returns {string}
  */
-export function SelfDefendingTemplate (escapeSequenceEncoder: IEscapeSequenceEncoder): string {
+export function SelfDefendingTemplate (): string {
     return `
         const {selfDefendingFunctionName} = {callControllerFunctionName}(this, function () {
             const test = function () {

+ 156 - 0
test/functional-tests/custom-code-helpers/self-defending/templates/SelfDefendingNoEvalTemplate.spec.ts

@@ -0,0 +1,156 @@
+import { assert } from 'chai';
+
+import { ObfuscationTarget } from '../../../../../src/enums/ObfuscationTarget';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
+
+import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
+
+import { evaluateInWorker } from '../../../../helpers/evaluateInWorker';
+import { readFileAsString } from '../../../../helpers/readFileAsString';
+import { beautifyCode } from '../../../../helpers/beautifyCode';
+
+import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+
+describe('SelfDefendingNoEvalTemplate', function () {
+    const evaluationTimeout: number = 3500;
+
+    this.timeout(10000);
+
+    describe('Variant #1: correctly obfuscate code with `HexadecimalIdentifierNamesGenerator``', () => {
+        const expectedEvaluationResult: number = 1;
+
+        let obfuscatedCode: string,
+            evaluationResult: number = 0;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/input.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    selfDefending: true,
+                    identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+                    target: ObfuscationTarget.BrowserNoEval
+                }
+            ).getObfuscatedCode();
+
+            return evaluateInWorker(obfuscatedCode, evaluationTimeout)
+                .then((result: string | null) => {
+                    if (!result) {
+                        return;
+                    }
+
+                    evaluationResult = parseInt(result, 10);
+                });
+        });
+
+        it('should correctly evaluate code with enabled self defending', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+    });
+
+    describe('Variant #2: correctly obfuscate code with `MangledIdentifierNamesGenerator` option', () => {
+        const expectedEvaluationResult: number = 1;
+
+        let obfuscatedCode: string,
+            evaluationResult: number = 0;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/input.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    selfDefending: true,
+                    identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                    target: ObfuscationTarget.BrowserNoEval
+                }
+            ).getObfuscatedCode();
+
+            return evaluateInWorker(obfuscatedCode, evaluationTimeout)
+                .then((result: string | null) => {
+                    if (!result) {
+                        return;
+                    }
+
+                    evaluationResult = parseInt(result, 10);
+                });
+        });
+
+        it('should correctly evaluate code with enabled self defending', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+    });
+
+    describe('Variant #3: correctly obfuscate code with `DictionaryIdentifierNamesGenerator` option', () => {
+        const expectedEvaluationResult: number = 1;
+
+        let obfuscatedCode: string,
+            evaluationResult: number = 0;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/input.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    selfDefending: true,
+                    identifierNamesGenerator: IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
+                    identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'eagle'],
+                    target: ObfuscationTarget.BrowserNoEval
+                }
+            ).getObfuscatedCode();
+
+            return evaluateInWorker(obfuscatedCode, evaluationTimeout)
+                .then((result: string | null) => {
+                    if (!result) {
+                        return;
+                    }
+
+                    evaluationResult = parseInt(result, 10);
+                });
+        });
+
+        it('should correctly evaluate code with enabled self defending', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+    });
+
+    describe('Variant #4: obfuscated code with beautified self defending code', () => {
+        const expectedEvaluationResult: number = 0;
+
+        let obfuscatedCode: string,
+            evaluationResult: number = 0;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/input.js');
+
+            obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    selfDefending: true,
+                    target: ObfuscationTarget.BrowserNoEval
+                }
+            ).getObfuscatedCode();
+            obfuscatedCode = beautifyCode(obfuscatedCode);
+
+            return evaluateInWorker(obfuscatedCode, evaluationTimeout)
+                .then((result: string | null) => {
+                    if (!result) {
+                        return;
+                    }
+
+                    evaluationResult = parseInt(result, 10);
+                });
+        });
+
+        it('should enter code in infinity loop', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+    });
+});

+ 3 - 6
test/functional-tests/custom-code-helpers/self-defending/templates/SelfDefendingTemplate.spec.ts

@@ -1,12 +1,13 @@
 import { assert } from 'chai';
 
-import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 
 import { evaluateInWorker } from '../../../../helpers/evaluateInWorker';
+import { beautifyCode } from '../../../../helpers/beautifyCode';
+import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 
@@ -131,11 +132,7 @@ describe('SelfDefendingTemplate', function () {
                     selfDefending: true
                 }
             ).getObfuscatedCode();
-            obfuscatedCode = obfuscatedCode
-                .replace(/function\(\){/g, 'function () {')
-                .replace(/=/g, ' = ')
-                .replace(/,/g, ', ')
-                .replace(/;/g, '; ');
+            obfuscatedCode = beautifyCode(obfuscatedCode);
 
             return evaluateInWorker(obfuscatedCode, evaluationTimeout)
                 .then((result: string | null) => {

+ 13 - 0
test/helpers/beautifyCode.ts

@@ -0,0 +1,13 @@
+/**
+ * Adds some spaces between some language constructions
+ *
+ * @param {string} code
+ * @returns {string}
+ */
+export function beautifyCode (code: string): string {
+    return code
+        .replace(/function\(\){/g, 'function () {')
+        .replace(/(!?=+)/g, ' $1 ')
+        .replace(/,/g, ', ')
+        .replace(/;/g, '; ');
+}

+ 1 - 0
test/index.spec.ts

@@ -61,6 +61,7 @@ import './functional-tests/custom-code-helpers/domain-lock/DomainLockCodeHelper.
 import './functional-tests/custom-code-helpers/domain-lock/templates/DomainLockNodeTemplate.spec';
 import './functional-tests/custom-code-helpers/self-defending/SelfDefendingCodeHelper.spec';
 import './functional-tests/custom-code-helpers/self-defending/templates/SelfDefendingTemplate.spec';
+import './functional-tests/custom-code-helpers/self-defending/templates/SelfDefendingNoEvalTemplate.spec';
 import './functional-tests/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.spec';
 import './functional-tests/custom-code-helpers/string-array/StringArrayRotateFunctionCodeHelper.spec';
 import './functional-tests/custom-code-helpers/string-array/StringArrayCodeHelper.spec';

+ 1 - 1
test/performance-tests/JavaScriptObfuscatorMemory.spec.ts

@@ -9,7 +9,7 @@ const heapValueToMB = (value: number) => Math.round(value / 1024 / 1024 * 100) /
 describe('JavaScriptObfuscator memory', function () {
     const iterationsCount: number = 500;
     const gcDiffThreshold: number = 10;
-    const allowedHeapDiffThreshold: number = 40;
+    const allowedHeapDiffThreshold: number = 50;
 
     this.timeout(100000);
 

部分文件因为文件数量过多而无法显示