0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-05-21 12:51:18 +03:00

Refactor instruction handlers to use single instruction per handler pattern

This commit is contained in:
bird_egop 2025-04-12 19:57:42 +03:00
parent 82ffd51a3e
commit 58a148ebd8
22 changed files with 1386 additions and 344 deletions

View File

@ -1,5 +1,3 @@
namespace X86Disassembler;
using System;
using System.IO;
using System.Text;
@ -7,203 +5,138 @@ using System.Collections.Generic;
using X86Disassembler.PE;
using X86Disassembler.X86;
internal class Program
namespace X86Disassembler;
/// <summary>
/// Main program class
/// </summary>
public class Program
{
// Path to the DLL file to disassemble
private const string DllPath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll"; // Example path, replace with your target DLL
// Hardcoded file path for testing
private const string FilePath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll";
// Maximum number of instructions to display per section
private const int MaxInstructionsToDisplay = 50;
static void Main(string[] args)
/// <summary>
/// Main entry point
/// </summary>
/// <param name="args">Command line arguments</param>
public static void Main(string[] args)
{
Console.WriteLine("X86 Disassembler and Decompiler");
Console.WriteLine("--------------------------------");
string filePath = DllPath;
// Load the file
Console.WriteLine($"Loading file: {FilePath}");
byte[] fileBytes = File.ReadAllBytes(FilePath);
Console.WriteLine($"Successfully loaded {FilePath}");
Console.WriteLine($"File size: {fileBytes.Length} bytes\n");
Console.WriteLine($"Loading file: {filePath}");
// Parse the PE format
Console.WriteLine("Parsing PE format...\n");
PEFormat peFormat = new PEFormat(fileBytes);
peFormat.Parse();
try
{
// Load the file into memory
byte[] fileBytes = File.ReadAllBytes(filePath);
Console.WriteLine($"Successfully loaded {filePath}");
Console.WriteLine($"File size: {fileBytes.Length} bytes");
Console.WriteLine();
Console.WriteLine("Parsing PE format...");
Console.WriteLine();
// Parse the PE format
PEFormat peFormat = new PEFormat(fileBytes);
if (!peFormat.Parse())
{
Console.WriteLine("Failed to parse PE file.");
return;
}
// Display PE information
DisplayPEInfo(peFormat);
// Disassemble code sections
DisassembleCodeSections(peFormat);
Console.WriteLine();
Console.WriteLine("Press Enter to exit...");
Console.Read(); // Use Read instead of ReadKey to avoid errors in redirected console
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
}
/// <summary>
/// Displays information about the PE file
/// </summary>
/// <param name="peFormat">The PE format object</param>
private static void DisplayPEInfo(PEFormat peFormat)
{
// Print PE file information
Console.WriteLine("PE File Information:");
Console.WriteLine($"Architecture: {(peFormat.OptionalHeader.Is64Bit() ? "64-bit" : "32-bit")}");
Console.WriteLine($"Entry Point: 0x{peFormat.OptionalHeader.AddressOfEntryPoint:X8}");
Console.WriteLine($"Image Base: 0x{peFormat.OptionalHeader.ImageBase:X8}");
Console.WriteLine($"Number of Sections: {peFormat.FileHeader.NumberOfSections}");
Console.WriteLine();
Console.WriteLine("\nSections:");
for (int i = 0; i < peFormat.SectionHeaders.Count; i++)
// Print section information
Console.WriteLine("Sections:");
foreach (var section in peFormat.SectionHeaders)
{
var section = peFormat.SectionHeaders[i];
string flags = "";
// Use the section's methods to determine characteristics
if (section.ContainsCode()) flags += "Code ";
if (section.IsExecutable()) flags += "Exec ";
if (section.IsReadable()) flags += "Read ";
if (section.IsWritable()) flags += "Write";
Console.WriteLine($" {i}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.VirtualSize,-8} [{flags}]");
Console.WriteLine($" {peFormat.SectionHeaders.IndexOf(section)}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.VirtualSize,-8} [{flags}]");
}
Console.WriteLine();
// Display exported functions
// Print export information
if (peFormat.ExportDirectory != null)
{
Console.WriteLine("\nExported Functions:");
Console.WriteLine("Exported Functions:");
Console.WriteLine($"DLL Name: {peFormat.ExportDirectory.DllName}");
Console.WriteLine($"Number of Functions: {peFormat.ExportDirectory.NumberOfFunctions}");
Console.WriteLine($"Number of Names: {peFormat.ExportDirectory.NumberOfNames}");
for (int i = 0; i < peFormat.ExportedFunctions.Count; i++)
{
var function = peFormat.ExportedFunctions[i];
Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.AddressRva:X8})");
var export = peFormat.ExportedFunctions[i];
Console.WriteLine($" {i}: {export.Name} (Ordinal={export.Ordinal}, RVA=0x{export.AddressRva:X8})");
}
Console.WriteLine();
}
// Display imported functions
// Print import information
if (peFormat.ImportDescriptors.Count > 0)
{
Console.WriteLine("\nImported Functions:");
Console.WriteLine("Imported Functions:");
Console.WriteLine($"Number of Imported DLLs: {peFormat.ImportDescriptors.Count}");
for (int i = 0; i < peFormat.ImportDescriptors.Count; i++)
foreach (var import in peFormat.ImportDescriptors)
{
var descriptor = peFormat.ImportDescriptors[i];
Console.WriteLine($" DLL: {descriptor.DllName}");
Console.WriteLine($" DLL: {import.DllName}");
for (int j = 0; j < descriptor.Functions.Count; j++)
for (int i = 0; i < import.Functions.Count; i++)
{
var function = descriptor.Functions[j];
var function = import.Functions[i];
if (function.IsOrdinal)
{
Console.WriteLine($" {j}: Ordinal {function.Ordinal}");
Console.WriteLine($" {i}: Ordinal {function.Ordinal}");
}
else
{
Console.WriteLine($" {j}: {function.Name} (Hint={function.Hint})");
Console.WriteLine($" {i}: {function.Name} (Hint={function.Hint})");
}
}
if (i < peFormat.ImportDescriptors.Count - 1)
{
Console.WriteLine(); // Add a blank line between DLLs for better readability
}
}
Console.WriteLine();
}
}
/// <summary>
/// Disassembles the code sections of the PE file
/// </summary>
/// <param name="peFormat">The PE format object</param>
private static void DisassembleCodeSections(PEFormat peFormat)
{
// Find code sections
var codeSections = peFormat.SectionHeaders.FindAll(s => s.ContainsCode());
Console.WriteLine($"\nFound {codeSections.Count} code section(s):");
Console.WriteLine($"Found {codeSections.Count} code section(s):");
foreach (var section in codeSections)
{
Console.WriteLine($" - {section.Name}: Size={section.VirtualSize} bytes, RVA=0x{section.VirtualAddress:X8}");
}
Console.WriteLine();
// Disassemble each code section
for (int i = 0; i < peFormat.SectionHeaders.Count; i++)
// Disassemble the first code section
if (codeSections.Count > 0)
{
var section = peFormat.SectionHeaders[i];
// Skip non-code sections
if (!section.ContainsCode())
continue;
var section = codeSections[0];
byte[] codeBytes = peFormat.GetSectionData(peFormat.SectionHeaders.IndexOf(section));
Console.WriteLine($"Disassembling section {section.Name} at RVA 0x{section.VirtualAddress:X8}:");
// Get section data using the section index
byte[] sectionData = peFormat.GetSectionData(i);
// Create a disassembler for the code section
Disassembler disassembler = new Disassembler(codeBytes, section.VirtualAddress);
// Create a disassembler for this section
ulong baseAddress = peFormat.OptionalHeader.ImageBase + section.VirtualAddress;
Disassembler disassembler = new Disassembler(sectionData, baseAddress);
// Disassemble all instructions
var instructions = disassembler.Disassemble();
// Disassemble and display instructions
int count = 0;
int maxInstructions = MaxInstructionsToDisplay; // Use the constant
while (count < maxInstructions)
// Print the first 100 instructions
int count = Math.Min(100, instructions.Count);
for (int i = 0; i < count; i++)
{
Instruction? instruction = disassembler.DisassembleNext();
if (instruction == null)
{
break;
}
// Format the instruction bytes
StringBuilder bytesStr = new StringBuilder();
foreach (byte b in instruction.Bytes)
{
bytesStr.Append($"{b:X2} ");
}
// Format the instruction
// Calculate the RVA by subtracting the image base
ulong rva = instruction.Address - peFormat.OptionalHeader.ImageBase;
string addressStr = $"{rva:X8}";
string bytesDisplay = bytesStr.ToString().PadRight(20); // Pad to 20 characters
string operandsStr = string.IsNullOrEmpty(instruction.Operands) ? "" : $" {instruction.Operands}";
Console.WriteLine($" {addressStr} {bytesDisplay} {instruction.Mnemonic}{operandsStr}");
count++;
Console.WriteLine(instructions[i]);
}
if (sectionData.Length > count * 10) // If we've only shown a small portion
// Print a summary of how many more instructions there are
if (instructions.Count > count)
{
Console.WriteLine($" ... ({sectionData.Length - (count * 10)} more bytes not shown)");
Console.WriteLine($"... ({instructions.Count - count} more bytes not shown)");
}
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
}

View File

@ -1,4 +1,5 @@
using System.Text;
using System.Collections.Generic;
namespace X86Disassembler.X86;
@ -7,77 +8,62 @@ namespace X86Disassembler.X86;
/// </summary>
public class Disassembler
{
// Buffer containing the code to disassemble
// The buffer containing the code to disassemble
private readonly byte[] _codeBuffer;
// Base address for the code (RVA)
private readonly ulong _baseAddress;
// The length of the buffer
private readonly int _length;
// Current position in the code buffer
private int _position;
// Instruction decoder
private readonly InstructionDecoder _decoder;
// The base address of the code
private readonly uint _baseAddress;
/// <summary>
/// Initializes a new instance of the Disassembler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to disassemble</param>
/// <param name="baseAddress">The base address (RVA) of the code</param>
public Disassembler(byte[] codeBuffer, ulong baseAddress)
/// <param name="baseAddress">The base address of the code</param>
public Disassembler(byte[] codeBuffer, uint baseAddress)
{
_codeBuffer = codeBuffer;
_length = codeBuffer.Length;
_baseAddress = baseAddress;
_position = 0;
_decoder = new InstructionDecoder(codeBuffer);
}
/// <summary>
/// Disassembles the next instruction in the code buffer
/// </summary>
/// <returns>The disassembled instruction, or null if the end of the buffer is reached</returns>
public Instruction? DisassembleNext()
{
if (_position >= _codeBuffer.Length)
{
return null; // End of buffer reached
}
// Create a new instruction
Instruction instruction = new Instruction
{
Address = _baseAddress + (uint)_position
};
// Decode the instruction
int bytesRead = _decoder.DecodeAt(_position, instruction);
if (bytesRead == 0)
{
return null; // Failed to decode instruction
}
// Update position
_position += bytesRead;
return instruction;
}
/// <summary>
/// Disassembles all instructions in the code buffer
/// Disassembles the code buffer and returns the disassembled instructions
/// </summary>
/// <returns>A list of disassembled instructions</returns>
public List<Instruction> DisassembleAll()
public List<Instruction> Disassemble()
{
List<Instruction> instructions = new List<Instruction>();
// Reset position
_position = 0;
// Create an instruction decoder
InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
// Disassemble all instructions
Instruction? instruction;
while ((instruction = DisassembleNext()) != null)
// Decode instructions until the end of the buffer is reached
while (true)
{
int position = decoder.GetPosition();
// Check if we've reached the end of the buffer
if (position >= _length)
{
break;
}
// Decode the next instruction
Instruction? instruction = decoder.DecodeInstruction();
// Check if decoding failed
if (instruction == null)
{
break;
}
// Adjust the instruction address to include the base address
instruction.Address += _baseAddress;
// Add the instruction to the list
instructions.Add(instruction);
}

View File

@ -0,0 +1,59 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for CALL rel32 instruction (0xE8)
/// </summary>
public class CallRel32Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the CallRel32Handler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public CallRel32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xE8;
}
/// <summary>
/// Decodes a CALL rel32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "call";
int position = Decoder.GetPosition();
if (position + 4 > Length)
{
return false;
}
// Read the relative offset
int offset = BitConverter.ToInt32(CodeBuffer, position);
Decoder.SetPosition(position + 4);
// Calculate the target address
uint targetAddress = (uint)(position + offset + 4);
// Set the operands
instruction.Operands = $"0x{targetAddress:X8}";
return true;
}
}

