Exemple #1
0
    static ControlFlowGraph ReduceBreak(ControlFlowGraph graph, CfgLoop loop, LoopPart part)
    {
        // Func<string> vis = () => graph.ToVis().AddPointer("part", part.Index).ToString(); // For VS Code debug visualisation

        // Break = needs to exit the loop to be a break
        if (!part.Break)
        {
            return(graph);
        }

        // Outside entry = non-structured code, so give up
        if (part.OutsideEntry)
        {
            return(graph);
        }

        var children = graph.Children(part.Index);

        if (children.Length != 2)
        {
            throw new ControlFlowGraphException($"Break at {part.Index} has unexpected child count ({children.Length})", graph);
        }

        bool isfirstChildInLoop = loop.Body.Any(x => x.Index == children[0]) || children[0] == loop.Header.Index;
        var  exitTarget         = isfirstChildInLoop ? children[1] : children[0];

        // Add LoopSuccessor edge if this is the last link to the MainExit.
        var remainingLoopChildren = loop.Body
                                    .Where(x => x.Index != part.Index)
                                    .Aggregate(
            (IEnumerable <int>)graph.Children(loop.Header.Index),
            (current, x) => current.Union(graph.Children(x.Index)));

        if (loop.MainExit.HasValue && !remainingLoopChildren.Contains(loop.MainExit.Value))
        {
            graph = graph.AddEdge(loop.Header.Index, loop.MainExit.Value, CfgEdge.LoopSuccessor);
        }

        if (exitTarget != loop.MainExit)
        {
            var targetChildren = graph.Children(exitTarget);
            if (targetChildren.Length != 1 || targetChildren[0] != loop.MainExit)
            {
                return(graph);
            }

            var condition = graph.Nodes[part.Index];
            if (graph.GetEdgeLabel(part.Index, exitTarget) == CfgEdge.False)
            {
                condition = Emit.Negation(condition);
            }

            var ifNode = Emit.If(condition, Emit.Seq(graph.Nodes[exitTarget], Emit.Break()));
            return(graph
                   .RemoveNode(exitTarget)
                   .ReplaceNode(part.Index, ifNode));
        }

        return(ReplaceLoopBranch(graph, part.Index, exitTarget, Emit.Break()));
    }
    public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph)
    {
        var(reachability, reachableCount) = graph.Reverse().GetReachability(graph.ExitIndex);
        if (reachableCount == graph.ActiveNodeCount)
        {
            return(graph, null);
        }

        var acyclic         = graph.RemoveBackEdges();
        var distances       = acyclic.GetLongestPaths(acyclic.EntryIndex);
        int longestDistance = 0;
        int winner          = -1;

        for (int i = 0; i < graph.NodeCount; i++)
        {
            if (graph.Nodes[i] == null || reachability[i]) // Only consider nodes that can't reach the exit
            {
                continue;
            }

            if (distances[i] <= longestDistance)
            {
                continue;
            }
            longestDistance = distances[i];
            winner          = i;
        }

        if (winner == -1)
        {
            return(graph, null);
        }

        foreach (var backEdge in graph.GetBackEdges().Where(x => x.start == winner))
        {
            return(graph.AddEdge(backEdge.end, graph.ExitIndex, CfgEdge.LoopSuccessor), Description);
        }

        return(graph.AddEdge(winner, graph.ExitIndex, CfgEdge.DisjointGraphFixup), Description);
    }
        public static void DoSplitBlocksIntoAssignmentsAndPredicates(ControlFlowGraph cfg)
        {
            cfg.Vertices.ForEach(v =>
            {
                if (v.BalancedCode.IsEmpty() || v.Residue.IsEmpty())
                {
                    return;
                }

                var ass  = (v.BalancedCode.Last() is Assign) ? v.BalancedCode.Last().AssertCast <Assign>() : null;
                var lhs  = ass == null ? null : ass.Lhs.AssertCast <Ref>().Sym;
                var @ref = v.Residue.AssertSingle() is Ref ? v.Residue.AssertSingle().AssertCast <Ref>().Sym : null;
                if (lhs != null && @ref != null && lhs.ProtoId == @ref.ProtoId)
                {
                    // todo. this introduces a nasty bug if assigned variable is reused later
                    v.BalancedCode.RemoveLast();
                    v.Residue.SetElements(ass.Rhs);
                }

                if (v.BalancedCode.IsEmpty() || v.Residue.IsEmpty())
                {
                    return;
                }

                var v_test = new ControlFlowBlock();
                v_test.Residue.Add(v.Residue.AssertSingle());
                v.Residue.RemoveElements();

                var outEdges = cfg.Vedges(v, null);
                cfg.RemoveEdges(outEdges);

                cfg.AddVertex(v_test);
                cfg.AddEdge(new ControlFlowEdge(v, v_test));
                outEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(v_test, e.Target, e.Tag)));
            });
        }
