Beispiel #1
0
        List <ILNode> ConvertToAst(List <ByteCode> body)
        {
            List <ILNode> ast = new List <ILNode>();

            // Convert stack-based IL code to ILAst tree
            foreach (ByteCode byteCode in body)
            {
                ILRange ilRange = new ILRange()
                {
                    From = byteCode.Offset, To = byteCode.EndOffset
                };

                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);
                if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0)
                {
                    ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length];
                    for (int i = 0; i < prefixes.Length; i++)
                    {
                        prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand);
                    }
                    expr.Prefixes = prefixes;
                }

                // Label for this instruction
                if (byteCode.Label != null)
                {
                    ast.Add(byteCode.Label);
                }

                // Reference arguments using temporary variables
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    StackSlot slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true
                    };
                    ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
                    foreach (ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
                    }
                }
            }

            return(ast);
        }
Beispiel #2
0
        void HandleAwait(List <ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID)
        {
            // Handle the instructions prior to the exit out of the method to detect what is being awaited.
            // (analyses the last instructions in newBody and removes the analyzed instructions from newBody)

            if (doFinallyBodies != null)
            {
                // stloc(<>t__doFinallyBodies, ldc.i4(0))
                ILExpression dfbInitExpr;
                if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                int val;
                if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                newBody.RemoveAt(newBody.Count - 1);                 // remove doFinallyBodies assignment
            }

            // call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this))
            ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression;

            newBody.RemoveAt(newBody.Count - 1);             // remove AwaitUnsafeOnCompleted call
            if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call)
            {
                throw new SymbolicAnalysisFailedException();
            }
            string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name;

            if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted")
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (callAwaitUnsafeOnCompleted.Arguments.Count != 3)
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001))
            FieldReference awaiterFieldRef;
            ILExpression   loadThis, loadAwaiterVar;

            if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }
            newBody.RemoveAt(newBody.Count - 1);             // remove awaiter field assignment
            awaiterField = awaiterFieldRef.ResolveWithinSameModule();
            if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar)))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
            if (!MatchStateAssignment(newBody.LastOrDefault(), out targetStateID))
            {
                throw new SymbolicAnalysisFailedException();
            }
            newBody.RemoveAt(newBody.Count - 1);             // remove awaiter field assignment
        }