View File

@ -0,0 +1,68 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for conditional jump instructions (0x70-0x7F)
/// </summary>
public class ConditionalJumpHandler : InstructionHandler
{
// Mnemonics for conditional jumps
private static readonly string[] ConditionalJumpMnemonics = new string[]
{
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "jnbe",
"js", "jns", "jp", "jnp", "jl", "jnl", "jle", "jnle"
};
/// <summary>
/// Initializes a new instance of the ConditionalJumpHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public ConditionalJumpHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
// Conditional jumps are in the range 0x70-0x7F
return opcode >= 0x70 && opcode <= 0x7F;
}
/// <summary>
/// Decodes a conditional jump instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Get the mnemonic from the table
int index = opcode - 0x70;
instruction.Mnemonic = ConditionalJumpMnemonics[index];
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the relative offset
sbyte offset = (sbyte)CodeBuffer[position];
Decoder.SetPosition(position + 1);
// Calculate the target address
uint targetAddress = (uint)(position + offset + 1);
// Set the operands
instruction.Operands = $"0x{targetAddress:X8}";
return true;
}
}

View File

@ -0,0 +1,63 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for FNSTSW instruction (0xDFE0)
/// </summary>
public class FnstswHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the FnstswHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public FnstswHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
// FNSTSW is a two-byte opcode (0xDF 0xE0)
if (opcode == 0xDF)
{
int position = Decoder.GetPosition();
if (position < Length && CodeBuffer[position] == 0xE0)
{
return true;
}
}
return false;
}
/// <summary>
/// Decodes an FNSTSW instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
int position = Decoder.GetPosition();
if (position >= Length || CodeBuffer[position] != 0xE0)
{
return false;
}
// Skip the second byte of the opcode
Decoder.SetPosition(position + 1);
// Set the mnemonic and operands
instruction.Mnemonic = "fnstsw";
instruction.Operands = "ax";
return true;
}
}

