diff --git a/X86Disassembler/Program.cs b/X86Disassembler/Program.cs
index 95e75e3..fdb8ec0 100644
--- a/X86Disassembler/Program.cs
+++ b/X86Disassembler/Program.cs
@@ -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;
+
+///
+/// Main program class
+///
+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)
+ ///
+ /// Main entry point
+ ///
+ /// Command line arguments
+ 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);
- }
- }
-
- ///
- /// Displays information about the PE file
- ///
- /// The PE format object
- 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();
}
- }
-
- ///
- /// Disassembles the code sections of the PE file
- ///
- /// The PE format object
- 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];
+ 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}:");
- // 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();
}
}
\ No newline at end of file
diff --git a/X86Disassembler/X86/Disassembler.cs b/X86Disassembler/X86/Disassembler.cs
index 5cf3c3f..70c0917 100644
--- a/X86Disassembler/X86/Disassembler.cs
+++ b/X86Disassembler/X86/Disassembler.cs
@@ -1,4 +1,5 @@
using System.Text;
+using System.Collections.Generic;
namespace X86Disassembler.X86;
@@ -7,77 +8,62 @@ namespace X86Disassembler.X86;
///
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;
///
/// Initializes a new instance of the Disassembler class
///
/// The buffer containing the code to disassemble
- /// The base address (RVA) of the code
- public Disassembler(byte[] codeBuffer, ulong baseAddress)
+ /// The base address of the code
+ public Disassembler(byte[] codeBuffer, uint baseAddress)
{
_codeBuffer = codeBuffer;
+ _length = codeBuffer.Length;
_baseAddress = baseAddress;
- _position = 0;
- _decoder = new InstructionDecoder(codeBuffer);
}
///
- /// Disassembles the next instruction in the code buffer
- ///
- /// The disassembled instruction, or null if the end of the buffer is reached
- 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;
- }
-
- ///
- /// Disassembles all instructions in the code buffer
+ /// Disassembles the code buffer and returns the disassembled instructions
///
/// A list of disassembled instructions
- public List DisassembleAll()
+ public List Disassemble()
{
List instructions = new List();
- // 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);
}
diff --git a/X86Disassembler/X86/Handlers/CallRel32Handler.cs b/X86Disassembler/X86/Handlers/CallRel32Handler.cs
new file mode 100644
index 0000000..04950f6
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/CallRel32Handler.cs
@@ -0,0 +1,59 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for CALL rel32 instruction (0xE8)
+///
+public class CallRel32Handler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the CallRel32Handler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public CallRel32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xE8;
+ }
+
+ ///
+ /// Decodes a CALL rel32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/ConditionalJumpHandler.cs b/X86Disassembler/X86/Handlers/ConditionalJumpHandler.cs
new file mode 100644
index 0000000..ed433f0
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/ConditionalJumpHandler.cs
@@ -0,0 +1,68 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for conditional jump instructions (0x70-0x7F)
+///
+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"
+ };
+
+ ///
+ /// Initializes a new instance of the ConditionalJumpHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public ConditionalJumpHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ // Conditional jumps are in the range 0x70-0x7F
+ return opcode >= 0x70 && opcode <= 0x7F;
+ }
+
+ ///
+ /// Decodes a conditional jump instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/FnstswHandler.cs b/X86Disassembler/X86/Handlers/FnstswHandler.cs
new file mode 100644
index 0000000..635b75d
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/FnstswHandler.cs
@@ -0,0 +1,63 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for FNSTSW instruction (0xDFE0)
+///
+public class FnstswHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the FnstswHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public FnstswHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ 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;
+ }
+
+ ///
+ /// Decodes an FNSTSW instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/IInstructionHandler.cs b/X86Disassembler/X86/Handlers/IInstructionHandler.cs
new file mode 100644
index 0000000..5ce3628
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/IInstructionHandler.cs
@@ -0,0 +1,22 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Interface for instruction handlers
+///
+public interface IInstructionHandler
+{
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ bool CanHandle(byte opcode);
+
+ ///
+ /// Decodes an instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ bool Decode(byte opcode, Instruction instruction);
+}
diff --git a/X86Disassembler/X86/Handlers/InstructionHandler.cs b/X86Disassembler/X86/Handlers/InstructionHandler.cs
index 20604c3..b79b8ab 100644
--- a/X86Disassembler/X86/Handlers/InstructionHandler.cs
+++ b/X86Disassembler/X86/Handlers/InstructionHandler.cs
@@ -1,9 +1,9 @@
namespace X86Disassembler.X86.Handlers;
///
-/// Base class for all instruction handlers
+/// Abstract base class for instruction handlers
///
-public abstract class InstructionHandler
+public abstract class InstructionHandler : IInstructionHandler
{
// Buffer containing the code to decode
protected readonly byte[] CodeBuffer;
diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs
new file mode 100644
index 0000000..7608a84
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs
@@ -0,0 +1,72 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Factory for creating instruction handlers
+///
+public class InstructionHandlerFactory
+{
+ private readonly byte[] _codeBuffer;
+ private readonly InstructionDecoder _decoder;
+ private readonly int _length;
+ private readonly List _handlers = new List();
+
+ ///
+ /// Initializes a new instance of the InstructionHandlerFactory class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this factory
+ /// The length of the buffer
+ public InstructionHandlerFactory(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ {
+ _codeBuffer = codeBuffer;
+ _decoder = decoder;
+ _length = length;
+
+ // Register all instruction handlers
+ RegisterHandlers();
+ }
+
+ ///
+ /// Registers all instruction handlers
+ ///
+ 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));
+ }
+
+ ///
+ /// Gets a handler that can decode the given opcode
+ ///
+ /// The opcode to decode
+ /// A handler that can decode the opcode, or null if no handler is found
+ public IInstructionHandler? GetHandler(byte opcode)
+ {
+ foreach (var handler in _handlers)
+ {
+ if (handler.CanHandle(opcode))
+ {
+ return handler;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/JmpRel32Handler.cs b/X86Disassembler/X86/Handlers/JmpRel32Handler.cs
new file mode 100644
index 0000000..b41c251
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/JmpRel32Handler.cs
@@ -0,0 +1,59 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for JMP rel32 instruction (0xE9)
+///
+public class JmpRel32Handler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the JmpRel32Handler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public JmpRel32Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xE9;
+ }
+
+ ///
+ /// Decodes a JMP rel32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/JmpRel8Handler.cs b/X86Disassembler/X86/Handlers/JmpRel8Handler.cs
new file mode 100644
index 0000000..66f51f6
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/JmpRel8Handler.cs
@@ -0,0 +1,59 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for JMP rel8 instruction (0xEB)
+///
+public class JmpRel8Handler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the JmpRel8Handler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public JmpRel8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xEB;
+ }
+
+ ///
+ /// Decodes a JMP rel8 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/RetHandler.cs b/X86Disassembler/X86/Handlers/RetHandler.cs
new file mode 100644
index 0000000..76769ca
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/RetHandler.cs
@@ -0,0 +1,45 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for RET instruction (0xC3)
+///
+public class RetHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the RetHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public RetHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xC3;
+ }
+
+ ///
+ /// Decodes a RET instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ public override bool Decode(byte opcode, Instruction instruction)
+ {
+ // Set the mnemonic
+ instruction.Mnemonic = "ret";
+
+ // No operands for RET
+ instruction.Operands = string.Empty;
+
+ return true;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/RetImmHandler.cs b/X86Disassembler/X86/Handlers/RetImmHandler.cs
new file mode 100644
index 0000000..d2c6a31
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/RetImmHandler.cs
@@ -0,0 +1,56 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for RET instruction with immediate operand (0xC2)
+///
+public class RetImmHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the RetImmHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public RetImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xC2;
+ }
+
+ ///
+ /// Decodes a RET instruction with immediate operand
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/TestAlImmHandler.cs b/X86Disassembler/X86/Handlers/TestAlImmHandler.cs
new file mode 100644
index 0000000..2ea44cd
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/TestAlImmHandler.cs
@@ -0,0 +1,56 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for TEST AL, imm8 instruction (0xA8)
+///
+public class TestAlImmHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the TestAlImmHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public TestAlImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xA8;
+ }
+
+ ///
+ /// Decodes a TEST AL, imm8 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/TestEaxImmHandler.cs b/X86Disassembler/X86/Handlers/TestEaxImmHandler.cs
new file mode 100644
index 0000000..615bb81
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/TestEaxImmHandler.cs
@@ -0,0 +1,56 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for TEST EAX, imm32 instruction (0xA9)
+///
+public class TestEaxImmHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the TestEaxImmHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public TestEaxImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0xA9;
+ }
+
+ ///
+ /// Decodes a TEST EAX, imm32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/TestRegMemHandler.cs b/X86Disassembler/X86/Handlers/TestRegMemHandler.cs
new file mode 100644
index 0000000..96c9eee
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/TestRegMemHandler.cs
@@ -0,0 +1,82 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for TEST r/m32, r32 instruction (0x85)
+///
+public class TestRegMemHandler : InstructionHandler
+{
+ // ModR/M decoder
+ private readonly ModRMDecoder _modRMDecoder;
+
+ ///
+ /// Initializes a new instance of the TestRegMemHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public TestRegMemHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ _modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0x85;
+ }
+
+ ///
+ /// Decodes a TEST r/m32, r32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+
+ ///
+ /// Gets the 32-bit register name for the given register index
+ ///
+ /// The register index
+ /// The register name
+ private static string GetRegister32(byte reg)
+ {
+ string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
+ return registerNames[reg & 0x07];
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/TwoByteConditionalJumpHandler.cs b/X86Disassembler/X86/Handlers/TwoByteConditionalJumpHandler.cs
new file mode 100644
index 0000000..9b59ca5
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/TwoByteConditionalJumpHandler.cs
@@ -0,0 +1,88 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for two-byte conditional jump instructions (0x0F 0x80-0x8F)
+///
+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"
+ };
+
+ ///
+ /// Initializes a new instance of the TwoByteConditionalJumpHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public TwoByteConditionalJumpHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ 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;
+ }
+
+ ///
+ /// Decodes a two-byte conditional jump instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/XorAlImmHandler.cs b/X86Disassembler/X86/Handlers/XorAlImmHandler.cs
new file mode 100644
index 0000000..ba46ee6
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/XorAlImmHandler.cs
@@ -0,0 +1,56 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for XOR AL, imm8 instruction (0x34)
+///
+public class XorAlImmHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the XorAlImmHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public XorAlImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0x34;
+ }
+
+ ///
+ /// Decodes a XOR AL, imm8 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/XorEaxImmHandler.cs b/X86Disassembler/X86/Handlers/XorEaxImmHandler.cs
new file mode 100644
index 0000000..78fcae1
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/XorEaxImmHandler.cs
@@ -0,0 +1,56 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for XOR EAX, imm32 instruction (0x35)
+///
+public class XorEaxImmHandler : InstructionHandler
+{
+ ///
+ /// Initializes a new instance of the XorEaxImmHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public XorEaxImmHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0x35;
+ }
+
+ ///
+ /// Decodes a XOR EAX, imm32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/XorMemRegHandler.cs b/X86Disassembler/X86/Handlers/XorMemRegHandler.cs
new file mode 100644
index 0000000..acefa7c
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/XorMemRegHandler.cs
@@ -0,0 +1,82 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for XOR r/m32, r32 instruction (0x31)
+///
+public class XorMemRegHandler : InstructionHandler
+{
+ // ModR/M decoder
+ private readonly ModRMDecoder _modRMDecoder;
+
+ ///
+ /// Initializes a new instance of the XorMemRegHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public XorMemRegHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ _modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0x31;
+ }
+
+ ///
+ /// Decodes an XOR r/m32, r32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+
+ ///
+ /// Gets the 32-bit register name for the given register index
+ ///
+ /// The register index
+ /// The register name
+ private static string GetRegister32(byte reg)
+ {
+ string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
+ return registerNames[reg & 0x07];
+ }
+}
diff --git a/X86Disassembler/X86/Handlers/XorRegMemHandler.cs b/X86Disassembler/X86/Handlers/XorRegMemHandler.cs
new file mode 100644
index 0000000..21bf4c1
--- /dev/null
+++ b/X86Disassembler/X86/Handlers/XorRegMemHandler.cs
@@ -0,0 +1,82 @@
+namespace X86Disassembler.X86.Handlers;
+
+///
+/// Handler for XOR r32, r/m32 instruction (0x33)
+///
+public class XorRegMemHandler : InstructionHandler
+{
+ // ModR/M decoder
+ private readonly ModRMDecoder _modRMDecoder;
+
+ ///
+ /// Initializes a new instance of the XorRegMemHandler class
+ ///
+ /// The buffer containing the code to decode
+ /// The instruction decoder that owns this handler
+ /// The length of the buffer
+ public XorRegMemHandler(byte[] codeBuffer, InstructionDecoder decoder, int length)
+ : base(codeBuffer, decoder, length)
+ {
+ _modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length);
+ }
+
+ ///
+ /// Checks if this handler can decode the given opcode
+ ///
+ /// The opcode to check
+ /// True if this handler can decode the opcode
+ public override bool CanHandle(byte opcode)
+ {
+ return opcode == 0x33;
+ }
+
+ ///
+ /// Decodes an XOR r32, r/m32 instruction
+ ///
+ /// The opcode of the instruction
+ /// The instruction object to populate
+ /// True if the instruction was successfully decoded
+ 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;
+ }
+
+ ///
+ /// Gets the 32-bit register name for the given register index
+ ///
+ /// The register index
+ /// The register name
+ private static string GetRegister32(byte reg)
+ {
+ string[] registerNames = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
+ return registerNames[reg & 0x07];
+ }
+}
diff --git a/X86Disassembler/X86/Instruction.cs b/X86Disassembler/X86/Instruction.cs
index c744b0d..28c8253 100644
--- a/X86Disassembler/X86/Instruction.cs
+++ b/X86Disassembler/X86/Instruction.cs
@@ -1,50 +1,56 @@
namespace X86Disassembler.X86;
///
-/// Represents a decoded x86 instruction
+/// Represents an x86 instruction
///
public class Instruction
{
///
- /// The address of the instruction in memory
+ /// Gets or sets the address of the instruction
///
- public ulong Address { get; set; }
+ public uint Address { get; set; }
///
- /// The raw bytes of the instruction
- ///
- public byte[] Bytes { get; set; } = Array.Empty();
-
- ///
- /// The mnemonic of the instruction (e.g., "mov", "add", "jmp")
+ /// Gets or sets the mnemonic of the instruction
///
public string Mnemonic { get; set; } = string.Empty;
///
- /// The operands of the instruction as a formatted string
+ /// Gets or sets the operands of the instruction
///
public string Operands { get; set; } = string.Empty;
///
- /// The length of the instruction in bytes
+ /// Gets or sets the raw bytes of the instruction
///
- public int Length => Bytes.Length;
+ public byte[] RawBytes { get; set; } = Array.Empty();
///
/// Returns a string representation of the instruction
///
- /// A formatted string representing the instruction
+ /// A string representation of the instruction
public override string ToString()
{
- return $"{Address:X8} {BytesToString()} {Mnemonic} {Operands}".Trim();
- }
-
- ///
- /// Converts the instruction bytes to a formatted hex string
- ///
- /// A formatted hex string of the instruction bytes
- private string BytesToString()
- {
- return string.Join(" ", Bytes.Select(b => b.ToString("X2")));
+ // Format the address
+ string addressStr = $"{Address:X8}";
+
+ // 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}";
}
}
diff --git a/X86Disassembler/X86/InstructionDecoder.cs b/X86Disassembler/X86/InstructionDecoder.cs
index e91a948..5cba892 100644
--- a/X86Disassembler/X86/InstructionDecoder.cs
+++ b/X86Disassembler/X86/InstructionDecoder.cs
@@ -3,188 +3,147 @@ namespace X86Disassembler.X86;
using X86Disassembler.X86.Handlers;
///
-/// Decodes x86 instructions
+/// Decodes x86 instructions from a byte buffer
///
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 _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;
///
/// Initializes a new instance of the InstructionDecoder class
///
/// The buffer containing the code to decode
- public InstructionDecoder(byte[] codeBuffer)
+ /// The length of the buffer
+ 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
- {
- 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);
}
///
- /// Decodes an instruction at the current position in the code buffer
+ /// Decodes an instruction at the current position
///
- /// The instruction object to populate
- /// The number of bytes read
- public int Decode(Instruction instruction)
+ /// The decoded instruction, or null if the decoding failed
+ 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;
}
///
- /// Sets the current position in the code buffer
- ///
- /// The new position
- public void SetPosition(int position)
- {
- _position = position;
- }
-
- ///
- /// Gets the current position in the code buffer
+ /// Gets the current position in the buffer
///
/// The current position
public int GetPosition()
@@ -193,14 +152,111 @@ public class InstructionDecoder
}
///
- /// Decodes an instruction at the specified position in the code buffer
+ /// Sets the current position in the buffer
///
- /// The position in the code buffer
- /// The instruction object to populate
- /// The number of bytes read
- public int DecodeAt(int position, Instruction instruction)
+ /// The new position
+ public void SetPosition(int position)
{
_position = position;
- return Decode(instruction);
+ }
+
+ ///
+ /// Checks if the operand size prefix is present
+ ///
+ /// True if the operand size prefix is present
+ public bool HasOperandSizePrefix()
+ {
+ return _operandSizePrefix;
+ }
+
+ ///
+ /// Checks if the address size prefix is present
+ ///
+ /// True if the address size prefix is present
+ public bool HasAddressSizePrefix()
+ {
+ return _addressSizePrefix;
+ }
+
+ ///
+ /// Checks if a segment override prefix is present
+ ///
+ /// True if a segment override prefix is present
+ public bool HasSegmentOverridePrefix()
+ {
+ return _segmentOverridePrefix;
+ }
+
+ ///
+ /// Gets the segment override prefix
+ ///
+ /// The segment override prefix, or an empty string if none is present
+ public string GetSegmentOverride()
+ {
+ return _segmentOverride;
+ }
+
+ ///
+ /// Checks if the LOCK prefix is present
+ ///
+ /// True if the LOCK prefix is present
+ public bool HasLockPrefix()
+ {
+ return _lockPrefix;
+ }
+
+ ///
+ /// Checks if the REP/REPNE prefix is present
+ ///
+ /// True if the REP/REPNE prefix is present
+ public bool HasRepPrefix()
+ {
+ return _repPrefix;
+ }
+
+ ///
+ /// Reads a byte from the buffer and advances the position
+ ///
+ /// The byte read
+ public byte ReadByte()
+ {
+ if (_position >= _length)
+ {
+ return 0;
+ }
+
+ return _codeBuffer[_position++];
+ }
+
+ ///
+ /// Reads a 16-bit value from the buffer and advances the position
+ ///
+ /// The 16-bit value read
+ public ushort ReadUInt16()
+ {
+ if (_position + 1 >= _length)
+ {
+ return 0;
+ }
+
+ ushort value = BitConverter.ToUInt16(_codeBuffer, _position);
+ _position += 2;
+ return value;
+ }
+
+ ///
+ /// Reads a 32-bit value from the buffer and advances the position
+ ///
+ /// The 32-bit value read
+ public uint ReadUInt32()
+ {
+ if (_position + 3 >= _length)
+ {
+ return 0;
+ }
+
+ uint value = BitConverter.ToUInt32(_codeBuffer, _position);
+ _position += 4;
+ return value;
}
}