using X86Disassembler.Analysers.DecompilerTypes; using X86Disassembler.X86; using X86Disassembler.X86.Operands; namespace X86Disassembler.Analysers; /// /// Analyzes control flow structures in disassembled code /// public class ControlFlowAnalyzer { /// /// The analyzer context /// private readonly AnalyzerContext _context; /// /// Creates a new control flow analyzer /// /// The analyzer context public ControlFlowAnalyzer(AnalyzerContext context) { _context = context; } /// /// Analyzes the control flow of a function to identify high-level structures /// /// The function to analyze public void AnalyzeControlFlow(Function function) { // First, identify if-else structures IdentifyIfElseStructures(function); // Then, identify switch statements IdentifySwitchStatements(function); } /// /// Identifies if-else structures in the control flow graph /// /// The function to analyze private void IdentifyIfElseStructures(Function function) { // First pass: identify basic if-else structures foreach (var block in function.AsmFunction.Blocks) { // Skip blocks that don't end with a conditional jump if (block.Instructions.Count == 0) { continue; } var lastInstruction = block.Instructions[^1]; // Look for conditional jumps (Jcc instructions) if (IsConditionalJump(lastInstruction.Type)) { // This is a potential if-then-else structure // The true branch is the target of the jump // The false branch is the fallthrough block // Get the jump target address ulong targetAddress = GetJumpTargetAddress(lastInstruction); // Find the target block if (_context.BlocksByAddress.TryGetValue(targetAddress, out var targetBlock)) { // Find the fallthrough block (the block that follows this one in memory) var fallthroughBlock = FindFallthroughBlock(block); if (fallthroughBlock != null) { // Check if the fallthrough block ends with an unconditional jump // This could indicate an if-else structure where the 'else' branch jumps to a common merge point InstructionBlock? mergeBlock = null; bool hasElseBlock = true; if (fallthroughBlock.Instructions.Count > 0 && fallthroughBlock.Instructions[^1].Type == InstructionType.Jmp) { // Get the jump target address ulong mergeAddress = GetJumpTargetAddress(fallthroughBlock.Instructions[^1]); // Find the merge block if (_context.BlocksByAddress.TryGetValue(mergeAddress, out var potentialMergeBlock)) { mergeBlock = potentialMergeBlock; } } // Check if the 'then' block also jumps to the same merge point if (mergeBlock != null && targetBlock.Instructions.Count > 0 && targetBlock.Instructions[^1].Type == InstructionType.Jmp) { ulong thenJumpAddress = GetJumpTargetAddress(targetBlock.Instructions[^1]); if (thenJumpAddress == mergeBlock.Address) { // We have a classic if-else structure with a merge point // Store the if-else structure in the context var ifElseStructure = new IfElseStructure { ConditionBlock = block, ThenBlock = targetBlock, ElseBlock = fallthroughBlock, MergeBlock = mergeBlock, IsComplete = true // Both branches merge back }; _context.StoreAnalysisData(block.Address, "IfElseStructure", ifElseStructure); continue; } } // If we get here, we have a simple if-then or if-then-else without a clear merge point var simpleIfStructure = new IfElseStructure { ConditionBlock = block, ThenBlock = targetBlock, ElseBlock = hasElseBlock ? fallthroughBlock : null, IsComplete = false // No clear merge point }; _context.StoreAnalysisData(block.Address, "IfElseStructure", simpleIfStructure); } } } } // Second pass: identify nested if-else structures foreach (var block in function.AsmFunction.Blocks) { var ifElseStructure = _context.GetAnalysisData(block.Address, "IfElseStructure"); if (ifElseStructure != null) { // Check if the 'then' block contains another if-else structure var nestedThenIf = _context.GetAnalysisData(ifElseStructure.ThenBlock.Address, "IfElseStructure"); if (nestedThenIf != null) { ifElseStructure.NestedThenStructure = nestedThenIf; } // Check if the 'else' block contains another if-else structure if (ifElseStructure.ElseBlock != null) { var nestedElseIf = _context.GetAnalysisData(ifElseStructure.ElseBlock.Address, "IfElseStructure"); if (nestedElseIf != null) { ifElseStructure.NestedElseStructure = nestedElseIf; } } } } } /// /// Identifies switch statements in the control flow graph /// /// The function to analyze private void IdentifySwitchStatements(Function function) { // For each block in the function foreach (var block in function.AsmFunction.Blocks) { // Look for patterns that indicate a switch statement // Common patterns include: // 1. A series of compare and jump instructions // 2. An indirect jump through a jump table // For now, we'll focus on the first pattern (series of compares) if (IsPotentialSwitchHeader(block)) { // This is a potential switch statement var switchStructure = new SwitchStructure { HeaderBlock = block, Cases = [] }; // Find the cases by analyzing the successors foreach (var successor in block.Successors) { // Each successor is a potential case switchStructure.Cases.Add(new SwitchCase { CaseBlock = successor, Value = 0 // We'd need more analysis to determine the actual value }); } // Store the switch structure in the context _context.StoreAnalysisData(block.Address, "SwitchStructure", switchStructure); } } } /// /// Checks if the given instruction type is a conditional jump /// /// The instruction type /// True if the instruction is a conditional jump, false otherwise private bool IsConditionalJump(InstructionType type) { // Check for common conditional jumps return type == InstructionType.Jz || type == InstructionType.Jnz || type == InstructionType.Jg || type == InstructionType.Jge || type == InstructionType.Jl || type == InstructionType.Jle || type == InstructionType.Ja || type == InstructionType.Jae || type == InstructionType.Jb || type == InstructionType.Jbe || type == InstructionType.Jo || type == InstructionType.Jno || type == InstructionType.Js || type == InstructionType.Jns || type == InstructionType.Jp || type == InstructionType.Jnp; } /// /// Gets the target address of a jump instruction /// /// The jump instruction /// The target address of the jump private ulong GetJumpTargetAddress(Instruction instruction) { // The target address is usually the first operand of the jump instruction if (instruction.StructuredOperands.Count > 0 && instruction.StructuredOperands[0] is ImmediateOperand immOp) { return (ulong)immOp.Value; } // If we can't determine the target, return 0 return 0; } /// /// Finds the fallthrough block for a given block /// /// The block to find the fallthrough for /// The fallthrough block, or null if none found private InstructionBlock? FindFallthroughBlock(InstructionBlock block) { // The fallthrough block is the one that follows this one in memory // It should be a successor of this block foreach (var successor in block.Successors) { // Check if this successor is the fallthrough block // (its address should be immediately after this block) if (successor.Address > block.Address) { return successor; } } return null; } /// /// Checks if the given block is a potential switch statement header /// /// The block to check /// True if the block is a potential switch header, false otherwise private bool IsPotentialSwitchHeader(InstructionBlock block) { // A switch header typically has multiple successors if (block.Successors.Count <= 2) { return false; } // Look for patterns that indicate a switch statement // For now, we'll just check if the block ends with an indirect jump if (block.Instructions.Count > 0) { var lastInstruction = block.Instructions[^1]; if (lastInstruction.Type == InstructionType.Jmp && lastInstruction.StructuredOperands.Count > 0 && !(lastInstruction.StructuredOperands[0] is ImmediateOperand)) { return true; } } return false; } /// /// Represents an if-else structure in the control flow graph /// public class IfElseStructure { /// /// The block containing the condition /// public InstructionBlock ConditionBlock { get; set; } = null!; /// /// The block representing the 'then' branch (taken when condition is true) /// public InstructionBlock ThenBlock { get; set; } = null!; /// /// The block representing the 'else' branch (taken when condition is false) /// public InstructionBlock? ElseBlock { get; set; } /// /// The block where both branches merge back together (if applicable) /// public InstructionBlock? MergeBlock { get; set; } /// /// Whether this is a complete if-else structure with a merge point /// public bool IsComplete { get; set; } /// /// Nested if-else structure in the 'then' branch (if any) /// public IfElseStructure? NestedThenStructure { get; set; } /// /// Nested if-else structure in the 'else' branch (if any) /// public IfElseStructure? NestedElseStructure { get; set; } } /// /// Represents a switch statement in the control flow graph /// public class SwitchStructure { /// /// The block containing the switch header /// public InstructionBlock HeaderBlock { get; set; } = null!; /// /// The cases of the switch statement /// public List Cases { get; set; } = []; } /// /// Represents a case in a switch statement /// public class SwitchCase { /// /// The value of the case /// public int Value { get; set; } /// /// The block containing the case code /// public InstructionBlock CaseBlock { get; set; } = null!; } }