View File

@ -0,0 +1,22 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Interface for instruction handlers
/// </summary>
public interface IInstructionHandler
{
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
bool CanHandle(byte opcode);
/// <summary>
/// Decodes an instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
bool Decode(byte opcode, Instruction instruction);
}

View File

@ -1,9 +1,9 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Base class for all instruction handlers
/// Abstract base class for instruction handlers
/// </summary>
public abstract class InstructionHandler
public abstract class InstructionHandler : IInstructionHandler
{
// Buffer containing the code to decode
protected readonly byte[] CodeBuffer;

View File

@ -0,0 +1,72 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Factory for creating instruction handlers
/// </summary>
public class InstructionHandlerFactory
{
private readonly byte[] _codeBuffer;
private readonly InstructionDecoder _decoder;
private readonly int _length;
private readonly List<IInstructionHandler> _handlers = new List<IInstructionHandler>();
/// <summary>
/// Initializes a new instance of the InstructionHandlerFactory class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this factory</param>
/// <param name="length">The length of the buffer</param>
public InstructionHandlerFactory(byte[] codeBuffer, InstructionDecoder decoder, int length)
{
_codeBuffer = codeBuffer;
_decoder = decoder;
_length = length;
// Register all instruction handlers
RegisterHandlers();
}
/// <summary>
/// Registers all instruction handlers
/// </summary>
private void RegisterHandlers()
{
// Register specific instruction handlers
_handlers.Add(new RetHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new RetImmHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new JmpRel32Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new JmpRel8Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new CallRel32Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new XorRegMemHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new TestRegMemHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new TestAlImmHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new TestEaxImmHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new FnstswHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new ConditionalJumpHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new TwoByteConditionalJumpHandler(_codeBuffer, _decoder, _length));
// Register group handlers for instructions that share similar decoding logic
_handlers.Add(new Group1Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new Group3Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new FloatingPointHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new DataTransferHandler(_codeBuffer, _decoder, _length));
}
/// <summary>
/// Gets a handler that can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to decode</param>
/// <returns>A handler that can decode the opcode, or null if no handler is found</returns>
public IInstructionHandler? GetHandler(byte opcode)
{
foreach (var handler in _handlers)
{
if (handler.CanHandle(opcode))
{
return handler;
}
}
return null;
}
}

View File

@ -0,0 +1,59 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for JMP rel32 instruction (0xE9)
/// </summary>
public class JmpRel32Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the JmpRel32Handler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public JmpRel32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xE9;
}
/// <summary>
/// Decodes a JMP rel32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "jmp";
int position = Decoder.GetPosition();
if (position + 4 > Length)
{
return false;
}
// Read the relative offset
int offset = BitConverter.ToInt32(CodeBuffer, position);
Decoder.SetPosition(position + 4);
// Calculate the target address
uint targetAddress = (uint)(position + offset + 4);
// Set the operands
instruction.Operands = $"0x{targetAddress:X8}";
return true;
}
}

