using dnlib.DotNet; using dnlib.DotNet.Emit; using System; using System.Collections.Generic; using System.Linq; namespace Obfuz.Emit { public class BasicBlock { public readonly List instructions = new List(); public readonly List inBlocks = new List(); public readonly List outBlocks = new List(); public bool inLoop; public void AddTargetBasicBlock(BasicBlock target) { if (!outBlocks.Contains(target)) { outBlocks.Add(target); } if (!target.inBlocks.Contains(this)) { target.inBlocks.Add(this); } } } public class BasicBlockCollection { private readonly MethodDef _method; private readonly List _blocks = new List(); private readonly Dictionary _inst2BlockMap = new Dictionary(); public IList Blocks => _blocks; public BasicBlockCollection(MethodDef method, bool computeInLoop) { _method = method; HashSet splitPoints = BuildSplitPoint(method); BuildBasicBlocks(method, splitPoints); BuildInOutGraph(method); if (computeInLoop) { ComputeBlocksInLoop(); } } public void ComputeBlocksInLoop() { var loopBlocks = FindLoopBlocks(_blocks); foreach (var block in loopBlocks) { block.inLoop = true; } } public BasicBlock GetBasicBlockByInstruction(Instruction inst) { return _inst2BlockMap[inst]; } private HashSet BuildSplitPoint(MethodDef method) { var insts = method.Body.Instructions; var splitPoints = new HashSet(); foreach (ExceptionHandler eh in method.Body.ExceptionHandlers) { if (eh.TryStart != null) { splitPoints.Add(eh.TryStart); } if (eh.TryEnd != null) { splitPoints.Add(eh.TryEnd); } if (eh.HandlerStart != null) { splitPoints.Add(eh.HandlerStart); } if (eh.HandlerEnd != null) { splitPoints.Add(eh.HandlerEnd); } if (eh.FilterStart != null) { splitPoints.Add(eh.FilterStart); } } for (int i = 0, n = insts.Count; i < n; i++) { Instruction curInst = insts[i]; Instruction nextInst = i + 1 < n ? insts[i + 1] : null; switch (curInst.OpCode.FlowControl) { case FlowControl.Branch: { if (nextInst != null) { splitPoints.Add(nextInst); } splitPoints.Add((Instruction)curInst.Operand); break; } case FlowControl.Cond_Branch: { if (nextInst != null) { splitPoints.Add(nextInst); } if (curInst.Operand is Instruction targetInst) { splitPoints.Add(targetInst); } else if (curInst.Operand is Instruction[] targetInsts) { foreach (var target in targetInsts) { splitPoints.Add(target); } } break; } case FlowControl.Return: { if (nextInst != null) { splitPoints.Add(nextInst); } break; } case FlowControl.Throw: { if (nextInst != null) { splitPoints.Add(nextInst); } break; } } } return splitPoints; } private void BuildBasicBlocks(MethodDef method, HashSet splitPoints) { var insts = method.Body.Instructions; BasicBlock curBlock = new BasicBlock(); foreach (Instruction inst in insts) { if (splitPoints.Contains(inst) && curBlock.instructions.Count > 0) { _blocks.Add(curBlock); curBlock = new BasicBlock(); } curBlock.instructions.Add(inst); _inst2BlockMap.Add(inst, curBlock); } if (curBlock.instructions.Count > 0) { _blocks.Add(curBlock); } } private void BuildInOutGraph(MethodDef method) { var insts = method.Body.Instructions; for (int i = 0, n = _blocks.Count; i < n; i++) { BasicBlock curBlock = _blocks[i]; BasicBlock nextBlock = i + 1 < n ? _blocks[i + 1] : null; Instruction lastInst = curBlock.instructions.Last(); switch (lastInst.OpCode.FlowControl) { case FlowControl.Branch: { Instruction targetInst = (Instruction)lastInst.Operand; BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst); curBlock.AddTargetBasicBlock(targetBlock); break; } case FlowControl.Cond_Branch: { if (lastInst.Operand is Instruction targetInst) { BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst); curBlock.AddTargetBasicBlock(targetBlock); } else if (lastInst.Operand is Instruction[] targetInsts) { foreach (var target in targetInsts) { BasicBlock targetBlock = GetBasicBlockByInstruction(target); curBlock.AddTargetBasicBlock(targetBlock); } } else { throw new Exception("Invalid operand type for conditional branch"); } if (nextBlock != null) { curBlock.AddTargetBasicBlock(nextBlock); } break; } case FlowControl.Return: case FlowControl.Throw: { break; } } } } private static HashSet FindLoopBlocks(List allBlocks) { // Tarjan算法找强连通分量 var sccList = FindStronglyConnectedComponents(allBlocks); // 筛选有效循环 var loopBlocks = new HashSet(); foreach (var scc in sccList) { // 有效循环需满足以下条件之一: // 1. 分量包含多个块 // 2. 单个块有自环(跳转自己) if (scc.Count > 1 || (scc.Count == 1 && scc[0].outBlocks.Contains(scc[0]))) { foreach (var block in scc) { loopBlocks.Add(block); } } } return loopBlocks; } private static List> FindStronglyConnectedComponents(List allBlocks) { int index = 0; var stack = new Stack(); var indexes = new Dictionary(); var lowLinks = new Dictionary(); var onStack = new HashSet(); var sccList = new List>(); foreach (var block in allBlocks.Where(b => !indexes.ContainsKey(b))) { StrongConnect(block); } return sccList; void StrongConnect(BasicBlock v) { indexes[v] = index; lowLinks[v] = index; index++; stack.Push(v); onStack.Add(v); foreach (var w in v.outBlocks) { if (!indexes.ContainsKey(w)) { StrongConnect(w); lowLinks[v] = System.Math.Min(lowLinks[v], lowLinks[w]); } else if (onStack.Contains(w)) { lowLinks[v] = System.Math.Min(lowLinks[v], indexes[w]); } } if (lowLinks[v] == indexes[v]) { var scc = new List(); BasicBlock w; do { w = stack.Pop(); onStack.Remove(w); scc.Add(w); } while (!w.Equals(v)); sccList.Add(scc); } } } } }