浏览代码

Dead code injection refactoring #1

sanex3339 7 年之前
父节点
当前提交
43f64f73e7

文件差异内容过多而无法显示
+ 0 - 0
dist/index.js


+ 94 - 54
src/JavaScriptObfuscator.ts

@@ -184,19 +184,11 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
             return astTree;
         }
 
-        // first pass: AST-tree preparation
-        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Preparing);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.transformersList,
-            TransformationStage.Preparing
-        );
+        astTree = this.runPreparingStage(astTree);
 
-        // second pass: AST-tree analyzing
         this.logger.info(LoggingMessage.AnalyzingASTTreeStage);
         const stackTraceData: IStackTraceData[] = this.stackTraceAnalyzer.analyze(astTree);
 
-        // initialize custom node groups and configure custom nodes
         this.customNodeGroupStorage
             .getStorage()
             .forEach((customNodeGroup: ICustomNodeGroup) => {
@@ -210,51 +202,11 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
 
         this.obfuscationEventEmitter.emit(ObfuscationEvent.BeforeObfuscation, astTree, stackTraceData);
 
-        // third pass: dead code injection transformer
-        if (this.options.deadCodeInjection) {
-            this.logger.info(LoggingMessage.TransformationStage, TransformationStage.DeadCodeInjection);
-
-            astTree = this.transformersRunner.transform(
-                astTree,
-                JavaScriptObfuscator.transformersList,
-                TransformationStage.DeadCodeInjection
-            );
-        }
-
-        // fourth pass: control flow flattening transformers
-        if (this.options.controlFlowFlattening) {
-            this.logger.info(LoggingMessage.TransformationStage, TransformationStage.ControlFlowFlattening);
-
-            astTree = this.transformersRunner.transform(
-                astTree,
-                JavaScriptObfuscator.transformersList,
-                TransformationStage.ControlFlowFlattening
-            );
-        }
-
-        // fifth pass: converting transformers
-        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Converting);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.transformersList,
-            TransformationStage.Converting
-        );
-
-        // sixth pass: obfuscating transformers
-        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Obfuscating);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.transformersList,
-            TransformationStage.Obfuscating
-        );
-
-        // seventh pass: finalizing transformers
-        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Finalizing);
-        astTree = this.transformersRunner.transform(
-            astTree,
-            JavaScriptObfuscator.transformersList,
-            TransformationStage.Finalizing
-        );
+        astTree = this.runDeadCodeInjectionStage(astTree);
+        astTree = this.runControlFlowFlatteningStage(astTree);
+        astTree = this.runConvertingStage(astTree);
+        astTree = this.runObfuscatingStage(astTree);
+        astTree = this.runFinalizingStage(astTree);
 
         this.obfuscationEventEmitter.emit(ObfuscationEvent.AfterObfuscation, astTree, stackTraceData);
 
@@ -298,4 +250,92 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
             generatorOutput.map
         );
     }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runPreparingStage (astTree: ESTree.Program): ESTree.Program {
+        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Preparing);
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.Preparing
+        );
+    }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runDeadCodeInjectionStage (astTree: ESTree.Program): ESTree.Program {
+        if (this.options.deadCodeInjection) {
+            this.logger.info(LoggingMessage.TransformationStage, TransformationStage.DeadCodeInjection);
+        }
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.DeadCodeInjection
+        );
+    }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runControlFlowFlatteningStage (astTree: ESTree.Program): ESTree.Program {
+        if (this.options.controlFlowFlattening) {
+            this.logger.info(LoggingMessage.TransformationStage, TransformationStage.ControlFlowFlattening);
+        }
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.ControlFlowFlattening
+        );
+    }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runConvertingStage (astTree: ESTree.Program): ESTree.Program {
+        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Converting);
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.Converting
+        );
+    }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runObfuscatingStage (astTree: ESTree.Program): ESTree.Program {
+        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Obfuscating);
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.Obfuscating
+        );
+    }
+
+    /**
+     * @param {Program} astTree
+     * @returns {Program}
+     */
+    private runFinalizingStage (astTree: ESTree.Program): ESTree.Program {
+        this.logger.info(LoggingMessage.TransformationStage, TransformationStage.Finalizing);
+
+        return this.transformersRunner.transform(
+            astTree,
+            JavaScriptObfuscator.transformersList,
+            TransformationStage.Finalizing
+        );
+    }
 }

