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;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -7,203 +5,138 @@ using System.Collections.Generic;
using X86Disassembler.PE; using X86Disassembler.PE;
using X86Disassembler.X86; using X86Disassembler.X86;
internal class Program namespace X86Disassembler;
/// <summary>
/// Main program class
/// </summary>
public class Program
{ {
// Path to the DLL file to disassemble // Hardcoded file path for testing
private const string DllPath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll"; // Example path, replace with your target DLL private const string FilePath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll";
// Maximum number of instructions to display per section /// <summary>
private const int MaxInstructionsToDisplay = 50; /// Main entry point
/// </summary>
static void Main(string[] args) /// <param name="args">Command line arguments</param>
public static void Main(string[] args)
{ {
Console.WriteLine("X86 Disassembler and Decompiler"); Console.WriteLine("X86 Disassembler and Decompiler");
Console.WriteLine("--------------------------------"); 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 // Print PE file information
{
// 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)
{
Console.WriteLine("PE File Information:"); Console.WriteLine("PE File Information:");
Console.WriteLine($"Architecture: {(peFormat.OptionalHeader.Is64Bit() ? "64-bit" : "32-bit")}"); Console.WriteLine($"Architecture: {(peFormat.OptionalHeader.Is64Bit() ? "64-bit" : "32-bit")}");
Console.WriteLine($"Entry Point: 0x{peFormat.OptionalHeader.AddressOfEntryPoint:X8}"); Console.WriteLine($"Entry Point: 0x{peFormat.OptionalHeader.AddressOfEntryPoint:X8}");
Console.WriteLine($"Image Base: 0x{peFormat.OptionalHeader.ImageBase:X8}"); Console.WriteLine($"Image Base: 0x{peFormat.OptionalHeader.ImageBase:X8}");
Console.WriteLine($"Number of Sections: {peFormat.FileHeader.NumberOfSections}"); Console.WriteLine($"Number of Sections: {peFormat.FileHeader.NumberOfSections}");
Console.WriteLine();
Console.WriteLine("\nSections:"); // Print section information
for (int i = 0; i < peFormat.SectionHeaders.Count; i++) Console.WriteLine("Sections:");
foreach (var section in peFormat.SectionHeaders)
{ {
var section = peFormat.SectionHeaders[i];
string flags = ""; string flags = "";
// Use the section's methods to determine characteristics
if (section.ContainsCode()) flags += "Code "; if (section.ContainsCode()) flags += "Code ";
if (section.IsExecutable()) flags += "Exec "; if (section.IsExecutable()) flags += "Exec ";
if (section.IsReadable()) flags += "Read "; if (section.IsReadable()) flags += "Read ";
if (section.IsWritable()) flags += "Write"; 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) if (peFormat.ExportDirectory != null)
{ {
Console.WriteLine("\nExported Functions:"); Console.WriteLine("Exported Functions:");
Console.WriteLine($"DLL Name: {peFormat.ExportDirectory.DllName}"); Console.WriteLine($"DLL Name: {peFormat.ExportDirectory.DllName}");
Console.WriteLine($"Number of Functions: {peFormat.ExportDirectory.NumberOfFunctions}"); Console.WriteLine($"Number of Functions: {peFormat.ExportDirectory.NumberOfFunctions}");
Console.WriteLine($"Number of Names: {peFormat.ExportDirectory.NumberOfNames}"); Console.WriteLine($"Number of Names: {peFormat.ExportDirectory.NumberOfNames}");
for (int i = 0; i < peFormat.ExportedFunctions.Count; i++) for (int i = 0; i < peFormat.ExportedFunctions.Count; i++)
{ {
var function = peFormat.ExportedFunctions[i]; var export = peFormat.ExportedFunctions[i];
Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.AddressRva:X8})"); 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) if (peFormat.ImportDescriptors.Count > 0)
{ {
Console.WriteLine("\nImported Functions:"); Console.WriteLine("Imported Functions:");
Console.WriteLine($"Number of Imported DLLs: {peFormat.ImportDescriptors.Count}"); 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: {import.DllName}");
Console.WriteLine($" DLL: {descriptor.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) if (function.IsOrdinal)
{ {
Console.WriteLine($" {j}: Ordinal {function.Ordinal}"); Console.WriteLine($" {i}: Ordinal {function.Ordinal}");
} }
else 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 // Find code sections
var codeSections = peFormat.SectionHeaders.FindAll(s => s.ContainsCode()); var codeSections = peFormat.SectionHeaders.FindAll(s => s.ContainsCode());
Console.WriteLine($"Found {codeSections.Count} code section(s):");
Console.WriteLine($"\nFound {codeSections.Count} code section(s):");
foreach (var section in codeSections) foreach (var section in codeSections)
{ {
Console.WriteLine($" - {section.Name}: Size={section.VirtualSize} bytes, RVA=0x{section.VirtualAddress:X8}"); Console.WriteLine($" - {section.Name}: Size={section.VirtualSize} bytes, RVA=0x{section.VirtualAddress:X8}");
} }
Console.WriteLine(); Console.WriteLine();
// Disassemble each code section // Disassemble the first code section
for (int i = 0; i < peFormat.SectionHeaders.Count; i++) if (codeSections.Count > 0)
{ {
var section = peFormat.SectionHeaders[i]; var section = codeSections[0];
byte[] codeBytes = peFormat.GetSectionData(peFormat.SectionHeaders.IndexOf(section));
// Skip non-code sections
if (!section.ContainsCode())
continue;
Console.WriteLine($"Disassembling section {section.Name} at RVA 0x{section.VirtualAddress:X8}:"); Console.WriteLine($"Disassembling section {section.Name} at RVA 0x{section.VirtualAddress:X8}:");
// Get section data using the section index // Create a disassembler for the code section
byte[] sectionData = peFormat.GetSectionData(i); Disassembler disassembler = new Disassembler(codeBytes, section.VirtualAddress);
// Create a disassembler for this section // Disassemble all instructions
ulong baseAddress = peFormat.OptionalHeader.ImageBase + section.VirtualAddress; var instructions = disassembler.Disassemble();
Disassembler disassembler = new Disassembler(sectionData, baseAddress);
// Disassemble and display instructions // Print the first 100 instructions
int count = 0; int count = Math.Min(100, instructions.Count);
int maxInstructions = MaxInstructionsToDisplay; // Use the constant for (int i = 0; i < count; i++)
while (count < maxInstructions)
{ {
Instruction? instruction = disassembler.DisassembleNext(); Console.WriteLine(instructions[i]);
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++;
} }
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.Text;
using System.Collections.Generic;
namespace X86Disassembler.X86; namespace X86Disassembler.X86;
@ -7,77 +8,62 @@ namespace X86Disassembler.X86;
/// </summary> /// </summary>
public class Disassembler public class Disassembler
{ {
// Buffer containing the code to disassemble // The buffer containing the code to disassemble
private readonly byte[] _codeBuffer; private readonly byte[] _codeBuffer;
// Base address for the code (RVA) // The length of the buffer
private readonly ulong _baseAddress; private readonly int _length;
// Current position in the code buffer // The base address of the code
private int _position; private readonly uint _baseAddress;
// Instruction decoder
private readonly InstructionDecoder _decoder;
/// <summary> /// <summary>
/// Initializes a new instance of the Disassembler class /// Initializes a new instance of the Disassembler class
/// </summary> /// </summary>
/// <param name="codeBuffer">The buffer containing the code to disassemble</param> /// <param name="codeBuffer">The buffer containing the code to disassemble</param>
/// <param name="baseAddress">The base address (RVA) of the code</param> /// <param name="baseAddress">The base address of the code</param>
public Disassembler(byte[] codeBuffer, ulong baseAddress) public Disassembler(byte[] codeBuffer, uint baseAddress)
{ {
_codeBuffer = codeBuffer; _codeBuffer = codeBuffer;
_length = codeBuffer.Length;
_baseAddress = baseAddress; _baseAddress = baseAddress;
_position = 0;
_decoder = new InstructionDecoder(codeBuffer);
} }
/// <summary> /// <summary>
/// Disassembles the next instruction in the code buffer /// Disassembles the code buffer and returns the disassembled instructions
/// </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
/// </summary> /// </summary>
/// <returns>A list of disassembled instructions</returns> /// <returns>A list of disassembled instructions</returns>
public List<Instruction> DisassembleAll() public List<Instruction> Disassemble()
{ {
List<Instruction> instructions = new List<Instruction>(); List<Instruction> instructions = new List<Instruction>();
// Reset position // Create an instruction decoder
_position = 0; InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
// Disassemble all instructions // Decode instructions until the end of the buffer is reached
Instruction? instruction; while (true)
while ((instruction = DisassembleNext()) != null)
{ {
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); 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; namespace X86Disassembler.X86.Handlers;
/// <summary> /// <summary>
/// Base class for all instruction handlers /// Abstract base class for instruction handlers
/// </summary> /// </summary>
public abstract class InstructionHandler public abstract class InstructionHandler : IInstructionHandler
{ {
// Buffer containing the code to decode // Buffer containing the code to decode
protected readonly byte[] CodeBuffer; 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; namespace X86Disassembler.X86;
/// <summary> /// <summary>
/// Represents a decoded x86 instruction /// Represents an x86 instruction
/// </summary> /// </summary>
public class Instruction public class Instruction
{ {
/// <summary> /// <summary>
/// The address of the instruction in memory /// Gets or sets the address of the instruction
/// </summary> /// </summary>
public ulong Address { get; set; } public uint Address { get; set; }
/// <summary> /// <summary>
/// The raw bytes of the instruction /// Gets or sets the mnemonic of the instruction
/// </summary>
public byte[] Bytes { get; set; } = Array.Empty<byte>();
/// <summary>
/// The mnemonic of the instruction (e.g., "mov", "add", "jmp")
/// </summary> /// </summary>
public string Mnemonic { get; set; } = string.Empty; public string Mnemonic { get; set; } = string.Empty;
/// <summary> /// <summary>
/// The operands of the instruction as a formatted string /// Gets or sets the operands of the instruction
/// </summary> /// </summary>
public string Operands { get; set; } = string.Empty; public string Operands { get; set; } = string.Empty;
/// <summary> /// <summary>
/// The length of the instruction in bytes /// Gets or sets the raw bytes of the instruction
/// </summary> /// </summary>
public int Length => Bytes.Length; public byte[] RawBytes { get; set; } = Array.Empty<byte>();
/// <summary> /// <summary>
/// Returns a string representation of the instruction /// Returns a string representation of the instruction
/// </summary> /// </summary>
/// <returns>A formatted string representing the instruction</returns> /// <returns>A string representation of the instruction</returns>
public override string ToString() public override string ToString()
{ {
return $"{Address:X8} {BytesToString()} {Mnemonic} {Operands}".Trim(); // Format the address
} string addressStr = $"{Address:X8}";
/// <summary> // Format the raw bytes
/// Converts the instruction bytes to a formatted hex string string bytesStr = string.Empty;
/// </summary> foreach (byte b in RawBytes)
/// <returns>A formatted hex string of the instruction bytes</returns> {
private string BytesToString() bytesStr += $"{b:X2} ";
{ }
return string.Join(" ", Bytes.Select(b => b.ToString("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; using X86Disassembler.X86.Handlers;
/// <summary> /// <summary>
/// Decodes x86 instructions /// Decodes x86 instructions from a byte buffer
/// </summary> /// </summary>
public class InstructionDecoder public class InstructionDecoder
{ {
// Instruction prefix bytes // The buffer containing the code to decode
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
private readonly byte[] _codeBuffer; private readonly byte[] _codeBuffer;
// Current position in the code buffer // The length of the buffer
private int _position;
// Length of the buffer
private readonly int _length; private readonly int _length;
// List of instruction handlers // The current position in the buffer
private readonly List<InstructionHandler> _handlers; 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> /// <summary>
/// Initializes a new instance of the InstructionDecoder class /// Initializes a new instance of the InstructionDecoder class
/// </summary> /// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param> /// <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; _codeBuffer = codeBuffer;
_length = length;
_position = 0; _position = 0;
_length = codeBuffer.Length; _segmentOverride = string.Empty;
// Initialize the instruction handlers // Create the instruction handler factory
_handlers = new List<InstructionHandler> _handlerFactory = new InstructionHandlerFactory(_codeBuffer, this, _length);
{
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)
};
} }
/// <summary> /// <summary>
/// Decodes an instruction at the current position in the code buffer /// Decodes an instruction at the current position
/// </summary> /// </summary>
/// <param name="instruction">The instruction object to populate</param> /// <returns>The decoded instruction, or null if the decoding failed</returns>
/// <returns>The number of bytes read</returns> public Instruction? DecodeInstruction()
public int Decode(Instruction instruction)
{ {
// Store the starting position
int startPosition = _position;
// Check if we've reached the end of the buffer
if (_position >= _length) if (_position >= _length)
{ {
return 0; return null;
} }
// Handle instruction prefixes // Reset prefix flags
bool hasPrefix = true; _operandSizePrefix = false;
bool operandSizePrefix = false; _addressSizePrefix = false;
bool addressSizePrefix = false; _segmentOverridePrefix = false;
string segmentOverride = string.Empty; _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]; byte prefix = _codeBuffer[_position];
switch (prefix) if (prefix == 0x66) // Operand size prefix
{ {
case PREFIX_LOCK: _operandSizePrefix = true;
case PREFIX_REPNE: _position++;
case PREFIX_REP: }
_position++; else if (prefix == 0x67) // Address size prefix
break; {
_addressSizePrefix = true;
case PREFIX_CS: _position++;
segmentOverride = "cs"; }
_position++; else if (prefix >= 0x26 && prefix <= 0x3E && (prefix & 0x7) == 0x6) // Segment override prefix
break; {
_segmentOverridePrefix = true;
case PREFIX_SS: switch (prefix)
segmentOverride = "ss"; {
_position++; case 0x26: _segmentOverride = "es"; break;
break; case 0x2E: _segmentOverride = "cs"; break;
case 0x36: _segmentOverride = "ss"; break;
case PREFIX_DS: case 0x3E: _segmentOverride = "ds"; break;
segmentOverride = "ds"; case 0x64: _segmentOverride = "fs"; break;
_position++; case 0x65: _segmentOverride = "gs"; break;
break; }
_position++;
case PREFIX_ES: }
segmentOverride = "es"; else if (prefix == 0xF0) // LOCK prefix
_position++; {
break; _lockPrefix = true;
_position++;
case PREFIX_FS: }
segmentOverride = "fs"; else if (prefix == 0xF2 || prefix == 0xF3) // REP/REPNE prefix
_position++; {
break; _repPrefix = true;
_position++;
case PREFIX_GS: }
segmentOverride = "gs"; else
_position++; {
break; break;
case PREFIX_OPERAND_SIZE:
operandSizePrefix = true;
_position++;
break;
case PREFIX_ADDRESS_SIZE:
addressSizePrefix = true;
_position++;
break;
default:
hasPrefix = false;
break;
} }
} }
// We've reached the end of the buffer after processing prefixes
if (_position >= _length) if (_position >= _length)
{ {
return _position - startPosition; return null;
} }
// Read the opcode // Read the opcode
byte opcode = _codeBuffer[_position++]; byte opcode = _codeBuffer[_position++];
// Try to find a handler for this opcode // Get a handler for the opcode
bool handled = false; var handler = _handlerFactory.GetHandler(opcode);
foreach (var handler in _handlers)
{
if (handler.CanHandle(opcode))
{
handled = handler.Decode(opcode, instruction);
if (handled)
{
break;
}
}
}
// If no handler was found or the instruction couldn't be decoded, if (handler == null || !handler.Decode(opcode, instruction))
// use a default mnemonic from the opcode map
if (!handled)
{ {
// If no handler is found or decoding fails, create a default instruction
instruction.Mnemonic = OpcodeMap.GetMnemonic(opcode); instruction.Mnemonic = OpcodeMap.GetMnemonic(opcode);
instruction.Operands = string.Empty; instruction.Operands = "??";
} }
// Copy the instruction bytes // Set the raw bytes
int bytesRead = _position - startPosition; int length = _position - startPosition;
instruction.Bytes = new byte[bytesRead]; instruction.RawBytes = new byte[length];
Array.Copy(_codeBuffer, startPosition, instruction.Bytes, 0, bytesRead); Array.Copy(_codeBuffer, startPosition, instruction.RawBytes, 0, length);
return bytesRead; return instruction;
} }
/// <summary> /// <summary>
/// Sets the current position in the code buffer /// Gets the current position in the 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
/// </summary> /// </summary>
/// <returns>The current position</returns> /// <returns>The current position</returns>
public int GetPosition() public int GetPosition()
@ -193,14 +152,111 @@ public class InstructionDecoder
} }
/// <summary> /// <summary>
/// Decodes an instruction at the specified position in the code buffer /// Sets the current position in the buffer
/// </summary> /// </summary>
/// <param name="position">The position in the code buffer</param> /// <param name="position">The new position</param>
/// <param name="instruction">The instruction object to populate</param> public void SetPosition(int position)
/// <returns>The number of bytes read</returns>
public int DecodeAt(int position, Instruction instruction)
{ {
_position = 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;
} }
} }