Browse Source

Fixed very rare cases when `rotateStringArray` couldn't rotate array properly

sanex 3 years ago
parent
commit
6e1ce07b5e

+ 1 - 0
CHANGELOG.md

@@ -2,6 +2,7 @@ Change Log
 
 v2.19.0
 ---
+* Fixed very rare cases when `rotateStringArray` couldn't rotate array properly
 * Improved `selfDefending` option
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/959
 

File diff suppressed because it is too large
+ 0 - 0
dist/index.browser.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 27 - 8
src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer.ts

@@ -219,30 +219,40 @@ export class StringArrayRotateFunctionTransformer extends AbstractNodeTransforme
      * @returns {TStatement}
      */
     private getStringArrayRotateFunctionNode (): TStatement {
-        const comparisonValue: number = this.randomGenerator.getRandomInteger(100000, 1_000_000);
+        const comparisonValue: number = this.getComparisonValue();
         const comparisonExpressionNumberNumericalExpressionData: TNumberNumericalExpressionData =
             this.numberNumericalExpressionAnalyzer.analyze(
                 comparisonValue,
                 StringArrayRotateFunctionTransformer.comparisonExpressionAdditionalPartsCount
             );
 
+        let index: number = 1;
         const comparisonExpressionNode: ESTree.Expression = NumericalExpressionDataToNodeConverter.convertIntegerNumberData(
             comparisonExpressionNumberNumericalExpressionData,
             ((number: number, isPositiveNumber) => {
+                const multipliedNumber: number = number * index;
                 const literalNode: ESTree.Literal = NodeFactory.literalNode(
-                    `${number}${this.randomGenerator.getRandomString(6)}`
+                    `${multipliedNumber}${this.randomGenerator.getRandomString(6)}`
                 );
                 const parseIntCallExpression: ESTree.CallExpression = NodeFactory.callExpressionNode(
                     NodeFactory.identifierNode('parseInt'),
                     [literalNode]
                 );
 
-                return isPositiveNumber
-                    ? parseIntCallExpression
-                    : NodeFactory.unaryExpressionNode(
-                        '-',
-                        parseIntCallExpression
-                    );
+                const binaryExpressionNode: ESTree.BinaryExpression = NodeFactory.binaryExpressionNode(
+                    '/',
+                    isPositiveNumber
+                        ? parseIntCallExpression
+                        : NodeFactory.unaryExpressionNode(
+                            '-',
+                            parseIntCallExpression
+                        ),
+                    NodeFactory.literalNode(index, index.toString())
+                );
+
+                index++;
+
+                return binaryExpressionNode;
             })
         );
 
@@ -265,4 +275,13 @@ export class StringArrayRotateFunctionTransformer extends AbstractNodeTransforme
     private isComparisonExpressionStringLiteralNode (stringLiteralNode: TStringLiteralNode): boolean {
         return /\d/.test(stringLiteralNode.value);
     }
+
+    /**
+     * Extracted to a standalone method to correctly stub this behaviour
+     *
+     * @returns {number}
+     */
+    private getComparisonValue (): number {
+        return this.randomGenerator.getRandomInteger(100000, 1_000_000);
+    }
 }

+ 12 - 34
test/dev/dev.ts

@@ -1,47 +1,25 @@
 'use strict';
 
-import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
+import { readFileAsString } from '../helpers/readFileAsString';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
+    const code: string = readFileAsString(__dirname + '/../functional-tests/javascript-obfuscator/fixtures/custom-nodes-identifier-names-collision.js');
 
-    let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-        `
-            class Test {
-                constructor () {
-                    let test = {}
-                }
-                
-                static methodA = () => {
-                    console.log('method_A');
-                }
-                
-                methodB () {
-                    console.log('method_B');
-                    
-                    Test.methodA();
-                }
-            }
-            
-            const instance = new Test();
-            
-            Test.methodA();
-            instance.methodB();
-        `,
+    let obfuscationResult = JavaScriptObfuscator.obfuscate(
+        code,
         {
-            ...NO_ADDITIONAL_NODES_PRESET,
+            identifierNamesGenerator: 'mangled',
             compact: false,
-            simplify: false,
             stringArray: true,
-            stringArrayThreshold: 1,
-            stringArrayEncoding: ['rc4', 'base64'],
-            stringArrayWrappersCount: 5,
-            rotateStringArray: true,
-            identifierNamesGenerator: 'mangled',
-            selfDefending: true
+            seed: 429105580
         }
-    ).getObfuscatedCode();
+    );
+
+    let obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
+    let identifierNamesCache = obfuscationResult.getIdentifierNamesCache();
 
     console.log(obfuscatedCode);
     console.log(eval(obfuscatedCode));
