Bläddra i källkod

Merge pull request #740 from javascript-obfuscator/string-array-calls-wrapper-mangled-generator-fix

Fixed `stringArrayCallsWrapper` option with `mangled` names generator
Timofey Kachalov 4 år sedan
förälder
incheckning
78383e7cb3

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
dist/index.browser.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
dist/index.cli.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
dist/index.js


+ 2 - 2
package.json

@@ -57,7 +57,7 @@
     "@types/mkdirp": "1.0.1",
     "@types/mocha": "8.0.3",
     "@types/multimatch": "4.0.0",
-    "@types/node": "14.6.4",
+    "@types/node": "14.10.0",
     "@types/rimraf": "3.0.0",
     "@types/sinon": "9.0.5",
     "@types/string-template": "1.0.2",
@@ -69,7 +69,7 @@
     "coveralls": "3.1.0",
     "eslint": "7.8.1",
     "eslint-plugin-import": "2.22.0",
-    "eslint-plugin-jsdoc": "30.3.3",
+    "eslint-plugin-jsdoc": "30.4.0",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-prefer-arrow": "1.2.2",
     "eslint-plugin-unicorn": "21.0.0",

+ 69 - 3
src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts

@@ -60,14 +60,68 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
     }
 
     /**
-     * We can only ignore limited nameLength, it has no sense here
+     * @param {string} nextName
+     * @param {string} prevName
+     * @returns {boolean}
+     */
+    public static isIncrementedMangledName (nextName: string, prevName: string): boolean {
+        if (nextName === prevName) {
+            return false;
+        }
+
+        const nextNameLength: number = nextName.length;
+        const prevNameLength: number = prevName.length;
+
+        if (nextNameLength !== prevNameLength) {
+            return nextNameLength > prevNameLength;
+        }
+
+        for (let i: number = 0; i < nextNameLength; i++) {
+            const nextNameCharacter: string = nextName[i];
+            const prevNameCharacter: string = prevName[i];
+
+            if (nextNameCharacter === prevNameCharacter) {
+                continue;
+            }
+
+            const isUpperCaseNextNameCharacter: boolean = MangledIdentifierNamesGenerator.isUpperCaseCharacter(nextNameCharacter);
+            const isUpperCasePrevNameCharacter: boolean = MangledIdentifierNamesGenerator.isUpperCaseCharacter(prevNameCharacter);
+
+            if (
+                isUpperCaseNextNameCharacter
+                && !isUpperCasePrevNameCharacter
+            ) {
+                return true;
+            } else if (
+                !isUpperCaseNextNameCharacter
+                && isUpperCasePrevNameCharacter
+            ) {
+                return false;
+            }
+        }
+
+        return nextName > prevName;
+    }
+
+    /**
+     * @param {string} character
+     * @returns {boolean}
+     */
+    private static isUpperCaseCharacter (string: string): boolean {
+        return /^[A-Z]*$/.test(string);
+    }
+
+    /**
+     * Generates next name based on a global previous mangled name
+     * We can ignore nameLength parameter here, it hasn't sense with this generator
+     *
      * @param {number} nameLength
      * @returns {string}
      */
     public generateNext (nameLength?: number): string {
         const identifierName: string = this.generateNewMangledName(this.previousMangledName);
 
-        this.previousMangledName = identifierName;
+        this.updatePreviousMangledName(identifierName);
         this.preserveName(identifierName);
 
         return identifierName;
@@ -84,7 +138,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
         const identifierName: string = this.generateNewMangledName(this.previousMangledName);
         const identifierNameWithPrefix: string = `${prefix}${identifierName}`;
 
-        this.previousMangledName = identifierName;
+        this.updatePreviousMangledName(identifierName);
 
         if (!this.isValidIdentifierName(identifierNameWithPrefix)) {
             return this.generateForGlobalScope(nameLength);
@@ -116,6 +170,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
 
         MangledIdentifierNamesGenerator.lastMangledNameInScopeMap.set(lexicalScopeNode, identifierName);
 
+        this.updatePreviousMangledName(identifierName);
         this.preserveNameForLexicalScope(identifierName, lexicalScopeNode);
 
         return identifierName;
@@ -137,6 +192,17 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
         return MangledIdentifierNamesGenerator.nameSequence;
     }
 
+    /**
+     * @param {string} name
+     */
+    protected updatePreviousMangledName (name: string): void {
+        if (!MangledIdentifierNamesGenerator.isIncrementedMangledName(name, this.previousMangledName)) {
+            return;
+        }
+
+        this.previousMangledName = name;
+    }
+
     /**
      * @param {string} previousMangledName
      * @returns {string}

+ 1 - 1
src/node-transformers/string-array-transformers/StringArrayTransformer.ts

@@ -268,7 +268,7 @@ export class StringArrayTransformer extends AbstractNodeTransformer {
         const isFilledScopeCallsWrapperNamesList: boolean = stringArrayScopeCallsWrapperNames.length === this.options.stringArrayWrappersCount;
 
         if (!isFilledScopeCallsWrapperNamesList) {
-            const nextScopeCallsWrapperName: string = this.identifierNamesGenerator.generateForLexicalScope(currentLexicalScopeNode);
+            const nextScopeCallsWrapperName: string = this.identifierNamesGenerator.generateNext();
 
             stringArrayScopeCallsWrapperNames.push(nextScopeCallsWrapperName);
             stringArrayScopeCallsWrapperDataByEncoding[encoding] = {

+ 27 - 9
test/dev/dev.ts

@@ -1,28 +1,46 @@
 'use strict';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-            const foo = 'foo';
-            const bar = 'bar';
+            const foo = 'aaa';
+
+            function test (a, b) {
+                const bar = 'bbb';
+                
+                function test1 (a, b) {
+                    const baz = 'ccc';
+                    
+                    function test2 (a, b) {
+                        const bark = 'ddd';
+                        
+                        return bark;
+                    }
                     
-            function test () {
-                const baz = 'baz';
-                const bark = 'bark';
+                    return baz + test2();
+                }
+                
+                return bar + test1();
+            }
             
-                function test1() {
-                    const hawk = 'hawk';
-                    const eagle = 'eagle';
-                } 
+            function test3 (a, b) {
+                const hawk = 'eee';
+                
+                return hawk;
             }
+            
+            foo + test() + test3();
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
+            identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+            renameGlobals: true,
             stringArray: true,
             stringArrayThreshold: 1,
             stringArrayWrappersChainedCalls: true,

+ 194 - 21
test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/StringArrayTransformer.spec.ts

@@ -256,41 +256,214 @@ describe('StringArrayTransformer', function () {
         });
 
         describe('Variant #5: `stringArrayWrappersChainedCalls` option is enabled', () => {
-            describe('Variant #1: correct evaluation of the string array wrappers chained calls', () => {
-                const samplesCount: number = 50;
-                const expectedEvaluationResult: string = 'aaabbbcccdddeee';
-                let isEvaluationSuccessful: boolean = true;
+            describe('Variant #1: correct chained calls', () => {
+                describe('Variant #1: `Mangled` identifier names generator', () => {
+                    const stringArrayCallRegExp: RegExp = new RegExp(
+                        'const q *= *b;' +
+                        'const foo *= *q\\(\'0x0\'\\);' +
+                        'function test\\(c, *d\\) *{' +
+                            'const r *= *q;' +
+                            'const e *= *r\\(\'0x1\'\\);' +
+                            'const f *= *r\\(\'0x2\'\\);' +
+                            'function g\\(h, *i\\) *{' +
+                                'const s *= *r;' +
+                                'const j *= *s\\(\'0x3\'\\);' +
+                                'const k *= *s\\(\'0x4\'\\);' +
+                                'function l\\(m, *n *\\) *{' +
+                                    'const t *= *s;' +
+                                    'const o *= *t\\(\'0x3\'\\);' +
+                                    'const p *= *t\\(\'0x4\'\\);' +
+                                    'return o *\\+ *p;' +
+                                '}' +
+                                'return j *\\+ *k;' +
+                            '}' +
+                            'return e *\\+ *f *\\+ *g\\(\\);' +
+                        '}'
+                    );
 
-                before(() => {
-                    const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-eval.js');
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-1.js');
 
-                    for (let i = 0; i < samplesCount; i++) {
-                        const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
                             code,
                             {
                                 ...NO_ADDITIONAL_NODES_PRESET,
+                                identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
                                 stringArray: true,
                                 stringArrayThreshold: 1,
-                                stringArrayEncoding: [
-                                    StringArrayEncoding.None,
-                                    StringArrayEncoding.Rc4
-                                ],
                                 stringArrayWrappersChainedCalls: true,
-                                stringArrayWrappersCount: 5
+                                stringArrayWrappersCount: 1
                             }
                         ).getObfuscatedCode();
+                    });
 
-                        const evaluationResult: string = eval(obfuscatedCode);
+                    it('should add correct scope calls wrappers', () => {
+                        assert.match(obfuscatedCode, stringArrayCallRegExp);
+                    });
+                });
+            });
 
-                        if (evaluationResult !== expectedEvaluationResult) {
-                            isEvaluationSuccessful = false;
-                            break;
-                        }
-                    }
+            describe('Variant #2: correct evaluation of the string array wrappers chained calls', () => {
+                describe('Variant #1: base', () => {
+                    describe('Variant #1: `Hexadecimal` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-1.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+
+                    describe('Variant #2: `Mangled` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-1.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
                 });
 
-                it('should correctly evaluate string array wrappers chained calls', () => {
-                    assert.equal(isEvaluationSuccessful, true);
+                describe('Variant #2: advanced', () => {
+                    describe('Variant #1: `Hexadecimal` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-2.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
+
+                    describe('Variant #2: `Mangled` identifier names generator', () => {
+                        const samplesCount: number = 50;
+                        const expectedEvaluationResult: string = 'aaabbbcccdddeee';
+                        let isEvaluationSuccessful: boolean = true;
+
+                        before(() => {
+                            const code: string = readFileAsString(__dirname + '/fixtures/string-array-wrappers-chained-calls-2.js');
+
+                            for (let i = 0; i < samplesCount; i++) {
+                                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                                    code,
+                                    {
+                                        ...NO_ADDITIONAL_NODES_PRESET,
+                                        identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
+                                        stringArray: true,
+                                        stringArrayThreshold: 1,
+                                        stringArrayEncoding: [
+                                            StringArrayEncoding.None,
+                                            StringArrayEncoding.Rc4
+                                        ],
+                                        stringArrayWrappersChainedCalls: true,
+                                        stringArrayWrappersCount: 5
+                                    }
+                                ).getObfuscatedCode();
+
+                                const evaluationResult: string = eval(obfuscatedCode);
+
+                                if (evaluationResult !== expectedEvaluationResult) {
+                                    isEvaluationSuccessful = false;
+                                    break;
+                                }
+                            }
+                        });
+
+                        it('should correctly evaluate string array wrappers chained calls', () => {
+                            assert.equal(isEvaluationSuccessful, true);
+                        });
+                    });
                 });
             });
         });

+ 24 - 0
test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/fixtures/string-array-wrappers-chained-calls-1.js

@@ -0,0 +1,24 @@
+const foo = 'aaa';
+
+function test (a, b) {
+    const bar = 'bbb';
+    const baz = 'ccc';
+
+    function test1 (a, b) {
+        const bark = 'ddd';
+        const hawk = 'eee';
+
+        function test2 (a, b) {
+            const bark = 'ddd';
+            const hawk = 'eee';
+
+            return bark + hawk;
+        }
+
+        return bark + hawk;
+    }
+
+    return bar + baz + test1();
+}
+
+foo + test();

+ 27 - 0
test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/fixtures/string-array-wrappers-chained-calls-2.js

@@ -0,0 +1,27 @@
+const foo = 'aaa';
+
+function test (a, b) {
+    const bar = 'bbb';
+
+    function test1 (a, b) {
+        const baz = 'ccc';
+
+        function test2 (a, b) {
+            const bark = 'ddd';
+
+            return bark;
+        }
+
+        return baz + test2();
+    }
+
+    return bar + test1();
+}
+
+function test3 (a, b) {
+    const hawk = 'eee';
+
+    return hawk;
+}
+
+foo + test() + test3();

+ 0 - 17
test/functional-tests/node-transformers/string-array-transformers/string-array-transformer/fixtures/string-array-wrappers-chained-calls-eval.js

@@ -1,17 +0,0 @@
-const foo = 'aaa';
-
-function test () {
-    const bar = 'bbb';
-    const baz = 'ccc';
-
-    function test1 () {
-        const bark = 'ddd';
-        const hawk = 'eee';
-
-        return bark + hawk;
-    }
-
-    return bar + baz + test1();
-}
-
-foo + test();

+ 60 - 1
test/unit-tests/generators/identifier-names-generators/MangledlIdentifierNamesGenerator.spec.ts

@@ -10,6 +10,7 @@ import { IInversifyContainerFacade } from '../../../../src/interfaces/container/
 import { IdentifierNamesGenerator } from '../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
 
 import { InversifyContainerFacade } from '../../../../src/container/InversifyContainerFacade';
+import { MangledIdentifierNamesGenerator } from '../../../../src/generators/identifier-names-generators/MangledIdentifierNamesGenerator';
 
 describe('MangledIdentifierNamesGenerator', () => {
     describe('generateNext', () => {
@@ -170,7 +171,65 @@ describe('MangledIdentifierNamesGenerator', () => {
         });
     });
 
-    describe('isValidIdentifierName (identifierName: string): boolean', () => {
+    describe('isIncrementedMangledName', () => {
+        const names: [nameA: string, nameB: string, result: boolean][] = [
+            ['aa', 'aa', false],
+
+            ['a', '9', true],
+            ['9', 'a', false],
+
+            ['b', 'a', true],
+            ['a', 'b', false],
+
+            ['A', 'z', true],
+            ['z', 'A', false],
+
+            ['B', 'A', true],
+            ['A', 'B', false],
+
+            ['a0', 'Z', true],
+            ['Z', 'a0', false],
+
+            ['a9', 'a0', true],
+            ['a0', 'a9', false],
+
+            ['z0', 'a0', true],
+            ['a0', 'z0', false],
+
+            ['a0', 'a', true],
+            ['a', 'a0', false],
+
+            ['A0', 'a0', true],
+            ['a0', 'A0', false],
+
+            ['z1', 'a0', true],
+            ['a0', 'z1', false],
+
+            ['aa0', 'ZZ', true],
+            ['ZZ', 'aa0', false],
+
+            ['aaA', 'aa0', true],
+            ['aa0', 'aaA', false]
+        ];
+
+        names.forEach(([nameA, nameB, expectedResult], index: number) => {
+            describe(`Variant #${index + 1}: \`${nameA}\` and \`${nameB}\``, () => {
+                let result: boolean;
+
+                beforeEach(() => {
+                    console.time();
+                    result = MangledIdentifierNamesGenerator.isIncrementedMangledName(nameA, nameB);
+                    console.timeEnd();
+                });
+
+                it('should compare mangled names', () => {
+                    assert.equal(result, expectedResult);
+                });
+            });
+        })
+    });
+
+    describe('isValidIdentifierName', () => {
         describe('Variant #1: reserved name as simple string', () => {
             const expectedFirstIdentifier: string = 'a';
             const expectedSecondIdentifier: string = 'd';

+ 8 - 8
yarn.lock

@@ -415,10 +415,10 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d"
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
 
-"@types/node@14.6.4":
-  version "14.6.4"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a"
-  integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==
+"@types/node@14.10.0":
+  version "14.10.0"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.0.tgz#15815dff82c8dc30827f6b1286f865902945095a"
+  integrity sha512-SOIyrdADB4cq6eY1F+9iU48iIomFAPltu11LCvA9PKcyEwHadjCFzNVPotAR+oEJA0bCP4Xvvgy+vwu1ZjVh8g==
 
 "@types/normalize-package-data@^2.4.0":
   version "2.4.0"
@@ -2035,10 +2035,10 @@ [email protected]:
     resolve "^1.17.0"
     tsconfig-paths "^3.9.0"
 
-eslint-plugin-jsdoc@30.3.3:
-  version "30.3.3"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.3.3.tgz#25ddf2e59164a6182c5a9e12e6560d69e252013d"
-  integrity sha512-u2z0LUTm00KLrWB2h7y+hkzYcTIDIi87efXRnv36cFvs23hIOuT77FdZNDBxbB4dxquHBNsyxLpyEuERYUVNEA==
+eslint-plugin-jsdoc@30.4.0:
+  version "30.4.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.4.0.tgz#3c84f59b6a04eaef2b636f75eeee905d16b7f2f9"
+  integrity sha512-eb22QADWcISPQJwFJ+rUAl1NXdyOq3qy0Cp0+MZzpwlqFgJ+eJ7Fd/jYTfwDuN8QyFWumuyzSpwQBnF4PfM9Wg==
   dependencies:
     comment-parser "^0.7.6"
     debug "^4.1.1"

Vissa filer visades inte eftersom för många filer har ändrats