View File

@ -0,0 +1,59 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for JMP rel8 instruction (0xEB)
/// </summary>
public class JmpRel8Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the JmpRel8Handler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public JmpRel8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xEB;
}
/// <summary>
/// Decodes a JMP rel8 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "jmp";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the relative offset
sbyte offset = (sbyte)CodeBuffer[position];
Decoder.SetPosition(position + 1);
// Calculate the target address
uint targetAddress = (uint)(position + offset + 1);
// Set the operands
instruction.Operands = $"0x{targetAddress:X8}";
return true;
}
}

View File

@ -0,0 +1,45 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for RET instruction (0xC3)
/// </summary>
public class RetHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the RetHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public RetHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xC3;
}
/// <summary>
/// Decodes a RET instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "ret";
// No operands for RET
instruction.Operands = string.Empty;
return true;
}
}

View File

@ -0,0 +1,56 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for RET instruction with immediate operand (0xC2)
/// </summary>
public class RetImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the RetImmHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public RetImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xC2;
}
/// <summary>
/// Decodes a RET instruction with immediate operand
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "ret";
int position = Decoder.GetPosition();
if (position + 2 > Length)
{
return false;
}
// Read the immediate value
ushort imm16 = BitConverter.ToUInt16(CodeBuffer, position);
Decoder.SetPosition(position + 2);
// Set the operands
instruction.Operands = $"0x{imm16:X4}";
return true;
}
}

View File

