Procházet zdrojové kódy

Updated logic for random string array function position

sanex před 3 roky
rodič
revize
c364415a13

+ 11 - 4
src/custom-code-helpers/string-array/StringArrayCodeHelper.ts

@@ -33,6 +33,12 @@ export class StringArrayCodeHelper extends AbstractCustomCodeHelper {
     @initializable()
     private stringArrayFunctionName!: string;
 
+    /**
+     * @type {string}
+     */
+    @initializable()
+    private stringArrayName!: string;
+
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
      * @param {ICustomCodeHelperFormatter} customCodeHelperFormatter
@@ -60,13 +66,16 @@ export class StringArrayCodeHelper extends AbstractCustomCodeHelper {
     /**
      * @param {IStringArrayStorage} stringArrayStorage
      * @param {string} stringArrayFunctionName
+     * @param {string} stringArrayName
      */
     public initialize (
         stringArrayStorage: IStringArrayStorage,
-        stringArrayFunctionName: string
+        stringArrayFunctionName: string,
+        stringArrayName: string
     ): void {
         this.stringArrayStorage = stringArrayStorage;
         this.stringArrayFunctionName = stringArrayFunctionName;
+        this.stringArrayName = stringArrayName;
     }
 
     /**
@@ -81,11 +90,9 @@ export class StringArrayCodeHelper extends AbstractCustomCodeHelper {
      * @returns {string}
      */
     protected override getCodeHelperTemplate (): string {
-        const stringArrayName: string = this.identifierNamesGenerator.generateNext();
-
         return this.customCodeHelperFormatter.formatTemplate(StringArrayTemplate(), {
             stringArrayFunctionName: this.stringArrayFunctionName,
-            stringArrayName: stringArrayName,
+            stringArrayName: this.stringArrayName,
             stringArrayStorageItems: this.getEncodedStringArrayStorageItems()
         });
     }

+ 10 - 3
src/custom-code-helpers/string-array/group/StringArrayCodeHelperGroup.ts

@@ -119,12 +119,18 @@ export class StringArrayCodeHelperGroup extends AbstractCustomCodeHelperGroup {
             return;
         }
 
+        const stringArrayFunctionName: string = this.stringArrayStorage.getStorageName();
+
         // stringArray helper initialize
         const stringArrayCodeHelper: ICustomCodeHelper<TInitialData<StringArrayCodeHelper>> =
             this.customCodeHelperFactory(CustomCodeHelper.StringArray);
-        const stringArrayFunctionName: string = this.stringArrayStorage.getStorageName();
+        const stringArrayName: string = this.identifierNamesGenerator.generateNext();
 
-        stringArrayCodeHelper.initialize(this.stringArrayStorage, stringArrayFunctionName);
+        stringArrayCodeHelper.initialize(
+            this.stringArrayStorage,
+            stringArrayFunctionName,
+            stringArrayName
+        );
         this.customCodeHelpers.set(CustomCodeHelper.StringArray, stringArrayCodeHelper);
 
         // stringArrayCallsWrapper helper initialize
@@ -133,6 +139,7 @@ export class StringArrayCodeHelperGroup extends AbstractCustomCodeHelperGroup {
             const stringArrayCallsWrapperCodeHelper: ICustomCodeHelper<TInitialData<StringArrayCallsWrapperCodeHelper>> =
                 this.customCodeHelperFactory(stringArrayCallsWrapperCodeHelperName);
             const stringArrayCallsWrapperName: string = this.stringArrayStorage.getStorageCallsWrapperName(stringArrayEncoding);
+
             stringArrayCallsWrapperCodeHelper.initialize(
                 stringArrayFunctionName,
                 stringArrayCallsWrapperName,
@@ -160,7 +167,7 @@ export class StringArrayCodeHelperGroup extends AbstractCustomCodeHelperGroup {
     private getScopeStatementRandomIndex (scopeStatements: TStatement[]): number {
         return this.randomGenerator.getRandomInteger(
             0,
-            Math.max(0, scopeStatements.length - 1)
+            Math.max(0, scopeStatements.length)
         );
     }
 }

+ 2 - 2
src/custom-code-helpers/string-array/templates/string-array-calls-wrapper/AtobTemplate.ts

@@ -17,12 +17,12 @@ export function AtobTemplate (selfDefending: boolean): string {
             for (
                 let bc = 0, bs, buffer, idx = 0;
                 buffer = input.charAt(idx++);
-                ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) 
+                ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4)
                     ? output += ${((): string => {
                         const basePart: string = 'String.fromCharCode(255 & bs >> (-2 * bc & 6))';
                         
                         return selfDefending
-                            ? `(func.charCodeAt(idx + 10) - 10 ? ${basePart} : bc)`
+                            ? `((func.charCodeAt(idx + 10) - 10 !== 0) ? ${basePart} : bc)`
                             : basePart;
                     })()}
                     : 0

+ 46 - 5
test/functional-tests/custom-code-helpers/string-array/StringArrayCallsWrapperCodeHelper.spec.ts

@@ -10,7 +10,48 @@ import { readFileAsString } from '../../../helpers/readFileAsString';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
 
 describe('StringArrayCallsWrapperCodeHelper', () => {
-    const regExp: RegExp = /_0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6} *- *0x0\;/;
+    const stringCallsWrapperRegExp: RegExp = /function _0x([a-f0-9]){4} *\(_0x([a-f0-9]){4,6} *,_0x([a-f0-9]){4,6}\)/;
+
+    describe('`stringArray` option is set', () => {
+        const samplesCount: number = 100;
+        const stringArrayCallsWrapperAtFirstPositionRegExp: RegExp = new RegExp(`^${stringCallsWrapperRegExp.source}`);
+        const variableDeclarationAtFirstPositionRegExp: RegExp = /^var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+
+        let obfuscatedCode: string;
+        let stringArrayCallsWrapperAtFirstPositionMatchesCount: number = 0;
+        let variableDeclarationAtFirstPositionMatchesCount: number = 0;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
+
+            for (let i = 0; i < samplesCount; i++) {
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+
+                if (obfuscatedCode.match(stringArrayCallsWrapperAtFirstPositionRegExp)) {
+                    stringArrayCallsWrapperAtFirstPositionMatchesCount++;
+                }
+
+                if (obfuscatedCode.match(variableDeclarationAtFirstPositionRegExp)) {
+                    variableDeclarationAtFirstPositionMatchesCount++;
+                }
+            }
+        });
+
+        it('Match #1: should correctly append code helper into the obfuscated code at random index', () => {
+            assert.isAbove(stringArrayCallsWrapperAtFirstPositionMatchesCount, 1);
+        });
+
+        it('Match #2: should correctly append code helper into the obfuscated code at random index', () => {
+            assert.isAbove(variableDeclarationAtFirstPositionMatchesCount, 1);
+        });
+    });
 
     describe('`stringArray` option is set', () => {
         let obfuscatedCode: string;
@@ -29,7 +70,7 @@ describe('StringArrayCallsWrapperCodeHelper', () => {
         });
 
         it('should correctly append code helper into the obfuscated code', () => {
-            assert.match(obfuscatedCode, regExp);
+            assert.match(obfuscatedCode, stringCallsWrapperRegExp);
         });
     });
 
@@ -49,15 +90,15 @@ describe('StringArrayCallsWrapperCodeHelper', () => {
         });
 
         it('shouldn\'t append code helper into the obfuscated code', () => {
-            assert.notMatch(obfuscatedCode, regExp);
+            assert.notMatch(obfuscatedCode, stringCallsWrapperRegExp);
         });
     });
 
     describe('Preserve string array name', () => {
         const callsWrapperRegExp: RegExp = new RegExp(`` +
-            `function *b *\\(c, *d\\) *{ *` +
+            `function *c *\\(b, *d\\) *{ *` +
                 `var e *= *a\\(\\); *` +
-                `b *= *function *\\(f, *g\\) *{` +
+                `c *= *function *\\(f, *g\\) *{` +
                     `f *= *f *- *0x0; *` +
                     `var h *= *e\\[f]; *` +
         ``);

+ 31 - 11
test/functional-tests/custom-code-helpers/string-array/StringArrayCodeHelper.spec.ts

@@ -8,26 +8,46 @@ import { readFileAsString } from '../../../helpers/readFileAsString';
 import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
 
 describe('StringArrayCodeHelper', () => {
-    const regExp: RegExp = getStringArrayRegExp(['test']);
+    const stringArrayRegExp: RegExp = getStringArrayRegExp(['test']);
 
     describe('`stringArray` option is set', () => {
+        const samplesCount: number = 100;
+        const stringArrayAtFirstPositionRegExp: RegExp = new RegExp(`^${stringArrayRegExp.source}`);
+        const variableDeclarationAtFirstPositionRegExp: RegExp = /^var test *= *_0x([a-f0-9]){4}\(0x0\);/;
+
         let obfuscatedCode: string;
+        let stringArrayAtFirstPositionMatchesCount: number = 0;
+        let variableDeclarationAtFirstPositionMatchesCount: number = 0;
 
         before(() => {
             const code: string = readFileAsString(__dirname + '/fixtures/simple-input.js');
 
-            obfuscatedCode = JavaScriptObfuscator.obfuscate(
-                code,
-                {
-                    ...NO_ADDITIONAL_NODES_PRESET,
-                    stringArray: true,
-                    stringArrayThreshold: 1
+            for (let i = 0; i < samplesCount; i++) {
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        stringArray: true,
+                        stringArrayThreshold: 1
+                    }
+                ).getObfuscatedCode();
+
+                if (obfuscatedCode.match(stringArrayAtFirstPositionRegExp)) {
+                    stringArrayAtFirstPositionMatchesCount++;
                 }
-            ).getObfuscatedCode();
+
+                if (obfuscatedCode.match(variableDeclarationAtFirstPositionRegExp)) {
+                    variableDeclarationAtFirstPositionMatchesCount++;
+                }
+            }
+        });
+
+        it('Match #1: should correctly append code helper into the obfuscated code at random index', () => {
+            assert.isAbove(stringArrayAtFirstPositionMatchesCount, 1);
         });
 
-        it('should correctly append code helper into the obfuscated code', () => {
-            assert.match(obfuscatedCode, regExp);
+        it('Match #2: should correctly append code helper into the obfuscated code at random index', () => {
+            assert.isAbove(variableDeclarationAtFirstPositionMatchesCount, 1);
         });
     });
 
@@ -47,7 +67,7 @@ describe('StringArrayCodeHelper', () => {
         });
 
         it('shouldn\'t append code helper into the obfuscated code', () => {
-            assert.notMatch(obfuscatedCode, regExp);
+            assert.notMatch(obfuscatedCode, stringArrayRegExp);
         });
     });
 });

+ 107 - 5
test/functional-tests/custom-code-helpers/string-array/templates/string-array-calls-wrapper-node-template/StringArrayCallsWrapperTemplate.spec.ts

@@ -207,7 +207,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
             const selfDefendingEnabled: boolean = true;
 
             describe('Variant #1: correct code evaluation for single-line code', () => {
-                describe('Variant #1: index shift amount is `0`', () => {
+                describe('Variant #1: long decoded string', () => {
                     const index: string = '0x0';
 
                     const indexShiftAmount: number = 0;
@@ -258,10 +258,62 @@ describe('StringArrayCallsWrapperTemplate', () => {
                         assert.deepEqual(decodedValue, expectedDecodedValue);
                     });
                 });
+
+                describe('Variant #2: 3-characters decoded string', () => {
+                    const index: string = '0x0';
+
+                    const indexShiftAmount: number = 0;
+
+                    const expectedDecodedValue: string = 'foo';
+
+                    let decodedValue: string;
+
+                    before(async() => {
+                        const stringArrayTemplate = format(StringArrayTemplate(), {
+                            stringArrayName,
+                            stringArrayFunctionName,
+                            stringArrayStorageItems: `'${cryptUtilsSwappedAlphabet.btoa('foo')}'`
+                        });
+                        const atobPolyfill = format(AtobTemplate(selfDefendingEnabled), {
+                            atobFunctionName
+                        });
+                        const atobDecodeTemplate: string = format(
+                            StringArrayBase64DecodeTemplate(randomGenerator),
+                            {
+                                atobPolyfill,
+                                atobFunctionName,
+                                selfDefendingCode: '',
+                                stringArrayCacheName,
+                                stringArrayCallsWrapperName
+                            }
+                        );
+                        const stringArrayCallsWrapperTemplate: string = await minimizeCode(
+                            format(StringArrayCallsWrapperTemplate(), {
+                                decodeCodeHelperTemplate: atobDecodeTemplate,
+                                indexShiftAmount,
+                                stringArrayCacheName,
+                                stringArrayCallsWrapperName,
+                                stringArrayFunctionName
+                            })
+                        );
+
+                        decodedValue = Function(`
+                            ${stringArrayTemplate}
+                        
+                            ${stringArrayCallsWrapperTemplate}
+                            
+                            return ${stringArrayCallsWrapperName}(${index});
+                        `)();
+                    });
+
+                    it('should correctly return decoded value', () => {
+                        assert.deepEqual(decodedValue, expectedDecodedValue);
+                    });
+                });
             });
 
             describe('Variant #2: invalid code evaluation for multi-line code', () => {
-                describe('Variant #1: index shift amount is `0`', () => {
+                describe('Variant #1: long decoded string', () => {
                     const index: string = '0x0';
 
                     const indexShiftAmount: number = 0;
@@ -306,7 +358,57 @@ describe('StringArrayCallsWrapperTemplate', () => {
                         `)();
                     });
 
-                    it('should correctly return decoded value', () => {
+                    it('should return invalid decoded value', () => {
+                        assert.deepEqual(decodedValue, expectedDecodedValue);
+                    });
+                });
+
+                describe('Variant #2: 3-characters decoded string', () => {
+                    const index: string = '0x0';
+
+                    const indexShiftAmount: number = 0;
+
+                    const expectedDecodedValue: string = 'foo';
+
+                    let decodedValue: string;
+
+                    before(() => {
+                        const stringArrayTemplate = format(StringArrayTemplate(), {
+                            stringArrayName,
+                            stringArrayFunctionName,
+                            stringArrayStorageItems: `'${cryptUtilsSwappedAlphabet.btoa('foo')}'`
+                        });
+                        const atobPolyfill = format(AtobTemplate(selfDefendingEnabled), {
+                            atobFunctionName
+                        });
+                        const atobDecodeTemplate: string = format(
+                            StringArrayBase64DecodeTemplate(randomGenerator),
+                            {
+                                atobPolyfill,
+                                atobFunctionName,
+                                selfDefendingCode: '',
+                                stringArrayCacheName,
+                                stringArrayCallsWrapperName
+                            }
+                        );
+                        const stringArrayCallsWrapperTemplate: string = format(StringArrayCallsWrapperTemplate(), {
+                            decodeCodeHelperTemplate: atobDecodeTemplate,
+                            indexShiftAmount,
+                            stringArrayCacheName,
+                            stringArrayCallsWrapperName,
+                            stringArrayFunctionName
+                        });
+
+                        decodedValue = Function(`
+                            ${stringArrayTemplate}
+                        
+                            ${stringArrayCallsWrapperTemplate}
+                            
+                            return ${stringArrayCallsWrapperName}(${index});
+                        `)();
+                    });
+
+                    it('should return invalid decoded value', () => {
                         assert.deepEqual(decodedValue, expectedDecodedValue);
                     });
                 });
@@ -435,7 +537,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
             const selfDefendingEnabled: boolean = true;
 
             describe('Variant #1: correct code evaluation for single-line code', () => {
-                describe('Variant #1: index shift amount is `0`', () => {
+                describe('Variant #1: long decoded string', () => {
                     const index: string = '0x0';
                     const key: string = 'key';
 
@@ -497,7 +599,7 @@ describe('StringArrayCallsWrapperTemplate', () => {
             });
 
             describe('Variant #2: invalid code evaluation for multi-line code', () => {
-                describe('Variant #1: index shift amount is `0`', () => {
+                describe('Variant #1: long decoded string', () => {
                     const index: string = '0x0';
                     const key: string = 'key';