+ 6 - 6
src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts

@@ -27,7 +27,7 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
      * @type {BlockStatement}
      */
     @initializable()
-    private randomBlockStatementNode: BlockStatement;
+    private deadCodeInjectionRootAstHostNode: BlockStatement;
 
     /**
      * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
@@ -45,14 +45,14 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
 
     /**
      * @param {BlockStatement} blockStatementNode
-     * @param {BlockStatement} randomBlockStatementNode
+     * @param {BlockStatement} deadCodeInjectionRootAstHostNode
      */
     public initialize (
         blockStatementNode: BlockStatement,
-        randomBlockStatementNode: BlockStatement
+        deadCodeInjectionRootAstHostNode: BlockStatement
     ): void {
         this.blockStatementNode = blockStatementNode;
-        this.randomBlockStatementNode = randomBlockStatementNode;
+        this.deadCodeInjectionRootAstHostNode = deadCodeInjectionRootAstHostNode;
     }
 
     /**
@@ -67,8 +67,8 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
         const rightString: string = random2 ? leftString : this.randomGenerator.getRandomString(5);
 
         const [consequent, alternate]: [BlockStatement, BlockStatement] = random1 === random2
-            ? [this.blockStatementNode, this.randomBlockStatementNode]
-            : [this.randomBlockStatementNode, this.blockStatementNode];
+            ? [this.blockStatementNode, this.deadCodeInjectionRootAstHostNode]
+            : [this.deadCodeInjectionRootAstHostNode, this.blockStatementNode];
 
         const structure: BlockStatement = Nodes.getBlockStatementNode([
             Nodes.getIfStatementNode(

+ 6 - 0
src/interfaces/node-transformers/INodeTransformer.d.ts

@@ -18,6 +18,12 @@ export interface INodeTransformer {
      */
     analyzeNode ? (node: ESTree.Node, parentNode: ESTree.Node | null): void;
 