Exemple #4
0
        public static void DoRemoveReturnThunk(ControlFlowGraph cfg)
        {
            var preRets = cfg.Vedges(null, cfg.Finish);

            if (preRets.Count() > 1)
            {
                return;
            }

            var wannabe = preRets.AssertSingle().Source;

            if (wannabe.BalancedCode.IsEmpty() &&
                wannabe.Residue.SingleOrDefault() is Ref)
            {
                var retThunk = wannabe;
                retThunk.BalancedCode.AssertEmpty();
                var auxLocal = retThunk.Residue.AssertSingle().AssertCast <Ref>().Sym;

                var rets = cfg.Vedges(null, retThunk);
                rets.AssertEach(ret => ret.Tag == null);

                cfg.RemoveVertex(retThunk);
                rets.ForEach(ret =>
                {
                    var src = ret.Source;
                    src.Residue.AssertEmpty();

                    var ass = src.BalancedCode.Last().AssertCast <Assign>();
                    var lhs = ass.Lhs.AssertCast <Ref>();
                    var rhs = ass.Rhs.AssertCast <Expression>();
                    (lhs.Sym.ProtoId == auxLocal.ProtoId).AssertTrue();

                    cfg.AddEdge(new ControlFlowEdge(src, cfg.Finish));
                    src.BalancedCode.RemoveLast();
                    src.Residue.Add(rhs);
                });
            }
        }
        public static ControlFlowGraph DoCreateCarcass(IMethodBody cil, out ReadOnlyDictionary <ControlFlowBlock, ReadOnlyCollection <IILOp> > blocks2parts)
        {
            // create the control flow graph
            var cfg = new ControlFlowGraph();

            // partition the code into blocks with continuous control flow
            // todo. support switches and protected regions
            var    targets          = new HashSet <IILOp>(cil.OfType <Branch>().Select(br => br.Target));
            var    l_partitions     = new List <ReadOnlyCollection <IILOp> >();
            var    l_partition      = new List <IILOp>();
            Action qualifyPartition = () => { if (l_partition.IsNotEmpty())
                                              {
                                                  l_partitions.Add(l_partition.ToReadOnly()); l_partition = new List <IILOp>();
                                              }
            };

            foreach (var op in cil)
            {
                if (op is Branch || op is Ret)
                {
                    qualifyPartition();
                }
                else
                {
                    if (targets.Contains(op))
                    {
                        qualifyPartition();
                    }
                    l_partition.Add(op);
                    if (op is Throw)
                    {
                        qualifyPartition();
                    }
                }
            }
            qualifyPartition();
            var partitions = l_partitions.ToReadOnly();

            // create blocks and map those to ops and partitions
            blocks2parts = partitions.ToDictionary(p => new ControlFlowBlock(), p => p).ToReadOnly();
            blocks2parts.ForEach(kvp => cfg.AddVertex(kvp.Key));
            var op2blocks = new Dictionary <IILOp, ControlFlowBlock>();

            blocks2parts.ForEach(kvp => kvp.Value.ForEach(op => op2blocks.Add(op, kvp.Key)));
            cil.ForEach(op => { if (!op2blocks.ContainsKey(op))
                                {
                                    op2blocks.Add(op, null);
                                }
                        });

            // prepare to link the blocks
            Action <IILOp, IILOp, CilPredicateType?> link = (op1, op2, cil_pred) =>
            {
                var source   = op1 == null ? cfg.Start : op2blocks[op1];
                var target   = op2 == null ? cfg.Finish : op2blocks[op2];
                var hir_pred = cil_pred == null ? (HirPredicateType?)null :
                               (HirPredicateType)Enum.Parse(typeof(HirPredicateType), cil_pred.Value.ToString());
                cfg.AddEdge(new ControlFlowEdge(source, target, hir_pred));
            };

            // link the blocks (down from 300+ LOC to this simple loop =))
            if (cil.IsEmpty())
            {
                link(null, null, null);
            }
            foreach (var op in cil)
            {
                // todo. support switches here
                if (op is Switch)
                {
                    throw AssertionHelper.Fail();
                }

                // todo. support general case of control flow
                // n0te. throw needs something on stack, so br > throw is impossible
                Func <IILOp, bool> isJmp = op1 => op1 is Ret || op1 is Branch;
                if (isJmp(op) && isJmp(op.Prev))
                {
                    continue;
                }

                if (isJmp(op))
                {
                    Func <IILOp, CilPredicateType?> pred = op1 => op1 is Ret ? null : op1 is Branch ? ((Branch)op1).PredicateType : ((Func <CilPredicateType?>)(() => { throw AssertionHelper.Fail(); }))();
                    Func <IILOp, bool>  uncond           = op1 => isJmp(op1) && pred(op1) == null;
                    Func <IILOp, bool>  cond             = op1 => isJmp(op1) && pred(op1) != null;
                    Func <IILOp, IILOp> target           = null; target = op1 =>
                                                                          op1 is Ret ? null : op1 is Branch?target(((Branch)op1).Target) : op1;

                    (target(op) is Branch).AssertFalse();
                    if (target(op) is Ret)
                    {
                        link(op.Prev, null, pred(op));
                    }
                    else
                    {
                        link(op.Prev, target(op), pred(op));
                    }

                    isJmp(op.Next).AssertImplies(uncond(op.Next));
                    if (cond(op))
                    {
                        link(op.Prev, target(op.Next), pred(op).Negate());
                    }
                }
                else if (op is Throw)
                {
                    // do nothing - throw doesn't create links
                }
                else
                {
                    if (op.Prev == null)
                    {
                        link(null, op, null);
                    }
                    if (isJmp(op.Next))
                    {
                        continue;
                    }

                    var blk      = op2blocks.GetOrDefault(op);
                    var blk_next = op2blocks.GetOrDefault(op.Next);
                    if (blk != blk_next)
                    {
                        link(op, op.Next, null);
                    }
                }
            }

            // yield control to the next step of the pipeline
            return(cfg);
        }
        public static void DoDecompileComplexConditions(ControlFlowGraph cfg)
        {
            var flow = cfg.Cflow(cfg.Start);

            while (true)
            {
                var vs = flow.FirstOrDefault(v =>
                                             v.Residue.Count() == 1 &&
                                             cfg.Vedges(v, null).Count() == 2 &&
                                             cfg.Vedges(v, null).All(e => e.Target.BalancedCode.IsEmpty() && e.Target.Residue.Count() == 1));
                if (vs == null)
                {
                    break;
                }

                var conv = cfg.ConvStrict(vs);
                var ass  = conv.BalancedCode.AssertFirst().AssertCast <Assign>();
                (ass.Lhs is Ref && ass.Rhs is Loophole).AssertTrue();

                var parts      = cfg.Cflow(vs, conv);
                var innerEdges = cfg.Edges(parts, parts).ToList();
                cfg.Edges(null, parts).Except(innerEdges).AssertEach(e => e.Target == vs);
                cfg.Edges(parts, null).Except(innerEdges).AssertEach(e => e.Source == vs || e.Source == conv);

                while (true)
                {
                    var somethingWasChanged = false;
                    foreach (var pivot in parts)
                    {
                        var pivot_inEdges = cfg.Vedges(null, pivot);
                        if (pivot_inEdges.Count() != 1)
                        {
                            continue;
                        }

                        var e_pred2pivot  = pivot_inEdges.AssertSingle();
                        var pred          = e_pred2pivot.Source;
                        var pred_outEdges = cfg.Vedges(pred, null);
                        if (pred_outEdges.Count() != 2)
                        {
                            continue;
                        }

                        var e_pred2target1  = pred_outEdges.AssertSingle(e => e.Target != pivot);
                        var target1         = e_pred2target1.Target;
                        var e_pivot2target1 = cfg.Vedge(pivot, target1);
                        if (e_pivot2target1 == null)
                        {
                            continue;
                        }

                        var pivot_outEdges = cfg.Vedges(pivot, null);
                        if (pivot_outEdges.Count() != 2)
                        {
                            continue;
                        }
                        var e_pivot2target2 = pivot_outEdges.AssertSingle(e => e.Target != target1);
                        var target2         = e_pivot2target2.Target;

                        var @operator = e_pred2target1.Condition == PredicateType.IsTrue ? OperatorType.OrElse :
                                        e_pred2target1.Condition == PredicateType.IsFalse ? OperatorType.AndAlso :
                                        ((Func <OperatorType>)(() => { throw AssertionHelper.Fail(); }))();
                        var clause_left  = pred.Residue.AssertSingle();
                        var clause_right = pivot.Residue.AssertSingle();
                        var negate_rhs   = e_pred2target1.Condition != e_pivot2target1.Condition;
                        if (negate_rhs)
                        {
                            clause_right = Operator.Not(clause_right);
                        }
                        var junction = Operator.Create(@operator, clause_left, clause_right);

                        cfg.RemoveVertex(pivot);
                        cfg.AddEdge(new ControlFlowEdge(pred, target2, e_pred2target1.Condition.Negate()));
                        pred.Residue.SetElements(junction);
                        somethingWasChanged |= true;
                    }

                    if (!somethingWasChanged)
                    {
                        break;
                    }
                }

                parts = cfg.Cflow(vs, conv);
                (parts.Count() == 4).AssertTrue();
                var @const = parts.Except(vs, conv).AssertSingle(v => v.Residue.AssertSingle() is Const);
                var vnext  = parts.Except(vs, conv, @const).AssertSingle();
                (cfg.Vedge(@const, vnext) == null && cfg.Vedge(vnext, @const) == null).AssertTrue();
                cfg.Vedge(vs, vnext).IsConditional.AssertTrue();
                cfg.Vedge(vs, @const).IsConditional.AssertTrue();
                cfg.Vedge(vnext, conv).IsUnconditional.AssertTrue();
                cfg.Vedge(@const, conv).IsUnconditional.AssertTrue();

                var estart     = vs.Residue.AssertSingle();
                var enext      = vnext.Residue.AssertSingle();
                var cond_const = @const.Residue.AssertSingle().AssertCast <Const>().Value.AssertCast <int>();
                var cond_edge  = cfg.Vedge(vs, @const).Condition;
                var val_const  = cond_const == 1 ? true :
                                 cond_const == 0 ? false :
                                 ((Func <bool>)(() => { throw AssertionHelper.Fail(); }))();
                var val_edge = cond_edge == PredicateType.IsTrue ? true :
                               cond_edge == PredicateType.IsFalse ? false :
                               ((Func <bool>)(() => { throw AssertionHelper.Fail(); }))();

                var operator1     = val_const ? OperatorType.OrElse : OperatorType.AndAlso;
                var clause_left1  = val_edge && val_const ? estart : Operator.Not(estart);
                var clause_right1 = !val_edge && !val_const?Operator.Not(enext) : enext;

                var junction1 = Operator.Create(operator1, clause_left1, clause_right1);

                var conv_outEdges = cfg.Vedges(conv, null);
                var conv_inEdges  = cfg.Vedges(null, conv).Except(cfg.Vedges(parts, conv));
                cfg.RemoveVertices(@const, vnext, conv);
                conv_outEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(vs, e.Target, e.Tag)));
                conv_inEdges.ForEach(e => cfg.AddEdge(new ControlFlowEdge(e.Source, vs, e.Tag)));

                vs.BalancedCode.Add(new Assign(ass.Lhs, junction1));
                vs.BalancedCode.AddElements(conv.BalancedCode.Skip(1));
                vs.Residue.SetElements(conv.Residue);
            }

            cfg.Edges().AssertEach(e => e.Tag.Arity() <= 1);
            cfg.Vertices.Where(v => v.Residue.IsNotEmpty()).AssertEach(v => v.Residue.Count() == 1);
            cfg.Vertices.AssertNone(v => v.Residue.IsNotEmpty() &&
                                    cfg.Vedges(v, null).Any(e => e.IsUnconditional && e.Target != cfg.Finish));
        }