@ -0,0 +1,56 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for TEST AL, imm8 instruction (0xA8)
/// </summary>
public class TestAlImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the TestAlImmHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public TestAlImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xA8;
}
/// <summary>
/// Decodes a TEST AL, imm8 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "test";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the immediate value
byte imm8 = CodeBuffer[position];
Decoder.SetPosition(position + 1);
// Set the operands
instruction.Operands = $"al, 0x{imm8:X2}";
return true;
}
}

View File

@ -0,0 +1,56 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for TEST EAX, imm32 instruction (0xA9)
/// </summary>
public class TestEaxImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the TestEaxImmHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public TestEaxImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0xA9;
}
/// <summary>
/// Decodes a TEST EAX, imm32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "test";
int position = Decoder.GetPosition();
if (position + 4 > Length)
{
return false;
}
// Read the immediate value
uint imm32 = BitConverter.ToUInt32(CodeBuffer, position);
Decoder.SetPosition(position + 4);
// Set the operands
instruction.Operands = $"eax, 0x{imm32:X8}";
return true;
}
}

View File

@ -0,0 +1,82 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for TEST r/m32, r32 instruction (0x85)
/// </summary>
public class TestRegMemHandler : InstructionHandler
{
// ModR/M decoder
private readonly ModRMDecoder _modRMDecoder;
/// <summary>
/// Initializes a new instance of the TestRegMemHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public TestRegMemHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
_modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0x85;
}
/// <summary>
/// Decodes a TEST r/m32, r32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "test";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6);
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Decode the destination operand
string destOperand = _modRMDecoder.DecodeModRM(mod, rm, false);
// Get the source register
string srcReg = GetRegister32(reg);
// Set the operands
instruction.Operands = $"{destOperand}, {srcReg}";
return true;
}
/// <summary>
/// Gets the 32-bit register name for the given register index
/// </summary>
/// <param name="reg">The register index</param>
/// <returns>The register name</returns>
private static string GetRegister32(byte reg)
{
string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
return registerNames[reg & 0x07];
}
}

View File

@ -0,0 +1,88 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for two-byte conditional jump instructions (0x0F 0x80-0x8F)
/// </summary>
public class TwoByteConditionalJumpHandler : InstructionHandler
{
// Mnemonics for conditional jumps
private static readonly string[] ConditionalJumpMnemonics = new string[]
{
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "jnbe",
"js", "jns", "jp", "jnp", "jl", "jnl", "jle", "jnle"
};
/// <summary>
/// Initializes a new instance of the TwoByteConditionalJumpHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public TwoByteConditionalJumpHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
// Two-byte conditional jumps start with 0x0F
if (opcode == 0x0F)
{
int position = Decoder.GetPosition();
if (position < Length)
{
byte secondByte = CodeBuffer[position];
// Second byte must be in the range 0x80-0x8F
return secondByte >= 0x80 && secondByte <= 0x8F;
}
}
return false;
}
/// <summary>
/// Decodes a two-byte conditional jump instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the second byte of the opcode
byte secondByte = CodeBuffer[position++];
Decoder.SetPosition(position);
// Get the mnemonic from the table
int index = secondByte - 0x80;
instruction.Mnemonic = ConditionalJumpMnemonics[index];
if (position + 4 > Length)
{
return false;
}
// Read the relative offset (32-bit)
int offset = BitConverter.ToInt32(CodeBuffer, position);
Decoder.SetPosition(position + 4);
// Calculate the target address
uint targetAddress = (uint)(position + offset + 4);
// Set the operands
instruction.Operands = $"0x{targetAddress:X8}";
return true;
}
}

View File

@ -0,0 +1,56 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for XOR AL, imm8 instruction (0x34)
/// </summary>
public class XorAlImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the XorAlImmHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public XorAlImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0x34;
}
/// <summary>
/// Decodes a XOR AL, imm8 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "xor";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the immediate value
byte imm8 = CodeBuffer[position];
Decoder.SetPosition(position + 1);
// Set the operands
instruction.Operands = $"al, 0x{imm8:X2}";
return true;
}
}

View File

@ -0,0 +1,56 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for XOR EAX, imm32 instruction (0x35)
/// </summary>
public class XorEaxImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the XorEaxImmHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public XorEaxImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0x35;
}
/// <summary>
/// Decodes a XOR EAX, imm32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "xor";
int position = Decoder.GetPosition();
if (position + 4 > Length)
{
return false;
}
// Read the immediate value
uint imm32 = BitConverter.ToUInt32(CodeBuffer, position);
Decoder.SetPosition(position + 4);
// Set the operands
instruction.Operands = $"eax, 0x{imm32:X8}";
return true;
}
}

View File