+    /**
+     * @param {Node} node
+     * @param {Node | null} parentNode
+     */
+    restoreNode ? (node: ESTree.Node, parentNode: ESTree.Node | null): void;
+
     /**
      * @param {Node} node
      * @param {Node | null} parentNode

+ 4 - 0
src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts

@@ -102,6 +102,10 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
      * @returns {IVisitor | null}
      */
     public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        if (!this.options.controlFlowFlattening) {
+            return null;
+        }
+
         switch (transformationStage) {
             case TransformationStage.ControlFlowFlattening:
                 return {

+ 4 - 0
src/node-transformers/control-flow-transformers/FunctionControlFlowTransformer.ts

@@ -106,6 +106,10 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
      * @returns {IVisitor | null}
      */
     public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        if (!this.options.controlFlowFlattening) {
+            return null;
+        }
+
         switch (transformationStage) {
             case TransformationStage.ControlFlowFlattening:
                 return {

+ 61 - 35
src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

@@ -10,16 +10,15 @@ import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
 import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
-import { ITransformersRunner } from '../../interfaces/node-transformers/ITransformersRunner';
 import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
 
 import { DeadCodeInjectionCustomNode } from '../../enums/custom-nodes/DeadCodeInjectionCustomNode';
-import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
 import { NodeType } from '../../enums/node/NodeType';
 import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
 
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
+import { Nodes } from '../../node/Nodes';
 import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
@@ -35,16 +34,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
     private static readonly minCollectedBlockStatementsCount: number = 5;
 
     /**
-     * @type {NodeTransformer[]}
+     * @type {WeakSet <BlockStatement>}
      */
-    private static readonly transformersToRenameBlockScopeIdentifiers: NodeTransformer[] = [
-        NodeTransformer.CatchClauseTransformer,
-        NodeTransformer.ClassDeclarationTransformer,
-        NodeTransformer.FunctionDeclarationTransformer,
-        NodeTransformer.FunctionTransformer,
-        NodeTransformer.LabeledStatementTransformer,
-        NodeTransformer.VariableDeclarationTransformer
-    ];
+    private readonly deadCodeInjectionRootAstHostNodeSet: WeakSet <ESTree.BlockStatement> = new WeakSet();
 
     /**
      * @type {ESTree.BlockStatement[]}
@@ -61,28 +53,20 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      */
     private readonly deadCodeInjectionCustomNodeFactory: TDeadNodeInjectionCustomNodeFactory;
 
-    /**
-     * @type {ITransformersRunner}
-     */
-    private readonly transformersRunner: ITransformersRunner;
-
     /**
      * @param {TControlFlowCustomNodeFactory} deadCodeInjectionCustomNodeFactory
-     * @param {ITransformersRunner} transformersRunner
      * @param {IRandomGenerator} randomGenerator
      * @param {IOptions} options
      */
     constructor (
         @inject(ServiceIdentifiers.Factory__IDeadCodeInjectionCustomNode)
             deadCodeInjectionCustomNodeFactory: TDeadNodeInjectionCustomNodeFactory,
-        @inject(ServiceIdentifiers.ITransformersRunner) transformersRunner: ITransformersRunner,
         @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
         @inject(ServiceIdentifiers.IOptions) options: IOptions
     ) {
         super(randomGenerator, options);
 
         this.deadCodeInjectionCustomNodeFactory = deadCodeInjectionCustomNodeFactory;
-        this.transformersRunner = transformersRunner;
     }
 
     /**
@@ -101,6 +85,10 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
      * @returns {IVisitor | null}
      */
     public getVisitor (transformationStage: TransformationStage): IVisitor | null {
+        if (!this.options.deadCodeInjection) {
+            return null;
+        }
+
         switch (transformationStage) {
             case TransformationStage.DeadCodeInjection:
                 return {
@@ -118,6 +106,15 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
                     }
                 };
 
+            case TransformationStage.Finalizing:
+                return {
+                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                        if (parentNode && this.isDeadCodeInjectionRootAstHostNode(node)) {
+                            return this.restoreNode(node, parentNode);
+                        }
+                    }
+                };
+
             default:
                 return null;
         }
@@ -176,6 +173,21 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         return this.replaceBlockStatementNode(blockStatementNode, randomBlockStatementNode, parentNode);
     }
 
+    /**
+     * @param {FunctionExpression} deadCodeInjectionRootAstHostNode
+     * @param {Node} parentNode
+     * @returns {Node}
+     */
+    public restoreNode (deadCodeInjectionRootAstHostNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node {
+        const hostNodeFirstStatement: ESTree.Statement = deadCodeInjectionRootAstHostNode.body[0];
+
+        if (!NodeGuards.isFunctionDeclarationNode(hostNodeFirstStatement)) {
+            throw new Error('Wrong dead code injection root AST host node. Host node should contain `FunctionDeclaration` node');
+        }
+
+        return hostNodeFirstStatement.body;
+    }
+
     /**
      * @param {BlockStatement} blockStatementNode
      * @param {BlockStatement[]} collectedBlockStatements
@@ -186,8 +198,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         collectedBlockStatements: ESTree.BlockStatement[],
         parentNode: ESTree.Node
     ): void {
-        let clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode),
-            nestedBlockStatementsCount: number = 0,
+        const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode);
+
+        let nestedBlockStatementsCount: number = 0,
             isValidBlockStatementNode: boolean = true;
 
         estraverse.replace(clonedBlockStatementNode, {
@@ -220,19 +233,17 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
             return;
         }
 
-        /**
-         * We should transform identifiers in the dead code block statement to avoid conflicts with original code
-         */
-        NodeUtils.parentizeNode(clonedBlockStatementNode, parentNode);
-        clonedBlockStatementNode = this.transformersRunner.transform(
-            clonedBlockStatementNode,
-            DeadCodeInjectionTransformer.transformersToRenameBlockScopeIdentifiers,
-            TransformationStage.Obfuscating
-        );
-
         collectedBlockStatements.push(clonedBlockStatementNode);
     }
 
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    private isDeadCodeInjectionRootAstHostNode (node: ESTree.Node): node is ESTree.BlockStatement {
+        return NodeGuards.isBlockStatementNode(node) && this.deadCodeInjectionRootAstHostNodeSet.has(node);
+    }
+
     /**
      * @param {BlockStatement} blockStatementNode
      * @param {BlockStatement} randomBlockStatementNode
@@ -244,14 +255,29 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
         randomBlockStatementNode: ESTree.BlockStatement,
         parentNode: ESTree.Node
     ): ESTree.BlockStatement {
+        /**
+         * we should wrap original random block statement node into the parent block statement node (ast root host node)
+         * with function declaration node. This function declaration node will create block scope for all identifiers
+         * inside random block statement node and this identifiers won't affect identifiers of the rest AST tree.
+         */
+        const deadCodeInjectionRootAstHostNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
+            Nodes.getFunctionDeclarationNode(
+                'foobar',
+                [],
+                randomBlockStatementNode
+            )
+        ]);
+
+        /**
+         * we should store that host node and then extract random block statement node on the `finalizing` stage
+         */
+        this.deadCodeInjectionRootAstHostNodeSet.add(deadCodeInjectionRootAstHostNode);
+
         const blockStatementDeadCodeInjectionCustomNode: ICustomNode = this.deadCodeInjectionCustomNodeFactory(
             DeadCodeInjectionCustomNode.BlockStatementDeadCodeInjectionNode
         );
 
