Browse Source

Implemented more correct way to store AST from eval expressions that allows recursive eval expressions obfuscation

sanex3339 7 years ago
parent
commit
afe8832bbb

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


+ 7 - 5
src/node-transformers/finalizing-transformers/AstToEvalCallExpressionTransformer.ts

@@ -9,6 +9,7 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
+import { NodeGuards } from '../../node/NodeGuards';
 import { Nodes } from '../../node/Nodes';
 import { NodeUtils } from '../../node/NodeUtils';
 
@@ -30,8 +31,8 @@ export class AstToEvalCallExpressionTransformer extends AbstractNodeTransformer
      */
     public getVisitor (): IVisitor {
         return {
-            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
-                if (parentNode && node.isEvalRoot) {
+            leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                if (parentNode && node.isEvalRoot && NodeGuards.isFunctionDeclarationNode(node)) {
                     return this.transformNode(node, parentNode);
                 }
             }
@@ -39,12 +40,13 @@ export class AstToEvalCallExpressionTransformer extends AbstractNodeTransformer
     }
 
     /**
-     * @param {ExpressionStatement} node
+     * @param {FunctionDeclaration} functionDeclaration
      * @param {Node} parentNode
      * @returns {Node}
      */
-    public transformNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
-        const code: string = NodeUtils.convertStructureToCode([node]);
+    public transformNode (functionDeclaration: ESTree.FunctionDeclaration, parentNode: ESTree.Node): ESTree.Node {
+        const targetAst: ESTree.Statement[] = functionDeclaration.body.body;
+        const code: string = NodeUtils.convertStructureToCode(targetAst);
 
         return Nodes.getCallExpressionNode(
             Nodes.getIdentifierNode('eval'),

+ 7 - 3
src/node-transformers/preparing-transformers/EvaCallExpressionToAstTransformer.ts

@@ -63,10 +63,14 @@ export class EvalCallExpressionToAstTransformer extends AbstractNodeTransformer
 
         const code: string = callExpressionFirstArgument.value;
         const ast: TStatement[] = NodeUtils.convertCodeToStructure(code);
-        const programNode: ESTree.Program = Nodes.getProgramNode(ast);
+        const functionDeclaration: ESTree.FunctionDeclaration = Nodes.getFunctionDeclarationNode(
+            'evalRoot',
+            [],
+            Nodes.getBlockStatementNode(<any>ast)
+        );
 
-        programNode.isEvalRoot = true;
+        functionDeclaration.isEvalRoot = true;
 
-        return programNode;
+        return functionDeclaration;
     }
 }

+ 5 - 7
test/dev/dev.ts

@@ -7,19 +7,17 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
         (function(){
-            function foo (bar) {
-                eval('console.log(bar, \\'baz\\');');
-                console.log(eval('bar; console.log(\\'bark\\')'));
+            function foo (a, b) {
+                return eval('var c = a + b; eval(\\'a + c\\');');
+
             }
             
-            foo(1);
+            console.log(foo(1, 2));
         })();
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            compact: false,
-            stringArray: true,
-            stringArrayThreshold: 1
+            compact: false
         }
     ).getObfuscatedCode();
 

+ 87 - 11
test/functional-tests/node-transformers/finalizing-transformers/ast-to-eval-call-expression-transformer/AstToEvalCallExpressionTransformer.spec.ts

