using X86Disassembler.X86;
using X86Disassembler.X86.Operands;
namespace X86Disassembler.Analysers;
///
/// Analyzes data flow through instructions to track register values
///
public class DataFlowAnalyzer
{
// Constants for analysis data keys
private const string REGISTER_VALUE_KEY = "RegisterValue";
private const string MEMORY_VALUE_KEY = "MemoryValue";
///
/// Represents a known value for a register or memory location
///
public class ValueInfo
{
///
/// The type of value (constant, register, memory, unknown)
///
public enum ValueType
{
Unknown,
Constant,
Register,
Memory
}
///
/// The type of this value
///
public ValueType Type { get; set; } = ValueType.Unknown;
///
/// The constant value (if Type is Constant)
///
public ulong? ConstantValue { get; set; }
///
/// The source register (if Type is Register)
///
public RegisterIndex? SourceRegister { get; set; }
///
/// The memory address or expression (if Type is Memory)
///
public string? MemoryExpression { get; set; }
///
/// The instruction that defined this value
///
public Instruction? DefiningInstruction { get; set; }
///
/// Returns a string representation of the value
///
public override string ToString()
{
return Type switch
{
ValueType.Constant => $"0x{ConstantValue:X8}",
ValueType.Register => $"{SourceRegister}",
ValueType.Memory => $"[{MemoryExpression}]",
_ => "unknown"
};
}
}
///
/// Analyzes data flow in the function and stores results in the analyzer context
///
/// The analyzer context to store results in
public void AnalyzeDataFlow(AnalyzerContext context)
{
// Process each block in order
foreach (var block in context.Function.Blocks)
{
// Dictionary to track register values within this block
Dictionary registerValues = new();
// Process each instruction in the block
foreach (var instruction in block.Instructions)
{
// Process the instruction based on its type
ProcessInstruction(instruction, registerValues, context);
// Store the current register state at this instruction's address
StoreRegisterState(instruction.Address, registerValues, context);
}
}
}
///
/// Processes an instruction to update register values
///
/// The instruction to process
/// The current register values
/// The analyzer context
private void ProcessInstruction(Instruction instruction, Dictionary registerValues, AnalyzerContext context)
{
// Handle different instruction types
switch (instruction.Type)
{
// MOV instructions
case InstructionType.Mov:
ProcessMovInstruction(instruction, registerValues);
break;
// XOR instructions
case InstructionType.Xor:
ProcessXorInstruction(instruction, registerValues);
break;
// ADD instructions
case InstructionType.Add:
ProcessAddInstruction(instruction, registerValues);
break;
// SUB instructions
case InstructionType.Sub:
ProcessSubInstruction(instruction, registerValues);
break;
// PUSH/POP instructions can affect register values
case InstructionType.Pop:
ProcessPopInstruction(instruction, registerValues);
break;
// Call instructions typically clobber certain registers
case InstructionType.Call:
ProcessCallInstruction(instruction, registerValues);
break;
// Other instructions that modify registers
default:
// For now, mark destination registers as unknown for unsupported instructions
if (instruction.StructuredOperands.Count > 0 &&
instruction.StructuredOperands[0] is RegisterOperand regOp)
{
registerValues[regOp.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
break;
}
}
///
/// Processes a MOV instruction to update register values
///
private void ProcessMovInstruction(Instruction instruction, Dictionary registerValues)
{
// Handle different MOV variants
if (instruction.StructuredOperands.Count >= 2)
{
var dest = instruction.StructuredOperands[0];
var src = instruction.StructuredOperands[1];
// MOV reg, imm
if (dest is RegisterOperand destReg && src is ImmediateOperand immSrc)
{
registerValues[destReg.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Constant,
ConstantValue = immSrc.Value,
DefiningInstruction = instruction
};
}
// MOV reg, reg
else if (dest is RegisterOperand destReg2 && src is RegisterOperand srcReg)
{
if (registerValues.TryGetValue(srcReg.Register, out var srcValue))
{
// Copy the source value
registerValues[destReg2.Register] = new ValueInfo
{
Type = srcValue.Type,
ConstantValue = srcValue.ConstantValue,
SourceRegister = srcValue.SourceRegister,
MemoryExpression = srcValue.MemoryExpression,
DefiningInstruction = instruction
};
}
else
{
// Source register value is unknown
registerValues[destReg2.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Register,
SourceRegister = srcReg.Register,
DefiningInstruction = instruction
};
}
}
// MOV reg, [mem]
else if (dest is RegisterOperand destReg3 && src is MemoryOperand memSrc)
{
registerValues[destReg3.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Memory,
MemoryExpression = memSrc.ToString(),
DefiningInstruction = instruction
};
}
// MOV [mem], reg or MOV [mem], imm
// These don't update register values, so we don't need to handle them here
}
}
///
/// Processes an XOR instruction to update register values
///
private void ProcessXorInstruction(Instruction instruction, Dictionary registerValues)
{
// Handle XOR reg, reg (often used for zeroing a register)
if (instruction.StructuredOperands.Count >= 2)
{
var dest = instruction.StructuredOperands[0];
var src = instruction.StructuredOperands[1];
// XOR reg, same_reg (zeroing idiom)
if (dest is RegisterOperand destReg && src is RegisterOperand srcReg &&
destReg.Register == srcReg.Register)
{
registerValues[destReg.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Constant,
ConstantValue = 0,
DefiningInstruction = instruction
};
}
// Other XOR operations make the result unknown
else if (dest is RegisterOperand destReg2)
{
registerValues[destReg2.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
}
}
///
/// Processes an ADD instruction to update register values
///
private void ProcessAddInstruction(Instruction instruction, Dictionary registerValues)
{
// Handle ADD reg, imm where we know the register value
if (instruction.StructuredOperands.Count >= 2)
{
var dest = instruction.StructuredOperands[0];
var src = instruction.StructuredOperands[1];
// ADD reg, imm where reg is a known constant
if (dest is RegisterOperand destReg && src is ImmediateOperand immSrc &&
registerValues.TryGetValue(destReg.Register, out var destValue) &&
destValue.Type == ValueInfo.ValueType.Constant &&
destValue.ConstantValue.HasValue)
{
// Calculate the new constant value
registerValues[destReg.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Constant,
ConstantValue = (uint?) (destValue.ConstantValue.Value + immSrc.Value),
DefiningInstruction = instruction
};
}
// Other ADD operations make the result unknown
else if (dest is RegisterOperand destReg2)
{
registerValues[destReg2.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
}
}
///
/// Processes a SUB instruction to update register values
///
private void ProcessSubInstruction(Instruction instruction, Dictionary registerValues)
{
// Handle SUB reg, imm where we know the register value
if (instruction.StructuredOperands.Count >= 2)
{
var dest = instruction.StructuredOperands[0];
var src = instruction.StructuredOperands[1];
// SUB reg, imm where reg is a known constant
if (dest is RegisterOperand destReg && src is ImmediateOperand immSrc &&
registerValues.TryGetValue(destReg.Register, out var destValue) &&
destValue.Type == ValueInfo.ValueType.Constant &&
destValue.ConstantValue.HasValue)
{
// Calculate the new constant value
registerValues[destReg.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Constant,
ConstantValue = (uint?) (destValue.ConstantValue.Value - immSrc.Value),
DefiningInstruction = instruction
};
}
// Other SUB operations make the result unknown
else if (dest is RegisterOperand destReg2)
{
registerValues[destReg2.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
}
}
///
/// Processes a POP instruction to update register values
///
private void ProcessPopInstruction(Instruction instruction, Dictionary registerValues)
{
// POP reg makes the register value unknown (comes from stack)
if (instruction.StructuredOperands.Count >= 1 &&
instruction.StructuredOperands[0] is RegisterOperand destReg)
{
registerValues[destReg.Register] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
}
///
/// Processes a CALL instruction to update register values
///
private void ProcessCallInstruction(Instruction instruction, Dictionary registerValues)
{
// CALL instructions typically clobber EAX, ECX, and EDX in x86 calling conventions
registerValues[RegisterIndex.A] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
registerValues[RegisterIndex.C] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
registerValues[RegisterIndex.D] = new ValueInfo
{
Type = ValueInfo.ValueType.Unknown,
DefiningInstruction = instruction
};
}
///
/// Stores the current register state at the given address
///
private void StoreRegisterState(ulong address, Dictionary registerValues, AnalyzerContext context)
{
// Create a copy of the register values to store
var registerValuesCopy = new Dictionary(registerValues);
// Store in the context
context.StoreAnalysisData(address, REGISTER_VALUE_KEY, registerValuesCopy);
}
///
/// Gets the register values at the given address
///
public static Dictionary? GetRegisterValues(ulong address, AnalyzerContext context)
{
return context.GetAnalysisData>(address, REGISTER_VALUE_KEY);
}
}