@ -0,0 +1,82 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for XOR r/m32, r32 instruction (0x31)
/// </summary>
public class XorMemRegHandler : InstructionHandler
{
// ModR/M decoder
private readonly ModRMDecoder _modRMDecoder;
/// <summary>
/// Initializes a new instance of the XorMemRegHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public XorMemRegHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
_modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0x31;
}
/// <summary>
/// Decodes an XOR r/m32, r32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "xor";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6);
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Decode the destination operand
string destOperand = _modRMDecoder.DecodeModRM(mod, rm, false);
// Get the source register
string srcReg = GetRegister32(reg);
// Set the operands
instruction.Operands = $"{destOperand}, {srcReg}";
return true;
}
/// <summary>
/// Gets the 32-bit register name for the given register index
/// </summary>
/// <param name="reg">The register index</param>
/// <returns>The register name</returns>
private static string GetRegister32(byte reg)
{
string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
return registerNames[reg & 0x07];
}
}

View File

@ -0,0 +1,82 @@
namespace X86Disassembler.X86.Handlers;
/// <summary>
/// Handler for XOR r32, r/m32 instruction (0x33)
/// </summary>
public class XorRegMemHandler : InstructionHandler
{
// ModR/M decoder
private readonly ModRMDecoder _modRMDecoder;
/// <summary>
/// Initializes a new instance of the XorRegMemHandler class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="decoder">The instruction decoder that owns this handler</param>
/// <param name="length">The length of the buffer</param>
public XorRegMemHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
: base(codeBuffer, decoder, length)
{
_modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
return opcode == 0x33;
}
/// <summary>
/// Decodes an XOR r32, r/m32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the mnemonic
instruction.Mnemonic = "xor";
int position = Decoder.GetPosition();
if (position >= Length)
{
return false;
}
// Read the ModR/M byte
byte modRM = CodeBuffer[position++];
Decoder.SetPosition(position);
// Extract the fields from the ModR/M byte
byte mod = (byte)((modRM & 0xC0) >> 6);
byte reg = (byte)((modRM & 0x38) >> 3);
byte rm = (byte)(modRM & 0x07);
// Decode the source operand
string srcOperand = _modRMDecoder.DecodeModRM(mod, rm, false);
// Get the destination register
string destReg = GetRegister32(reg);
// Set the operands
instruction.Operands = $"{destReg}, {srcOperand}";
return true;
}
/// <summary>
/// Gets the 32-bit register name for the given register index
/// </summary>
/// <param name="reg">The register index</param>
/// <returns>The register name</returns>
private static string GetRegister32(byte reg)
{
string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
return registerNames[reg & 0x07];
}
}

View File

@ -1,50 +1,56 @@
namespace X86Disassembler.X86;
/// <summary>
/// Represents a decoded x86 instruction
/// Represents an x86 instruction
/// </summary>
public class Instruction
{
/// <summary>
/// The address of the instruction in memory
/// Gets or sets the address of the instruction
/// </summary>
public ulong Address { get; set; }
public uint Address { get; set; }
/// <summary>
/// The raw bytes of the instruction
/// </summary>
public byte[] Bytes { get; set; } = Array.Empty<byte>();
/// <summary>
/// The mnemonic of the instruction (e.g., "mov", "add", "jmp")
/// Gets or sets the mnemonic of the instruction
/// </summary>
public string Mnemonic { get; set; } = string.Empty;
/// <summary>
/// The operands of the instruction as a formatted string
/// Gets or sets the operands of the instruction
/// </summary>
public string Operands { get; set; } = string.Empty;
/// <summary>
/// The length of the instruction in bytes
/// Gets or sets the raw bytes of the instruction
/// </summary>
public int Length => Bytes.Length;
public byte[] RawBytes { get; set; } = Array.Empty<byte>();
/// <summary>
/// Returns a string representation of the instruction
/// </summary>
/// <returns>A formatted string representing the instruction</returns>
/// <returns>A string representation of the instruction</returns>
public override string ToString()
{
return $"{Address:X8} {BytesToString()} {Mnemonic} {Operands}".Trim();
}
// Format the address
string addressStr = $"{Address:X8}";
/// <summary>
/// Converts the instruction bytes to a formatted hex string
/// </summary>
/// <returns>A formatted hex string of the instruction bytes</returns>
private string BytesToString()
{
return string.Join(" ", Bytes.Select(b => b.ToString("X2")));
// Format the raw bytes
string bytesStr = string.Empty;
foreach (byte b in RawBytes)
{
bytesStr += $"{b:X2} ";
}
// Pad the bytes string to a fixed width
bytesStr = bytesStr.PadRight(30);
// Format the instruction
string instructionStr = Mnemonic;
if (!string.IsNullOrEmpty(Operands))
{
instructionStr += " " + Operands;
}
return $" {addressStr} {bytesStr}{instructionStr}";
}
}

View File

