Browse Source

Eval transformer template literal string processing

sanex3339 7 years ago
parent
commit
d3271ca6d9

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


+ 53 - 13
src/node-transformers/preparing-transformers/EvaCallExpressionToAstTransformer.ts

@@ -27,6 +27,47 @@ export class EvalCallExpressionToAstTransformer extends AbstractNodeTransformer
         super(randomGenerator, options);
     }
 
+    /**
+     * @param {Expression | SpreadElement} node
+     * @returns {string | null}
+     */
+    private static extractEvalStringFromCallExpressionArgument (node: ESTree.Expression | ESTree.SpreadElement): string | null {
+        if (NodeGuards.isLiteralNode(node)) {
+            return EvalCallExpressionToAstTransformer
+                .getEvalStringFromLiteralNode(node);
+        }
+
+        if (NodeGuards.isTemplateLiteralNode(node)) {
+            return EvalCallExpressionToAstTransformer
+                .getEvalStringFromTemplateLiteralNode(node);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param {Literal} node
+     * @returns {string | null}
+     */
+    private static getEvalStringFromLiteralNode (node: ESTree.Literal): string | null {
+        return typeof node.value === 'string' ? node.value : null;
+    }
+
+    /**
+     * @param {TemplateLiteral} node
+     * @returns {string | null}
+     */
+    private static getEvalStringFromTemplateLiteralNode (node: ESTree.TemplateLiteral): string | null {
+        const quasis: ESTree.TemplateElement[] = node.quasis;
+        const allowedQuasisLength: number = 1;
+
+        if (quasis.length !== allowedQuasisLength || node.expressions.length) {
+            return null;
+        }
+
+        return quasis[0].value.cooked;
+    }
+
     /**
      * @return {IVisitor}
      */
@@ -53,32 +94,31 @@ export class EvalCallExpressionToAstTransformer extends AbstractNodeTransformer
     public transformNode (callExpressionNode: ESTree.CallExpression, parentNode: ESTree.Node): ESTree.Node {
         const callExpressionFirstArgument: ESTree.Expression | ESTree.SpreadElement = callExpressionNode.arguments[0];
 
-        if (
-            !callExpressionFirstArgument
-            || !NodeGuards.isLiteralNode(callExpressionFirstArgument)
-            || typeof callExpressionFirstArgument.value !== 'string'
-        ) {
+        if (!callExpressionFirstArgument) {
             return callExpressionNode;
         }
 
-        const code: string = callExpressionFirstArgument.value;
+        const evalString: string | null = EvalCallExpressionToAstTransformer
+            .extractEvalStringFromCallExpressionArgument(callExpressionFirstArgument);
+
+        if (!evalString) {
+            return callExpressionNode;
+        }
 
         let ast: TStatement[];
 
         // wrapping into try-catch to prevent parsing of incorrect `eval` string
         try {
-            ast = NodeUtils.convertCodeToStructure(code);
+            ast = NodeUtils.convertCodeToStructure(evalString);
         } catch (e) {
             return callExpressionNode;
         }
 
-        const evalRootAstHost: ESTree.FunctionExpression = Nodes.getFunctionExpressionNode(
-            [],
-            Nodes.getBlockStatementNode(<any>ast)
-        );
+        const evalRootAstHostNode: ESTree.FunctionExpression = Nodes
+            .getFunctionExpressionNode([], Nodes.getBlockStatementNode(<any>ast));
 
-        evalRootAstHost.isEvalRoot = true;
+        evalRootAstHostNode.isEvalRoot = true;
 
-        return evalRootAstHost;
+        return evalRootAstHostNode;
     }
 }

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

@@ -255,7 +255,39 @@ describe('AstToEvalCallExpressionTransformer', () => {
         });
     });
 
-    describe('variant #8: integration with control flow flattening', () => {
+    describe('variant #8: template literal inside eval expression', () => {
+        const functionIdentifierRegExp: RegExp = /function *_0x(?:[a-f0-9]){4,6} *\((_0x(?:[a-f0-9]){4,6})\)/;
+        const evalExpressionRegExp: RegExp = /eval *\('(_0x(?:[a-f0-9]){4,6});'\);/;
+
+        let functionIdentifierName: string | null,
+            obfuscatedCode: string,
+            variableReferenceIdentifierName: string | null;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/eval-expression-template-literal.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+
+            functionIdentifierName = getRegExpMatch(obfuscatedCode, functionIdentifierRegExp);
+            variableReferenceIdentifierName = getRegExpMatch(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should obfuscate eval string', () => {
+            assert.match(obfuscatedCode, evalExpressionRegExp);
+        });
+
+        it('should correctly transform function parameter inside eval expression', () => {
+            assert.equal(functionIdentifierName, variableReferenceIdentifierName);
+        });
+    });
+
+    describe('variant #9: 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/eval-expression-template-literal.js

@@ -0,0 +1,7 @@
+(function(){
+    function foo (bar) {
+        eval(`bar`);
+    }
+
+    foo(1);
+})();

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