using System; using System.Collections; using System.Collections.Generic; using System.Linq; using dnlib.DotNet.Emit; namespace Confuser.Core.Helpers { /// /// A Control Flow Graph (CFG) of a method /// public class ControlFlowGraph : IEnumerable { readonly List blocks; readonly CilBody body; readonly int[] instrBlocks; readonly Dictionary indexMap; ControlFlowGraph(CilBody body) { this.body = body; instrBlocks = new int[body.Instructions.Count]; blocks = new List(); indexMap = new Dictionary(); for (int i = 0; i < body.Instructions.Count; i++) indexMap.Add(body.Instructions[i], i); } /// /// Gets the number of blocks in this CFG. /// /// The number of blocks. public int Count { get { return blocks.Count; } } /// /// Gets the of the specified id. /// /// The id. /// The block with specified id. public ControlFlowBlock this[int id] { get { return blocks[id]; } } /// /// Gets the corresponding method body. /// /// The method body. public CilBody Body { get { return body; } } IEnumerator IEnumerable.GetEnumerator() { return blocks.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return blocks.GetEnumerator(); } /// /// Gets the block containing the specified instruction. /// /// The index of instruction. /// The block containing the instruction. public ControlFlowBlock GetContainingBlock(int instrIndex) { return blocks[instrBlocks[instrIndex]]; } /// /// Gets the index of the specified instruction. /// /// The instruction. /// The index of instruction. public int IndexOf(Instruction instr) { return indexMap[instr]; } void PopulateBlockHeaders(HashSet blockHeaders, HashSet entryHeaders) { for (int i = 0; i < body.Instructions.Count; i++) { Instruction instr = body.Instructions[i]; if (instr.Operand is Instruction) { blockHeaders.Add((Instruction)instr.Operand); if (i + 1 < body.Instructions.Count) blockHeaders.Add(body.Instructions[i + 1]); } else if (instr.Operand is Instruction[]) { foreach (Instruction target in (Instruction[])instr.Operand) blockHeaders.Add(target); if (i + 1 < body.Instructions.Count) blockHeaders.Add(body.Instructions[i + 1]); } else if ((instr.OpCode.FlowControl == FlowControl.Throw || instr.OpCode.FlowControl == FlowControl.Return) && i + 1 < body.Instructions.Count) { blockHeaders.Add(body.Instructions[i + 1]); } } blockHeaders.Add(body.Instructions[0]); foreach (ExceptionHandler eh in body.ExceptionHandlers) { blockHeaders.Add(eh.TryStart); blockHeaders.Add(eh.HandlerStart); blockHeaders.Add(eh.FilterStart); entryHeaders.Add(eh.HandlerStart); entryHeaders.Add(eh.FilterStart); } } void SplitBlocks(HashSet blockHeaders, HashSet entryHeaders) { int nextBlockId = 0; int currentBlockId = -1; Instruction currentBlockHdr = null; for (int i = 0; i < body.Instructions.Count; i++) { Instruction instr = body.Instructions[i]; if (blockHeaders.Contains(instr)) { if (currentBlockHdr != null) { Instruction footer = body.Instructions[i - 1]; var type = ControlFlowBlockType.Normal; if (entryHeaders.Contains(currentBlockHdr) || currentBlockHdr == body.Instructions[0]) type |= ControlFlowBlockType.Entry; if (footer.OpCode.FlowControl == FlowControl.Return || footer.OpCode.FlowControl == FlowControl.Throw) type |= ControlFlowBlockType.Exit; blocks.Add(new ControlFlowBlock(currentBlockId, type, currentBlockHdr, footer)); } currentBlockId = nextBlockId++; currentBlockHdr = instr; } instrBlocks[i] = currentBlockId; } if (blocks.Count == 0 || blocks[blocks.Count - 1].Id != currentBlockId) { Instruction footer = body.Instructions[body.Instructions.Count - 1]; var type = ControlFlowBlockType.Normal; if (entryHeaders.Contains(currentBlockHdr) || currentBlockHdr == body.Instructions[0]) type |= ControlFlowBlockType.Entry; if (footer.OpCode.FlowControl == FlowControl.Return || footer.OpCode.FlowControl == FlowControl.Throw) type |= ControlFlowBlockType.Exit; blocks.Add(new ControlFlowBlock(currentBlockId, type, currentBlockHdr, footer)); } } void LinkBlocks() { for (int i = 0; i < body.Instructions.Count; i++) { Instruction instr = body.Instructions[i]; if (instr.Operand is Instruction) { ControlFlowBlock srcBlock = blocks[instrBlocks[i]]; ControlFlowBlock dstBlock = blocks[instrBlocks[indexMap[(Instruction)instr.Operand]]]; dstBlock.Sources.Add(srcBlock); srcBlock.Targets.Add(dstBlock); } else if (instr.Operand is Instruction[]) { foreach (Instruction target in (Instruction[])instr.Operand) { ControlFlowBlock srcBlock = blocks[instrBlocks[i]]; ControlFlowBlock dstBlock = blocks[instrBlocks[indexMap[target]]]; dstBlock.Sources.Add(srcBlock); srcBlock.Targets.Add(dstBlock); } } } for (int i = 0; i < blocks.Count; i++) { if (blocks[i].Footer.OpCode.FlowControl != FlowControl.Branch && blocks[i].Footer.OpCode.FlowControl != FlowControl.Return && blocks[i].Footer.OpCode.FlowControl != FlowControl.Throw) { blocks[i].Targets.Add(blocks[i + 1]); blocks[i + 1].Sources.Add(blocks[i]); } } } /// /// Constructs a CFG from the specified method body. /// /// The method body. /// The CFG of the given method body. public static ControlFlowGraph Construct(CilBody body) { var graph = new ControlFlowGraph(body); if (body.Instructions.Count == 0) return graph; // Populate block headers var blockHeaders = new HashSet(); var entryHeaders = new HashSet(); graph.PopulateBlockHeaders(blockHeaders, entryHeaders); // Split blocks graph.SplitBlocks(blockHeaders, entryHeaders); // Link blocks graph.LinkBlocks(); return graph; } } /// /// The type of Control Flow Block /// [Flags] public enum ControlFlowBlockType { /// /// The block is a normal block /// Normal = 0, /// /// There are unknown edges to this block. Usually used at exception handlers / method entry. /// Entry = 1, /// /// There are unknown edges from this block. Usually used at filter blocks / throw / method exit. /// Exit = 2 } /// /// A block in Control Flow Graph (CFG). /// public class ControlFlowBlock { /// /// The footer instruction /// public readonly Instruction Footer; /// /// The header instruction /// public readonly Instruction Header; /// /// The identifier of this block /// public readonly int Id; /// /// The type of this block /// public readonly ControlFlowBlockType Type; internal ControlFlowBlock(int id, ControlFlowBlockType type, Instruction header, Instruction footer) { Id = id; Type = type; Header = header; Footer = footer; Sources = new List(); Targets = new List(); } /// /// Gets the source blocks of this control flow block. /// /// The source blocks. public IList Sources { get; private set; } /// /// Gets the target blocks of this control flow block. /// /// The target blocks. public IList Targets { get; private set; } /// /// Returns a that represents this block. /// /// A that represents this block. public override string ToString() { return string.Format("Block {0} => {1} {2}", Id, Type, string.Join(", ", Targets.Select(block => block.Id.ToString()).ToArray())); } } }