-})();
+    console.log(identifierNamesCache);
+})();

+ 59 - 1
test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/StringArrayRotateFunctionTransformer.spec.ts

@@ -1,7 +1,9 @@
 import { assert } from 'chai';
+import * as sinon from 'sinon';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
+import { evaluateInWorker } from '../../../../helpers/evaluateInWorker';
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 import { IdentifierNamesGenerator } from '../../../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
@@ -10,8 +12,12 @@ import { StringArrayIndexesType } from '../../../../../src/enums/node-transforme
 import { StringArrayWrappersType } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayWrappersType';
 
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
+import { NumberNumericalExpressionAnalyzer } from '../../../../../src/analyzers/number-numerical-expression-analyzer/NumberNumericalExpressionAnalyzer';
+import { StringArrayRotateFunctionTransformer } from '../../../../../src/node-transformers/string-array-transformers/StringArrayRotateFunctionTransformer';
+
+describe('StringArrayRotateFunctionTransformer', function () {
+    this.timeout(120000);
 
-describe('StringArrayRotateFunctionTransformer', () => {
     describe('Code helper append', () => {
         const regExp: RegExp = /while *\(!!\[]\) *\{/;
 
@@ -195,5 +201,57 @@ describe('StringArrayRotateFunctionTransformer', () => {
                 assert.equal(hasRuntimeErrors, false);
             });
         });
+
+        describe('Prevent early successful comparison', () => {
+            const evaluationTimeout:  number = 1000;
+            const samplesCount: number = 100;
+
+            let numberNumericalExpressionAnalyzerAnalyzeStub: sinon.SinonStub;
+            let stringArrayRotateFunctionTransformerGetComparisonValueStub: sinon.SinonStub;
+
+            let obfuscatedCode: string;
+            let evaluationError: Error | null = null;
+
+            before(async () => {
+                stringArrayRotateFunctionTransformerGetComparisonValueStub = sinon
+                    .stub(<any>StringArrayRotateFunctionTransformer.prototype, 'getComparisonValue')
+                    .returns(5);
+                numberNumericalExpressionAnalyzerAnalyzeStub = sinon
+                    .stub(NumberNumericalExpressionAnalyzer.prototype, 'analyze')
+                    .returns([[1, 2], 0, 3]);
+
+                const code: string = readFileAsString(__dirname + '/fixtures/early-successful-comparison.js');
+
+                for (let i = 0; i < samplesCount; i++) {
+                    obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            seed: i,
+                            rotateStringArray: true,
+                            shuffleStringArray: true,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+
+                    try {
+                        await evaluateInWorker(obfuscatedCode, evaluationTimeout);
+                    } catch (error) {
+                        evaluationError = error
+
+                        break;
+                    }
+                }
+            });
+
+            it('should correctly evaluate code', () => {
+                assert.equal(evaluationError, null);
+            });
+
+            after(() => {
+                numberNumericalExpressionAnalyzerAnalyzeStub.restore();
+                stringArrayRotateFunctionTransformerGetComparisonValueStub.restore();
+            })
+        });
     });
 });

+ 4 - 0
test/functional-tests/node-transformers/string-array-transformers/string-array-rotate-function-transformer/fixtures/early-successful-comparison.js

@@ -0,0 +1,4 @@
+function hi() {
+    console.log("Hello World!");
+}
+hi();

+ 5 - 3
test/runtime-tests/JavaScriptObfuscatorRuntime.spec.ts

@@ -207,14 +207,16 @@ describe('JavaScriptObfuscator runtime eval', function () {
             beforeEach(() => {
                 const code: string = readFileAsString(process.cwd() + '/dist/index.js');
 
-                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                const obfuscationResult = JavaScriptObfuscator.obfuscate(
                     code,
                     {
                         ...baseOptions,
                         ...options,
                         renameProperties: false
                     }
-                ).getObfuscatedCode();
+                );
+                const obfuscatorOptions = obfuscationResult.getOptions();
+                const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
 
                 return evaluateInWorker(
                     `
@@ -232,7 +234,7 @@ describe('JavaScriptObfuscator runtime eval', function () {
                         evaluationResult = result;
                     })
                     .catch((error: Error) => {
-                        evaluationResult = `${error.message}. ${error.stack}. Code: ${obfuscatedCode}`;
+                        evaluationResult = `${error.message}. ${error.stack}. Options: ${JSON.stringify(obfuscatorOptions)} Code: ${obfuscationResult}`;
                     });
             });
 

Some files were not shown because too many files changed in this diff