Beispiel #3
0
        List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            List <ILNode> ast = new List <ILNode>();

            while (ehs.Any())
            {
                ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();

                // Find the first and widest scope
                int tryStart = ehs.Min(eh => eh.TryStart.Offset);
                int tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    int tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart)
                    {
                        tryStartIdx++;
                    }
                    ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
                }

                // Cut the try block
                {
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    int tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd)
                    {
                        tryEndIdx++;
                    }
                    tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>();
                foreach (ExceptionHandler eh in handlers)
                {
                    int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
                    int startIdx         = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset)
                    {
                        startIdx++;
                    }
                    int endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset)
                    {
                        endIdx++;
                    }
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock()
                        {
                            ExceptionType = eh.CatchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                        {
                            // Exception is not used
                            catchBlock.ExceptionVariable = null;
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            ILExpression first = catchBlock.Body[0] as ILExpression;
                            if (first != null &&
                                first.Code == ILCode.Pop &&
                                first.Arguments[0].Code == ILCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
                                {
                                    catchBlock.ExceptionVariable = new ILVariable()
                                    {
                                        Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true
                                    }
                                }
                                ;
                                else
                                {
                                    catchBlock.ExceptionVariable = null;
                                }
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            ILVariable exTemp = new ILVariable()
                            {
                                Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true
                            };
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (ILVariable storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
Beispiel #4
0
            public readonly ILVariable LoadFrom;                 // Variable used for storage of the value

            public StackSlot(ByteCode[] definitions, ILVariable loadFrom)
            {
                this.Definitions = definitions;
                this.LoadFrom    = loadFrom;
            }
Beispiel #5
0
        List <ByteCode> StackAnalysis(MethodDefinition methodDef)
        {
            Dictionary <Instruction, ByteCode> instrToByteCode = new Dictionary <Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            List <ByteCode>    body     = new List <ByteCode>(methodDef.Body.Instructions.Count);
            List <Instruction> prefixes = null;

            foreach (Instruction inst in methodDef.Body.Instructions)
            {
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                    {
                        prefixes = new List <Instruction>(1);
                    }
                    prefixes.Add(inst);
                    continue;
                }
                ILCode code    = (ILCode)inst.OpCode.Code;
                object operand = inst.Operand;
                ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
                ByteCode byteCode = new ByteCode()
                {
                    Offset    = inst.Offset,
                    EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    Code      = code,
                    Operand   = operand,
                    PopCount  = inst.GetPopDelta(methodDef),
                    PushCount = inst.GetPushDelta()
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset   = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes          = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            Stack <ByteCode> agenda = new Stack <ByteCode>();

            int varCount = methodDef.Body.Variables.Count;

            var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (ExceptionHandler ex in methodDef.Body.ExceptionHandlers)
                {
                    ByteCode handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore     = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex]         = ldexception;
                        handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        ByteCode filterStart = instrToByteCode[ex.FilterStart];
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        // TODO: ldexceptions[ex] = ldexception;
                        filterStart.StackBefore     = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
                        filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore     = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                ByteCode byteCode = agenda.Pop();

                // Calculate new stack
                StackSlot[] newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);

                // Calculate new variable state
                VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new [] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == ILCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUknownState(varCount);
                }

                // Find all successors
                List <ByteCode> branchTargets = new List <ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    if (exceptionHandlerStarts.Contains(byteCode.Next))
                    {
                        // Do not fall though down to exception handler
                        // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
                    }
                    else
                    {
                        branchTargets.Add(byteCode.Next);
                    }
                }
                if (byteCode.Operand is Instruction[])
                {
                    foreach (Instruction inst in (Instruction[])byteCode.Operand)
                    {
                        ByteCode target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new ILLabel()
                            {
                                Name = target.Name
                            };
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new ILLabel()
                        {
                            Name = target.Name
                        };
                    }
                }

                // Apply the state to successors
                foreach (ByteCode branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore     = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore     = StackSlot.ModifyStack(newStack, 0, 0, null);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Length != newStack.Length)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Length; i++)
                        {
                            ByteCode[] oldDefs = branchTarget.StackBefore[i].Definitions;
                            ByteCode[] newDefs = oldDefs.Union(newStack[i].Definitions);
                            if (newDefs.Length > oldDefs.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            VariableSlot oldSlot = branchTarget.VariablesBefore[i];
                            VariableSlot newSlot = newVariableState[i];
                            if (!oldSlot.UnknownDefinition)
                            {
                                if (newSlot.UnknownDefinition)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldDefs = oldSlot.Definitions;
                                    ByteCode[] newDefs = oldDefs.Union(newSlot.Definitions);
                                    if (newDefs.Length > oldDefs.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I belive it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Genertate temporary variables to replace stack
            foreach (ByteCode byteCode in body)
            {
                int argIdx   = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true
                    };
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List <ILVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (ByteCode byteCode in body)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        ILVariable tmpVar = new ILVariable()
                        {
                            Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true
                        };
                        byteCode.StoreTo = new List <ILVariable>()
                        {
                            tmpVar
                        };
                        foreach (ByteCode bc in body)
                        {
                            for (int i = 0; i < bc.StackBefore.Length; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (ByteCode byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    List <ILLabel> newOperand = new List <ILLabel>();
                    foreach (Instruction target in (Instruction[])byteCode.Operand)
                    {
                        newOperand.Add(instrToByteCode[target].Label);
                    }
                    byteCode.Operand = newOperand.ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return(body);
        }
Beispiel #6
0
        public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr)
        {
            ILVariable v;

            return(node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar);
        }
Beispiel #7
0
        public static bool MatchLdloca(this ILNode node, ILVariable expectedVar)
        {
            ILVariable v;

            return(node.Match(ILCode.Ldloca, out v) && v == expectedVar);
        }