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:
parent
82ffd51a3e
commit
58a148ebd8
@ -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}");
|
||||||
Console.WriteLine($"Loading file: {filePath}");
|
byte[] fileBytes = File.ReadAllBytes(FilePath);
|
||||||
|
Console.WriteLine($"Successfully loaded {FilePath}");
|
||||||
try
|
Console.WriteLine($"File size: {fileBytes.Length} bytes\n");
|
||||||
{
|
|
||||||
// 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
|
// Parse the PE format
|
||||||
|
Console.WriteLine("Parsing PE format...\n");
|
||||||
PEFormat peFormat = new PEFormat(fileBytes);
|
PEFormat peFormat = new PEFormat(fileBytes);
|
||||||
if (!peFormat.Parse())
|
peFormat.Parse();
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to parse PE file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display PE information
|
// Print PE file 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})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
if (i < peFormat.ImportDescriptors.Count - 1)
|
|
||||||
{
|
|
||||||
Console.WriteLine(); // Add a blank line between DLLs for better readability
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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)
|
}
|
||||||
|
|
||||||
|
// Print a summary of how many more instructions there are
|
||||||
|
if (instructions.Count > count)
|
||||||
{
|
{
|
||||||
break;
|
Console.WriteLine($"... ({instructions.Count - count} more bytes not shown)");
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
Console.WriteLine($" ... ({sectionData.Length - (count * 10)} more bytes not shown)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("\nPress Enter to exit...");
|
||||||
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
X86Disassembler/X86/Handlers/CallRel32Handler.cs
Normal file
59
X86Disassembler/X86/Handlers/CallRel32Handler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
68
X86Disassembler/X86/Handlers/ConditionalJumpHandler.cs
Normal file
68
X86Disassembler/X86/Handlers/ConditionalJumpHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
63
X86Disassembler/X86/Handlers/FnstswHandler.cs
Normal file
63
X86Disassembler/X86/Handlers/FnstswHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
22
X86Disassembler/X86/Handlers/IInstructionHandler.cs
Normal file
22
X86Disassembler/X86/Handlers/IInstructionHandler.cs
Normal 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);
|
||||||
|
}
|
@ -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;
|
||||||
|
72
X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs
Normal file
72
X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
59
X86Disassembler/X86/Handlers/JmpRel32Handler.cs
Normal file
59
X86Disassembler/X86/Handlers/JmpRel32Handler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
59
X86Disassembler/X86/Handlers/JmpRel8Handler.cs
Normal file
59
X86Disassembler/X86/Handlers/JmpRel8Handler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
45
X86Disassembler/X86/Handlers/RetHandler.cs
Normal file
45
X86Disassembler/X86/Handlers/RetHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/X86/Handlers/RetImmHandler.cs
Normal file
56
X86Disassembler/X86/Handlers/RetImmHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/X86/Handlers/TestAlImmHandler.cs
Normal file
56
X86Disassembler/X86/Handlers/TestAlImmHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/X86/Handlers/TestEaxImmHandler.cs
Normal file
56
X86Disassembler/X86/Handlers/TestEaxImmHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
82
X86Disassembler/X86/Handlers/TestRegMemHandler.cs
Normal file
82
X86Disassembler/X86/Handlers/TestRegMemHandler.cs
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/X86/Handlers/XorAlImmHandler.cs
Normal file
56
X86Disassembler/X86/Handlers/XorAlImmHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/X86/Handlers/XorEaxImmHandler.cs
Normal file
56
X86Disassembler/X86/Handlers/XorEaxImmHandler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
82
X86Disassembler/X86/Handlers/XorMemRegHandler.cs
Normal file
82
X86Disassembler/X86/Handlers/XorMemRegHandler.cs
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
82
X86Disassembler/X86/Handlers/XorRegMemHandler.cs
Normal file
82
X86Disassembler/X86/Handlers/XorRegMemHandler.cs
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
@ -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}";
|
||||||
|
|
||||||
|
// Format the raw bytes
|
||||||
|
string bytesStr = string.Empty;
|
||||||
|
foreach (byte b in RawBytes)
|
||||||
|
{
|
||||||
|
bytesStr += $"{b:X2} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Pad the bytes string to a fixed width
|
||||||
/// Converts the instruction bytes to a formatted hex string
|
bytesStr = bytesStr.PadRight(30);
|
||||||
/// </summary>
|
|
||||||
/// <returns>A formatted hex string of the instruction bytes</returns>
|
// Format the instruction
|
||||||
private string BytesToString()
|
string instructionStr = Mnemonic;
|
||||||
|
if (!string.IsNullOrEmpty(Operands))
|
||||||
{
|
{
|
||||||
return string.Join(" ", Bytes.Select(b => b.ToString("X2")));
|
instructionStr += " " + Operands;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $" {addressStr} {bytesStr}{instructionStr}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
|
||||||
|
if (prefix == 0x66) // Operand size prefix
|
||||||
|
{
|
||||||
|
_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)
|
switch (prefix)
|
||||||
{
|
{
|
||||||
case PREFIX_LOCK:
|
case 0x26: _segmentOverride = "es"; break;
|
||||||
case PREFIX_REPNE:
|
case 0x2E: _segmentOverride = "cs"; break;
|
||||||
case PREFIX_REP:
|
case 0x36: _segmentOverride = "ss"; break;
|
||||||
|
case 0x3E: _segmentOverride = "ds"; break;
|
||||||
|
case 0x64: _segmentOverride = "fs"; break;
|
||||||
|
case 0x65: _segmentOverride = "gs"; break;
|
||||||
|
}
|
||||||
_position++;
|
_position++;
|
||||||
break;
|
}
|
||||||
|
else if (prefix == 0xF0) // LOCK prefix
|
||||||
case PREFIX_CS:
|
{
|
||||||
segmentOverride = "cs";
|
_lockPrefix = true;
|
||||||
_position++;
|
_position++;
|
||||||
break;
|
}
|
||||||
|
else if (prefix == 0xF2 || prefix == 0xF3) // REP/REPNE prefix
|
||||||
case PREFIX_SS:
|
{
|
||||||
segmentOverride = "ss";
|
_repPrefix = true;
|
||||||
_position++;
|
_position++;
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
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;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user