mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-07-01 20:40:27 +03:00
t
This commit is contained in:
13
X86Disassembler/Analysers/AsmFunction.cs
Normal file
13
X86Disassembler/Analysers/AsmFunction.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace X86Disassembler.Analysers;
|
||||
|
||||
public class AsmFunction
|
||||
{
|
||||
public ulong Address { get; set; }
|
||||
|
||||
public List<InstructionBlock> Blocks { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Function at {Address:X8}\n{string.Join("\n", Blocks.Select(x => $"\t{x}"))}";
|
||||
}
|
||||
}
|
197
X86Disassembler/Analysers/BlockDisassembler.cs
Normal file
197
X86Disassembler/Analysers/BlockDisassembler.cs
Normal file
@ -0,0 +1,197 @@
|
||||
using X86Disassembler.X86;
|
||||
|
||||
namespace X86Disassembler.Analysers;
|
||||
|
||||
/// <summary>
|
||||
/// Disassembles code into basic blocks by following control flow instructions.
|
||||
/// A basic block is a sequence of instructions with a single entry point (the first instruction)
|
||||
/// and a single exit point (the last instruction, typically a jump or return).
|
||||
/// </summary>
|
||||
public class BlockDisassembler
|
||||
{
|
||||
// The buffer containing the code to disassemble
|
||||
private readonly byte[] _codeBuffer;
|
||||
|
||||
// The length of the buffer
|
||||
private readonly int _length;
|
||||
|
||||
// The base address of the code
|
||||
private readonly ulong _baseAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BlockDisassembler class
|
||||
/// </summary>
|
||||
/// <param name="codeBuffer">The raw code bytes to be disassembled</param>
|
||||
/// <param name="baseAddress">The base RVA (Relative Virtual Address) of the code section</param>
|
||||
public BlockDisassembler(byte[] codeBuffer, ulong baseAddress)
|
||||
{
|
||||
_codeBuffer = codeBuffer;
|
||||
_length = codeBuffer.Length;
|
||||
|
||||
_baseAddress = baseAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disassembles code starting from the specified RVA address by following control flow.
|
||||
/// Creates blocks of instructions separated by jumps, branches, and returns.
|
||||
/// </summary>
|
||||
/// <param name="rvaAddress">The RVA (Relative Virtual Address) to start disassembly from</param>
|
||||
/// <returns>A list of instruction blocks representing the control flow of the code</returns>
|
||||
public AsmFunction DisassembleFromAddress(uint rvaAddress)
|
||||
{
|
||||
// Create instruction decoder for parsing the code buffer
|
||||
InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
|
||||
|
||||
// Track visited addresses to prevent infinite loops
|
||||
HashSet<ulong> visitedAddresses = [];
|
||||
|
||||
// Queue of addresses to process (breadth-first approach)
|
||||
Queue<ulong> addressQueue = [];
|
||||
// Calculate the file offset from the RVA by subtracting the base address
|
||||
addressQueue.Enqueue(rvaAddress - _baseAddress);
|
||||
|
||||
// List to store discovered basic blocks
|
||||
List<InstructionBlock> blocks = [];
|
||||
while (addressQueue.Count > 0)
|
||||
{
|
||||
// Get the next address to process
|
||||
var address = addressQueue.Dequeue();
|
||||
|
||||
// Skip if we've already visited this address
|
||||
if (!visitedAddresses.Add(address))
|
||||
{
|
||||
Console.WriteLine($"Already visited address {address}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Position the decoder at the current address
|
||||
decoder.SetPosition((int) address);
|
||||
|
||||
// Collect instructions for this block
|
||||
List<Instruction> instructions = [];
|
||||
|
||||
// Process instructions until we hit a control flow change
|
||||
while (true)
|
||||
{
|
||||
// If we've stepped onto an existing block, create a new block up to this point
|
||||
// and stop processing this path (to avoid duplicating instructions)
|
||||
if (blocks.Any(x => x.Address == (ulong) decoder.GetPosition()))
|
||||
{
|
||||
Console.WriteLine("Stepped on to existing block. Creating in the middle");
|
||||
RegisterBlock(blocks, address, instructions);
|
||||
break;
|
||||
}
|
||||
|
||||
// Decode the next instruction
|
||||
var instruction = decoder.DecodeInstruction();
|
||||
|
||||
// Handle decoding failures
|
||||
if (instruction is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpectedly failed to decode instruction at {address}");
|
||||
}
|
||||
|
||||
// Add the instruction to the current block
|
||||
instructions.Add(instruction);
|
||||
|
||||
// Check for conditional jump (e.g., JZ, JNZ, JLE)
|
||||
// For conditional jumps, we need to follow both the jump target and the fall-through path
|
||||
if (instruction.Type.IsConditionalJump())
|
||||
{
|
||||
// Register this block (it ends with a conditional jump)
|
||||
RegisterBlock(blocks, address, instructions);
|
||||
|
||||
// Queue the jump target address for processing
|
||||
addressQueue.Enqueue(
|
||||
instruction.StructuredOperands[0]
|
||||
.GetValue()
|
||||
);
|
||||
|
||||
// Queue the fall-through address (next instruction after this jump)
|
||||
addressQueue.Enqueue((uint) decoder.GetPosition());
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for unconditional jump (e.g., JMP)
|
||||
// For unconditional jumps, we only follow the jump target
|
||||
if (instruction.Type.IsRegularJump())
|
||||
{
|
||||
// Register this block (it ends with an unconditional jump)
|
||||
RegisterBlock(blocks, address, instructions);
|
||||
|
||||
// Queue the jump target address for processing
|
||||
addressQueue.Enqueue(
|
||||
instruction.StructuredOperands[0]
|
||||
.GetValue()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for return instruction (e.g., RET, RETF)
|
||||
// Returns end a block without any successors
|
||||
if (instruction.Type.IsRet())
|
||||
{
|
||||
// Register this block (it ends with a return)
|
||||
RegisterBlock(blocks, address, instructions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since blocks aren't necessarily ordered (ASM can jump anywhere it likes)
|
||||
// we need to sort the blocks ourselves
|
||||
blocks.Sort((b1, b2) => b1.Address.CompareTo(b2.Address));
|
||||
|
||||
return new AsmFunction()
|
||||
{
|
||||
Address = rvaAddress,
|
||||
Blocks = blocks,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and registers a new instruction block in the blocks collection
|
||||
/// </summary>
|
||||
/// <param name="blocks">The list of blocks to add to</param>
|
||||
/// <param name="address">The starting address of the block</param>
|
||||
/// <param name="instructions">The instructions contained in the block</param>
|
||||
public void RegisterBlock(List<InstructionBlock> blocks, ulong address, List<Instruction> instructions)
|
||||
{
|
||||
// Create a new block with the provided address and instructions
|
||||
var block = new InstructionBlock()
|
||||
{
|
||||
Address = address,
|
||||
Instructions = instructions
|
||||
};
|
||||
|
||||
// Add the block to the collection
|
||||
blocks.Add(block);
|
||||
|
||||
// Log the created block for debugging
|
||||
Console.WriteLine($"Created block:\n{block}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic block of instructions with a single entry and exit point
|
||||
/// </summary>
|
||||
public class InstructionBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting address of the block
|
||||
/// </summary>
|
||||
public ulong Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of instructions contained in this block
|
||||
/// </summary>
|
||||
public List<Instruction> Instructions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the block, including its address and instructions
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Address: {Address:X8}\n{string.Join("\n", Instructions)}";
|
||||
}
|
||||
}
|
40
X86Disassembler/Analysers/InstructionTypeExtensions.cs
Normal file
40
X86Disassembler/Analysers/InstructionTypeExtensions.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using X86Disassembler.X86;
|
||||
|
||||
namespace X86Disassembler.Analysers;
|
||||
|
||||
public static class InstructionTypeExtensions
|
||||
{
|
||||
public static bool IsConditionalJump(this InstructionType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
InstructionType.Jg => true,
|
||||
InstructionType.Jge => true,
|
||||
InstructionType.Jl => true,
|
||||
InstructionType.Jle => true,
|
||||
InstructionType.Ja => true,
|
||||
InstructionType.Jae => true,
|
||||
InstructionType.Jb => true,
|
||||
InstructionType.Jbe => true,
|
||||
InstructionType.Jz => true,
|
||||
InstructionType.Jnz => true,
|
||||
InstructionType.Jo => true,
|
||||
InstructionType.Jno => true,
|
||||
InstructionType.Js => true,
|
||||
InstructionType.Jns => true,
|
||||
InstructionType.Jp => true,
|
||||
InstructionType.Jnp => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsRegularJump(this InstructionType type)
|
||||
{
|
||||
return type == InstructionType.Jmp;
|
||||
}
|
||||
|
||||
public static bool IsRet(this InstructionType type)
|
||||
{
|
||||
return type is InstructionType.Ret or InstructionType.Retf;
|
||||
}
|
||||
}
|
16
X86Disassembler/Analysers/OperandExtensions.cs
Normal file
16
X86Disassembler/Analysers/OperandExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using X86Disassembler.X86;
|
||||
using X86Disassembler.X86.Operands;
|
||||
|
||||
namespace X86Disassembler.Analysers;
|
||||
|
||||
public static class OperandExtensions
|
||||
{
|
||||
public static uint GetValue(this Operand operand)
|
||||
{
|
||||
return operand switch
|
||||
{
|
||||
RelativeOffsetOperand roo => roo.TargetAddress,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user