@@ -12,7 +12,7 @@ import { getRegExpMatch } from '../../../../helpers/getRegExpMatch';
 describe('AstToEvalCallExpressionTransformer', () => {
     describe('variant #1: identifier reference', () => {
         const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
-        const variableReferenceIdentifierRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
+        const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
 
         let functionIdentifierName: string | null,
             obfuscatedCode: string,
@@ -30,11 +30,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
             obfuscatedCode = obfuscationResult.getObfuscatedCode();
 
             functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
-            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should obfuscate eval string', () => {
-            assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
+            assert.match(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should correctly transform function parameter inside eval expression', () => {
@@ -44,7 +44,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
 
     describe('variant #2: call expression with identifier reference', () => {
         const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
-        const variableReferenceIdentifierRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/;
+        const evalExpressionRegExp: RegExp = /eval *\('console\[\\'log\\']\((_0x(?:[a-f0-9]){4,6})\);'\);/;
 
         let functionIdentifierName: string | null,
             obfuscatedCode: string,
@@ -62,11 +62,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
             obfuscatedCode = obfuscationResult.getObfuscatedCode();
 
             functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
-            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should obfuscate eval string', () => {
-            assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
+            assert.match(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should correctly transform function parameter inside eval expression', () => {
@@ -75,7 +75,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
     });
 
     describe('variant #3: multiple statements in eval', () => {
-        const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *\\n_0x([a-f0-9]){4,6};'\);/;
+        const regExp: RegExp = /eval *\('_0x([a-f0-9]){4,6}; *_0x([a-f0-9]){4,6};'\);/;
 
         let obfuscatedCode: string;
 
@@ -127,7 +127,7 @@ describe('AstToEvalCallExpressionTransformer', () => {
 
     describe('variant #5: eval expression as argument', () => {
         const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
-        const variableReferenceIdentifierRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/;
+        const evalExpressionRegExp: RegExp = /console\['log']\(eval *\('(_0x(?:[a-f0-9]){4,6});'\)\);/;
 
         let functionIdentifierName: string | null,
             obfuscatedCode: string,
@@ -145,11 +145,11 @@ describe('AstToEvalCallExpressionTransformer', () => {
             obfuscatedCode = obfuscationResult.getObfuscatedCode();
 
             functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
-            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, variableReferenceIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should obfuscate eval string', () => {
-            assert.match(obfuscatedCode, variableReferenceIdentifierRegExp);
+            assert.match(obfuscatedCode, evalExpressionRegExp);
         });
 
         it('should correctly transform function parameter inside eval expression', () => {
@@ -157,7 +157,83 @@ describe('AstToEvalCallExpressionTransformer', () => {
         });
     });
 
-    describe('variant #6: integration with control flow flattening', () => {
+    describe('variant #6: nested eval expressions', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6}), *(_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionMatch: string = `` +
+            `eval *\\('` +
+                `var *(_0x(?:[a-f0-9]){4,6}) *= *(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
+                `eval\\(\\\\'` +
+                    `(_0x(?:[a-f0-9]){4,6}) *\\+ *(_0x(?:[a-f0-9]){4,6});` +
+                `\\\\'\\);` +
+            `'\\);` +
+        ``;
+        const evalExpressionRegExp: RegExp = new RegExp(evalExpressionMatch);
+        const expectedEvaluationResult: number = 4;
+
+        let evaluationResult: number,
+            functionIdentifierAName: string | null,
+            functionIdentifierBName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierAName1: string | null,
+            variableReferenceIdentifierAName2: string | null,
+            variableReferenceIdentifierBName: string | null,
+            variableReferenceIdentifierCName1: string | null,
+            variableReferenceIdentifierCName2: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/nested-eval-expressions.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierAName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 0);
+            functionIdentifierBName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp, 1);
+
+            // parameter `a` reference inside parent eval expression
+            variableReferenceIdentifierAName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 1);
+            // parameter `a` reference inside nested eval expression
+            variableReferenceIdentifierAName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 3);
+            // parameter `b` reference inside parent eval expression
+            variableReferenceIdentifierBName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 2);
+            // variable declaration `c` inside parent eval expression
+            variableReferenceIdentifierCName1 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 0);
+            // variable `c` reference inside nested eval expression
+            variableReferenceIdentifierCName2 = getRegExpMatch(obfuscatedCode, evalExpressionRegExp, 4);
+
+            evaluationResult = eval(obfuscatedCode);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should generate correct code', () => {
+            assert.equal(evaluationResult, expectedEvaluationResult);
+        });
+
+        it('match #1: should correctly transform function parameter `a` inside eval expression', () => {
+            assert.equal(functionIdentifierAName, variableReferenceIdentifierAName1);
+        });
+
+        it('match #2: should correctly transform function parameter `a` inside nested eval expression', () => {
+            assert.equal(functionIdentifierAName, variableReferenceIdentifierAName2);
+        });
+
+        it('match #3: should correctly transform function parameter `b` inside eval expression', () => {
+            assert.equal(functionIdentifierBName, variableReferenceIdentifierBName);
+        });
+
+        it('match #4: should correctly transform variable declaration and variable reference inside eval and nested eval expressions', () => {
+            assert.equal(variableReferenceIdentifierCName1, variableReferenceIdentifierCName2);
+        });
+    });
+
+    describe('variant #7: integration with control flow flattening', () => {
         const variableMatch: string = '_0x([a-f0-9]){4,6}';
         const controlFlowStorageNodeMatch: string = `` +
             `var *${variableMatch} *= *\\{` +

+ 7 - 0
test/functional-tests/node-transformers/finalizing-transformers/ast-to-eval-call-expression-transformer/fixtures/nested-eval-expressions.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (a, b) {
+        return eval('var c = a + b; eval(\'a + c\');');
+    }
+
+    return foo(1, 2);
+})();

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