-        blockStatementDeadCodeInjectionCustomNode.initialize(
-            blockStatementNode,
-            randomBlockStatementNode
-        );
+        blockStatementDeadCodeInjectionCustomNode.initialize(blockStatementNode, deadCodeInjectionRootAstHostNode);
 
         const newBlockStatementNode: ESTree.BlockStatement = <ESTree.BlockStatement>blockStatementDeadCodeInjectionCustomNode.getNode()[0];
 

+ 19 - 12
src/node-transformers/preparing-transformers/EvaCallExpressionTransformer.ts

@@ -22,7 +22,7 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
     /**
      * @type {WeakSet <FunctionExpression>}
      */
-    private readonly evalRootAstHostNodeStorage: WeakSet <ESTree.FunctionExpression> = new WeakSet();
+    private readonly evalRootAstHostNodeSet: WeakSet <ESTree.FunctionExpression> = new WeakSet();
 
     /**
      * @param {IRandomGenerator} randomGenerator
@@ -100,7 +100,7 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
                 return {
                     leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
                         if (parentNode && this.isEvalRootAstHostNode(node)) {
-                            return this.restoreEvalExpressionNode(node, parentNode);
+                            return this.restoreNode(node, parentNode);
                         }
                     }
                 };
@@ -138,28 +138,27 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
             return callExpressionNode;
         }
 
+        /**
+         * we should wrap AST-tree into the parent function expression node (ast root host node).
+         * This function expression node will help to correctly transform AST-tree.
+         */
         const evalRootAstHostNode: ESTree.FunctionExpression = Nodes
             .getFunctionExpressionNode([], Nodes.getBlockStatementNode(<any>ast));
 
-        this.evalRootAstHostNodeStorage.add(evalRootAstHostNode);
+        /**
+         * we should store that host node and then extract AST-tree on the `finalizing` stage
+         */
+        this.evalRootAstHostNodeSet.add(evalRootAstHostNode);
 
         return evalRootAstHostNode;
     }
 
-    /**
-     * @param {Node} node
-     * @returns {boolean}
-     */
-    private isEvalRootAstHostNode (node: ESTree.Node): node is ESTree.FunctionExpression {
-        return NodeGuards.isFunctionExpressionNode(node) && this.evalRootAstHostNodeStorage.has(node);
-    }
-
     /**
      * @param {FunctionExpression} evalRootAstHostNode
      * @param {Node} parentNode
      * @returns {Node}
      */
-    private restoreEvalExpressionNode (evalRootAstHostNode: ESTree.FunctionExpression, parentNode: ESTree.Node): ESTree.Node {
+    public restoreNode (evalRootAstHostNode: ESTree.FunctionExpression, parentNode: ESTree.Node): ESTree.Node {
         const targetAst: ESTree.Statement[] = evalRootAstHostNode.body.body;
         const obfuscatedCode: string = NodeUtils.convertStructureToCode(targetAst);
 
@@ -170,4 +169,12 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
             ]
         );
     }
+
+    /**
+     * @param {Node} node
+     * @returns {boolean}
+     */
+    private isEvalRootAstHostNode (node: ESTree.Node): node is ESTree.FunctionExpression {
+        return NodeGuards.isFunctionExpressionNode(node) && this.evalRootAstHostNodeSet.has(node);
+    }
 }

部分文件因为文件数量过多而无法显示