@ -3,188 +3,147 @@ namespace X86Disassembler.X86;
using X86Disassembler.X86.Handlers;
/// <summary>
/// Decodes x86 instructions
/// Decodes x86 instructions from a byte buffer
/// </summary>
public class InstructionDecoder
{
// Instruction prefix bytes
private const byte PREFIX_LOCK = 0xF0;
private const byte PREFIX_REPNE = 0xF2;
private const byte PREFIX_REP = 0xF3;
private const byte PREFIX_CS = 0x2E;
private const byte PREFIX_SS = 0x36;
private const byte PREFIX_DS = 0x3E;
private const byte PREFIX_ES = 0x26;
private const byte PREFIX_FS = 0x64;
private const byte PREFIX_GS = 0x65;
private const byte PREFIX_OPERAND_SIZE = 0x66;
private const byte PREFIX_ADDRESS_SIZE = 0x67;
// Buffer containing the code to decode
// The buffer containing the code to decode
private readonly byte[] _codeBuffer;
// Current position in the code buffer
private int _position;
// Length of the buffer
// The length of the buffer
private readonly int _length;
// List of instruction handlers
private readonly List<InstructionHandler> _handlers;
// The current position in the buffer
private int _position;
// The instruction handler factory
private readonly InstructionHandlerFactory _handlerFactory;
// Instruction prefixes
private bool _operandSizePrefix;
private bool _addressSizePrefix;
private bool _segmentOverridePrefix;
private bool _lockPrefix;
private bool _repPrefix;
private string _segmentOverride = string.Empty;
/// <summary>
/// Initializes a new instance of the InstructionDecoder class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
public InstructionDecoder(byte[] codeBuffer)
/// <param name="length">The length of the buffer</param>
public InstructionDecoder(byte[] codeBuffer, int length)
{
_codeBuffer = codeBuffer;
_length = length;
_position = 0;
_length = codeBuffer.Length;
_segmentOverride = string.Empty;
// Initialize the instruction handlers
_handlers = new List<InstructionHandler>
{
new Group1Handler(_codeBuffer, this, _length),
new FloatingPointHandler(_codeBuffer, this, _length),
new DataTransferHandler(_codeBuffer, this, _length),
new ControlFlowHandler(_codeBuffer, this, _length),
new Group3Handler(_codeBuffer, this, _length),
new TestHandler(_codeBuffer, this, _length),
new ArithmeticHandler(_codeBuffer, this, _length)
};
// Create the instruction handler factory
_handlerFactory = new InstructionHandlerFactory(_codeBuffer, this, _length);
}
/// <summary>
/// Decodes an instruction at the current position in the code buffer
/// Decodes an instruction at the current position
/// </summary>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>The number of bytes read</returns>
public int Decode(Instruction instruction)
/// <returns>The decoded instruction, or null if the decoding failed</returns>
public Instruction? DecodeInstruction()
{
// Store the starting position
int startPosition = _position;
// Check if we've reached the end of the buffer
if (_position >= _length)
{
return 0;
return null;
}
// Handle instruction prefixes
bool hasPrefix = true;
bool operandSizePrefix = false;
bool addressSizePrefix = false;
string segmentOverride = string.Empty;
// Reset prefix flags
_operandSizePrefix = false;
_addressSizePrefix = false;
_segmentOverridePrefix = false;
_lockPrefix = false;
_repPrefix = false;
_segmentOverride = string.Empty;
while (hasPrefix && _position < _length)
// Save the start position of the instruction
int startPosition = _position;
// Create a new instruction
Instruction instruction = new Instruction
{
Address = (uint)startPosition,
};
// Handle prefixes
while (_position < _length)
{
byte prefix = _codeBuffer[_position];
switch (prefix)
if (prefix == 0x66) // Operand size prefix
{
case PREFIX_LOCK:
case PREFIX_REPNE:
case PREFIX_REP:
_position++;
break;
case PREFIX_CS:
segmentOverride = "cs";
_position++;
break;
case PREFIX_SS:
segmentOverride = "ss";
_position++;
break;
case PREFIX_DS:
segmentOverride = "ds";
_position++;
break;
case PREFIX_ES:
segmentOverride = "es";
_position++;
break;
case PREFIX_FS:
segmentOverride = "fs";
_position++;
break;
case PREFIX_GS:
segmentOverride = "gs";
_position++;
break;
case PREFIX_OPERAND_SIZE:
operandSizePrefix = true;
_position++;
break;
case PREFIX_ADDRESS_SIZE:
addressSizePrefix = true;
_position++;
break;
default:
hasPrefix = false;
break;
_operandSizePrefix = true;
_position++;
}
else if (prefix == 0x67) // Address size prefix
{
_addressSizePrefix = true;
_position++;
}
else if (prefix >= 0x26 && prefix <= 0x3E && (prefix & 0x7) == 0x6) // Segment override prefix
{
_segmentOverridePrefix = true;
switch (prefix)
{
case 0x26: _segmentOverride = "es"; break;
case 0x2E: _segmentOverride = "cs"; break;
case 0x36: _segmentOverride = "ss"; break;
case 0x3E: _segmentOverride = "ds"; break;
case 0x64: _segmentOverride = "fs"; break;
case 0x65: _segmentOverride = "gs"; break;
}
_position++;
}
else if (prefix == 0xF0) // LOCK prefix
{
_lockPrefix = true;
_position++;
}
else if (prefix == 0xF2 || prefix == 0xF3) // REP/REPNE prefix
{
_repPrefix = true;
_position++;
}
else
{
break;
}
}
// We've reached the end of the buffer after processing prefixes
if (_position >= _length)
{
return _position - startPosition;
return null;
}
// Read the opcode
byte opcode = _codeBuffer[_position++];
// Try to find a handler for this opcode
bool handled = false;
foreach (var handler in _handlers)
{
if (handler.CanHandle(opcode))
{
handled = handler.Decode(opcode, instruction);
if (handled)
{
break;
}
}
}
// Get a handler for the opcode
var handler = _handlerFactory.GetHandler(opcode);
// If no handler was found or the instruction couldn't be decoded,
// use a default mnemonic from the opcode map
if (!handled)
if (handler == null || !handler.Decode(opcode, instruction))
{
// If no handler is found or decoding fails, create a default instruction
instruction.Mnemonic = OpcodeMap.GetMnemonic(opcode);
instruction.Operands = string.Empty;
instruction.Operands = "??";
}
// Copy the instruction bytes
int bytesRead = _position - startPosition;
instruction.Bytes = new byte[bytesRead];
Array.Copy(_codeBuffer, startPosition, instruction.Bytes, 0, bytesRead);
// Set the raw bytes
int length = _position - startPosition;
instruction.RawBytes = new byte[length];
Array.Copy(_codeBuffer, startPosition, instruction.RawBytes, 0, length);
return bytesRead;
return instruction;
}
/// <summary>
/// Sets the current position in the code buffer
/// </summary>
/// <param name="position">The new position</param>
public void SetPosition(int position)
{
_position = position;
}
/// <summary>
/// Gets the current position in the code buffer
/// Gets the current position in the buffer
/// </summary>
/// <returns>The current position</returns>
public int GetPosition()
@ -193,14 +152,111 @@ public class InstructionDecoder
}
/// <summary>
/// Decodes an instruction at the specified position in the code buffer
/// Sets the current position in the buffer
/// </summary>
/// <param name="position">The position in the code buffer</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>The number of bytes read</returns>
public int DecodeAt(int position, Instruction instruction)
/// <param name="position">The new position</param>
public void SetPosition(int position)
{
_position = position;
return Decode(instruction);
}
/// <summary>
/// Checks if the operand size prefix is present
/// </summary>
/// <returns>True if the operand size prefix is present</returns>
public bool HasOperandSizePrefix()
{
return _operandSizePrefix;
}
/// <summary>
/// Checks if the address size prefix is present
/// </summary>
/// <returns>True if the address size prefix is present</returns>
public bool HasAddressSizePrefix()
{
return _addressSizePrefix;
}
/// <summary>
/// Checks if a segment override prefix is present
/// </summary>
/// <returns>True if a segment override prefix is present</returns>
public bool HasSegmentOverridePrefix()
{
return _segmentOverridePrefix;
}
/// <summary>
/// Gets the segment override prefix
/// </summary>
/// <returns>The segment override prefix, or an empty string if none is present</returns>
public string GetSegmentOverride()
{
return _segmentOverride;
}
/// <summary>
/// Checks if the LOCK prefix is present
/// </summary>
/// <returns>True if the LOCK prefix is present</returns>
public bool HasLockPrefix()
{
return _lockPrefix;
}
/// <summary>
/// Checks if the REP/REPNE prefix is present
/// </summary>
/// <returns>True if the REP/REPNE prefix is present</returns>
public bool HasRepPrefix()
{
return _repPrefix;
}
/// <summary>
/// Reads a byte from the buffer and advances the position
/// </summary>
/// <returns>The byte read</returns>
public byte ReadByte()
{
if (_position >= _length)
{
return 0;
}
return _codeBuffer[_position++];
}
/// <summary>
/// Reads a 16-bit value from the buffer and advances the position
/// </summary>
/// <returns>The 16-bit value read</returns>
public ushort ReadUInt16()
{
if (_position + 1 >= _length)
{
return 0;
}
ushort value = BitConverter.ToUInt16(_codeBuffer, _position);
_position += 2;
return value;
}
/// <summary>
/// Reads a 32-bit value from the buffer and advances the position
/// </summary>
/// <returns>The 32-bit value read</returns>
public uint ReadUInt32()
{
if (_position + 3 >= _length)
{
return 0;
}
uint value = BitConverter.ToUInt32(_codeBuffer, _position);
_position += 4;
return value;
}
}