From 6a69b0b91b32c8bd137881f7ff23ef32a633e73a Mon Sep 17 00:00:00 2001 From: bird_egop Date: Sat, 12 Apr 2025 18:23:18 +0300 Subject: [PATCH] Update code style to follow project rules with one-liner namespace declarations --- X86Disassembler/PE/DOSHeader.cs | 69 ++- X86Disassembler/PE/ExportDirectory.cs | 53 ++- X86Disassembler/PE/FileHeader.cs | 29 +- X86Disassembler/PE/ImportDescriptor.cs | 41 +- X86Disassembler/PE/OptionalHeader.cs | 133 +++--- X86Disassembler/PE/PEFormat.cs | 421 +++++++++--------- X86Disassembler/PE/PEUtility.cs | 90 ++-- X86Disassembler/PE/Parsers/DOSHeaderParser.cs | 102 ++--- .../PE/Parsers/ExportDirectoryParser.cs | 320 +++++++------ .../PE/Parsers/FileHeaderParser.cs | 43 +- X86Disassembler/PE/Parsers/IParser.cs | 23 +- .../PE/Parsers/ImportDescriptorParser.cs | 328 +++++++------- .../PE/Parsers/OptionalHeaderParser.cs | 204 +++++---- .../PE/Parsers/SectionHeaderParser.cs | 58 ++- X86Disassembler/PE/SectionHeader.cs | 131 +++--- X86Disassembler/Program.cs | 253 ++++++----- 16 files changed, 1126 insertions(+), 1172 deletions(-) diff --git a/X86Disassembler/PE/DOSHeader.cs b/X86Disassembler/PE/DOSHeader.cs index ea30ee4..9e321bb 100644 --- a/X86Disassembler/PE/DOSHeader.cs +++ b/X86Disassembler/PE/DOSHeader.cs @@ -1,40 +1,37 @@ -using System; +namespace X86Disassembler.PE; -namespace X86Disassembler.PE +/// +/// Represents the DOS header of a PE file +/// +public class DOSHeader { - /// - /// Represents the DOS header of a PE file - /// - public class DOSHeader - { - public ushort e_magic; // Magic number (MZ) - public ushort e_cblp; // Bytes on last page of file - public ushort e_cp; // Pages in file - public ushort e_crlc; // Relocations - public ushort e_cparhdr; // Size of header in paragraphs - public ushort e_minalloc; // Minimum extra paragraphs needed - public ushort e_maxalloc; // Maximum extra paragraphs needed - public ushort e_ss; // Initial (relative) SS value - public ushort e_sp; // Initial SP value - public ushort e_csum; // Checksum - public ushort e_ip; // Initial IP value - public ushort e_cs; // Initial (relative) CS value - public ushort e_lfarlc; // File address of relocation table - public ushort e_ovno; // Overlay number - public ushort[] e_res; // Reserved words - public ushort e_oemid; // OEM identifier (for e_oeminfo) - public ushort e_oeminfo; // OEM information; e_oemid specific - public ushort[] e_res2; // Reserved words - public uint e_lfanew; // File address of new exe header + public ushort e_magic; // Magic number (MZ) + public ushort e_cblp; // Bytes on last page of file + public ushort e_cp; // Pages in file + public ushort e_crlc; // Relocations + public ushort e_cparhdr; // Size of header in paragraphs + public ushort e_minalloc; // Minimum extra paragraphs needed + public ushort e_maxalloc; // Maximum extra paragraphs needed + public ushort e_ss; // Initial (relative) SS value + public ushort e_sp; // Initial SP value + public ushort e_csum; // Checksum + public ushort e_ip; // Initial IP value + public ushort e_cs; // Initial (relative) CS value + public ushort e_lfarlc; // File address of relocation table + public ushort e_ovno; // Overlay number + public ushort[] e_res; // Reserved words + public ushort e_oemid; // OEM identifier (for e_oeminfo) + public ushort e_oeminfo; // OEM information; e_oemid specific + public ushort[] e_res2; // Reserved words + public uint e_lfanew; // File address of new exe header - /// - /// Initializes a new instance of the DOSHeader class - /// - public DOSHeader() - { - // Initialize arrays to avoid nullability warnings - e_res = new ushort[4]; - e_res2 = new ushort[10]; - } + /// + /// Initializes a new instance of the DOSHeader class + /// + public DOSHeader() + { + // Initialize arrays to avoid nullability warnings + e_res = new ushort[4]; + e_res2 = new ushort[10]; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/ExportDirectory.cs b/X86Disassembler/PE/ExportDirectory.cs index c29086c..4a02d95 100644 --- a/X86Disassembler/PE/ExportDirectory.cs +++ b/X86Disassembler/PE/ExportDirectory.cs @@ -1,30 +1,29 @@ -namespace X86Disassembler.PE +namespace X86Disassembler.PE; + +/// +/// Represents the Export Directory of a PE file +/// +public class ExportDirectory { - /// - /// Represents the Export Directory of a PE file - /// - public class ExportDirectory - { - public uint Characteristics; // Reserved, must be 0 - public uint TimeDateStamp; // Time and date stamp - public ushort MajorVersion; // Major version - public ushort MinorVersion; // Minor version - public uint Name; // RVA of the name of the DLL - public string DllName; // The actual name of the DLL - public uint Base; // Ordinal base - public uint NumberOfFunctions; // Number of functions - public uint NumberOfNames; // Number of names - public uint AddressOfFunctions; // RVA of the export address table - public uint AddressOfNames; // RVA of the export names table - public uint AddressOfNameOrdinals; // RVA of the ordinal table + public uint Characteristics; // Reserved, must be 0 + public uint TimeDateStamp; // Time and date stamp + public ushort MajorVersion; // Major version + public ushort MinorVersion; // Minor version + public uint Name; // RVA of the name of the DLL + public string DllName; // The actual name of the DLL + public uint Base; // Ordinal base + public uint NumberOfFunctions; // Number of functions + public uint NumberOfNames; // Number of names + public uint AddressOfFunctions; // RVA of the export address table + public uint AddressOfNames; // RVA of the export names table + public uint AddressOfNameOrdinals; // RVA of the ordinal table - /// - /// Initializes a new instance of the ExportDirectory class - /// - public ExportDirectory() - { - // Initialize string field to avoid nullability warning - DllName = string.Empty; - } + /// + /// Initializes a new instance of the ExportDirectory class + /// + public ExportDirectory() + { + // Initialize string field to avoid nullability warning + DllName = string.Empty; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/FileHeader.cs b/X86Disassembler/PE/FileHeader.cs index 00a8538..24364f8 100644 --- a/X86Disassembler/PE/FileHeader.cs +++ b/X86Disassembler/PE/FileHeader.cs @@ -1,16 +1,15 @@ -namespace X86Disassembler.PE +namespace X86Disassembler.PE; + +/// +/// Represents the File header of a PE file +/// +public class FileHeader { - /// - /// Represents the File header of a PE file - /// - public class FileHeader - { - public ushort Machine; // Target machine type - public ushort NumberOfSections; // Number of sections - public uint TimeDateStamp; // Time and date stamp - public uint PointerToSymbolTable; // File pointer to COFF symbol table - public uint NumberOfSymbols; // Number of symbols - public ushort SizeOfOptionalHeader; // Size of optional header - public ushort Characteristics; // Characteristics - } -} + public ushort Machine; // Target machine type + public ushort NumberOfSections; // Number of sections + public uint TimeDateStamp; // Time and date stamp + public uint PointerToSymbolTable; // File pointer to COFF symbol table + public uint NumberOfSymbols; // Number of symbols + public ushort SizeOfOptionalHeader; // Size of optional header + public ushort Characteristics; // Characteristics +} \ No newline at end of file diff --git a/X86Disassembler/PE/ImportDescriptor.cs b/X86Disassembler/PE/ImportDescriptor.cs index 0b5acdf..56bbf4d 100644 --- a/X86Disassembler/PE/ImportDescriptor.cs +++ b/X86Disassembler/PE/ImportDescriptor.cs @@ -1,28 +1,25 @@ -using System.Collections.Generic; +namespace X86Disassembler.PE; -namespace X86Disassembler.PE +/// +/// Represents an Import Descriptor in a PE file +/// +public class ImportDescriptor { + public uint OriginalFirstThunk; // RVA to original first thunk + public uint TimeDateStamp; // Time and date stamp + public uint ForwarderChain; // Forwarder chain + public uint Name; // RVA to the name of the DLL + public string DllName; // The actual name of the DLL + public uint FirstThunk; // RVA to first thunk + + public List Functions { get; } = new List(); + /// - /// Represents an Import Descriptor in a PE file + /// Initializes a new instance of the ImportDescriptor class /// - public class ImportDescriptor + public ImportDescriptor() { - public uint OriginalFirstThunk; // RVA to original first thunk - public uint TimeDateStamp; // Time and date stamp - public uint ForwarderChain; // Forwarder chain - public uint Name; // RVA to the name of the DLL - public string DllName; // The actual name of the DLL - public uint FirstThunk; // RVA to first thunk - - public List Functions { get; } = new List(); - - /// - /// Initializes a new instance of the ImportDescriptor class - /// - public ImportDescriptor() - { - // Initialize string field to avoid nullability warning - DllName = string.Empty; - } + // Initialize string field to avoid nullability warning + DllName = string.Empty; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/OptionalHeader.cs b/X86Disassembler/PE/OptionalHeader.cs index a4e4b83..ff335a1 100644 --- a/X86Disassembler/PE/OptionalHeader.cs +++ b/X86Disassembler/PE/OptionalHeader.cs @@ -1,73 +1,72 @@ -namespace X86Disassembler.PE +namespace X86Disassembler.PE; + +/// +/// Represents the Optional header of a PE file +/// +public class OptionalHeader { + // Optional Header Magic values + private const ushort PE32_MAGIC = 0x10B; // 32-bit executable + private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable + + // Standard fields + public ushort Magic; // Magic number (PE32 or PE32+) + public byte MajorLinkerVersion; // Major linker version + public byte MinorLinkerVersion; // Minor linker version + public uint SizeOfCode; // Size of code section + public uint SizeOfInitializedData; // Size of initialized data section + public uint SizeOfUninitializedData; // Size of uninitialized data section + public uint AddressOfEntryPoint; // Address of entry point + public uint BaseOfCode; // Base of code section + public uint BaseOfData; // Base of data section (PE32 only) + + // Windows-specific fields + public object ImageBase; // Image base address (uint for PE32, ulong for PE32+) + public uint SectionAlignment; // Section alignment + public uint FileAlignment; // File alignment + public ushort MajorOperatingSystemVersion; // Major OS version + public ushort MinorOperatingSystemVersion; // Minor OS version + public ushort MajorImageVersion; // Major image version + public ushort MinorImageVersion; // Minor image version + public ushort MajorSubsystemVersion; // Major subsystem version + public ushort MinorSubsystemVersion; // Minor subsystem version + public uint Win32VersionValue; // Win32 version value + public uint SizeOfImage; // Size of image + public uint SizeOfHeaders; // Size of headers + public uint CheckSum; // Checksum + public ushort Subsystem; // Subsystem + public ushort DllCharacteristics; // DLL characteristics + public object SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+) + public object SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+) + public object SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+) + public object SizeOfHeapCommit; // Size of heap commit (uint for PE32, ulong for PE32+) + public uint LoaderFlags; // Loader flags + public uint NumberOfRvaAndSizes; // Number of RVA and sizes + + public DataDirectory[] DataDirectories; // Data directories + /// - /// Represents the Optional header of a PE file + /// Initializes a new instance of the OptionalHeader class /// - public class OptionalHeader + public OptionalHeader() { - // Optional Header Magic values - private const ushort PE32_MAGIC = 0x10B; // 32-bit executable - private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable - - // Standard fields - public ushort Magic; // Magic number (PE32 or PE32+) - public byte MajorLinkerVersion; // Major linker version - public byte MinorLinkerVersion; // Minor linker version - public uint SizeOfCode; // Size of code section - public uint SizeOfInitializedData; // Size of initialized data section - public uint SizeOfUninitializedData; // Size of uninitialized data section - public uint AddressOfEntryPoint; // Address of entry point - public uint BaseOfCode; // Base of code section - public uint BaseOfData; // Base of data section (PE32 only) - - // Windows-specific fields - public object ImageBase; // Image base address (uint for PE32, ulong for PE32+) - public uint SectionAlignment; // Section alignment - public uint FileAlignment; // File alignment - public ushort MajorOperatingSystemVersion; // Major OS version - public ushort MinorOperatingSystemVersion; // Minor OS version - public ushort MajorImageVersion; // Major image version - public ushort MinorImageVersion; // Minor image version - public ushort MajorSubsystemVersion; // Major subsystem version - public ushort MinorSubsystemVersion; // Minor subsystem version - public uint Win32VersionValue; // Win32 version value - public uint SizeOfImage; // Size of image - public uint SizeOfHeaders; // Size of headers - public uint CheckSum; // Checksum - public ushort Subsystem; // Subsystem - public ushort DllCharacteristics; // DLL characteristics - public object SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+) - public object SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+) - public object SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+) - public object SizeOfHeapCommit; // Size of heap commit (uint for PE32, ulong for PE32+) - public uint LoaderFlags; // Loader flags - public uint NumberOfRvaAndSizes; // Number of RVA and sizes - - public DataDirectory[] DataDirectories; // Data directories - - /// - /// Initializes a new instance of the OptionalHeader class - /// - public OptionalHeader() - { - // Initialize object fields to avoid nullability warnings - ImageBase = 0u; // Default to 32-bit value - SizeOfStackReserve = 0u; - SizeOfStackCommit = 0u; - SizeOfHeapReserve = 0u; - SizeOfHeapCommit = 0u; + // Initialize object fields to avoid nullability warnings + ImageBase = 0u; // Default to 32-bit value + SizeOfStackReserve = 0u; + SizeOfStackCommit = 0u; + SizeOfHeapReserve = 0u; + SizeOfHeapCommit = 0u; - // Initialize array to avoid nullability warning - DataDirectories = new DataDirectory[0]; - } - - /// - /// Determines if the PE file is 64-bit based on the Magic value - /// - /// True if the PE file is 64-bit, false otherwise - public bool Is64Bit() - { - return Magic == PE32PLUS_MAGIC; - } + // Initialize array to avoid nullability warning + DataDirectories = new DataDirectory[0]; } -} + + /// + /// Determines if the PE file is 64-bit based on the Magic value + /// + /// True if the PE file is 64-bit, false otherwise + public bool Is64Bit() + { + return Magic == PE32PLUS_MAGIC; + } +} \ No newline at end of file diff --git a/X86Disassembler/PE/PEFormat.cs b/X86Disassembler/PE/PEFormat.cs index dbb8c62..130a0f8 100644 --- a/X86Disassembler/PE/PEFormat.cs +++ b/X86Disassembler/PE/PEFormat.cs @@ -1,236 +1,231 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; using X86Disassembler.PE.Parsers; -namespace X86Disassembler.PE +namespace X86Disassembler.PE; + +/// +/// Represents a Portable Executable (PE) file format parser +/// +public class PEFormat { + // DOS Header constants + private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' + private const uint PE_SIGNATURE = 0x00004550; // 'PE\0\0' + + // Optional Header Magic values + private const ushort PE32_MAGIC = 0x10B; // 32-bit executable + private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable + + // Section characteristics flags + private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code + private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable + private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable + private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable + + // Data directories + private const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // Export Directory + private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // Import Directory + private const int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // Resource Directory + private const int IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // Exception Directory + private const int IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // Security Directory + private const int IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // Base Relocation Table + private const int IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // Debug Directory + private const int IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; // Architecture Specific Data + private const int IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // RVA of GP + private const int IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory + private const int IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // Load Configuration Directory + private const int IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory + private const int IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table + private const int IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; // Delay Load Import Descriptors + private const int IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; // COM Runtime descriptor + + // PE file data + private byte[] _fileData; + + // Parser instances + private readonly DOSHeaderParser _dosHeaderParser; + private readonly FileHeaderParser _fileHeaderParser; + private readonly OptionalHeaderParser _optionalHeaderParser; + private readonly SectionHeaderParser _sectionHeaderParser; + private PEUtility _peUtility; + private ExportDirectoryParser _exportDirectoryParser; + private ImportDescriptorParser _importDescriptorParser; + + // Parsed headers + public DOSHeader DosHeader { get; private set; } + public FileHeader FileHeader { get; private set; } + public OptionalHeader OptionalHeader { get; private set; } + public List SectionHeaders { get; private set; } + public bool Is64Bit { get; private set; } + + // Export and Import information + public ExportDirectory ExportDirectory { get; private set; } + public List ExportedFunctions { get; private set; } + public List ImportDescriptors { get; private set; } + /// - /// Represents a Portable Executable (PE) file format parser + /// Initializes a new instance of the PEFormat class /// - public class PEFormat + /// The raw file data + public PEFormat(byte[] fileData) { - // DOS Header constants - private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' - private const uint PE_SIGNATURE = 0x00004550; // 'PE\0\0' + _fileData = fileData; + SectionHeaders = new List(); + ExportedFunctions = new List(); + ImportDescriptors = new List(); + + // Initialize parsers + _dosHeaderParser = new DOSHeaderParser(); + _fileHeaderParser = new FileHeaderParser(); + _optionalHeaderParser = new OptionalHeaderParser(); + _sectionHeaderParser = new SectionHeaderParser(); + + // Initialize properties to avoid nullability warnings + DosHeader = new DOSHeader(); + FileHeader = new FileHeader(); + OptionalHeader = new OptionalHeader(); + ExportDirectory = new ExportDirectory(); + + // These will be initialized during Parse() + _peUtility = null!; + _exportDirectoryParser = null!; + _importDescriptorParser = null!; + } - // Optional Header Magic values - private const ushort PE32_MAGIC = 0x10B; // 32-bit executable - private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable - - // Section characteristics flags - private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code - private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable - private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable - private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable - - // Data directories - private const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // Export Directory - private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // Import Directory - private const int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // Resource Directory - private const int IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // Exception Directory - private const int IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // Security Directory - private const int IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // Base Relocation Table - private const int IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // Debug Directory - private const int IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; // Architecture Specific Data - private const int IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // RVA of GP - private const int IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory - private const int IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // Load Configuration Directory - private const int IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory - private const int IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table - private const int IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; // Delay Load Import Descriptors - private const int IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; // COM Runtime descriptor - - // PE file data - private byte[] _fileData; - - // Parser instances - private readonly DOSHeaderParser _dosHeaderParser; - private readonly FileHeaderParser _fileHeaderParser; - private readonly OptionalHeaderParser _optionalHeaderParser; - private readonly SectionHeaderParser _sectionHeaderParser; - private PEUtility _peUtility; - private ExportDirectoryParser _exportDirectoryParser; - private ImportDescriptorParser _importDescriptorParser; - - // Parsed headers - public DOSHeader DosHeader { get; private set; } - public FileHeader FileHeader { get; private set; } - public OptionalHeader OptionalHeader { get; private set; } - public List SectionHeaders { get; private set; } - public bool Is64Bit { get; private set; } - - // Export and Import information - public ExportDirectory ExportDirectory { get; private set; } - public List ExportedFunctions { get; private set; } - public List ImportDescriptors { get; private set; } - - /// - /// Initializes a new instance of the PEFormat class - /// - /// The raw file data - public PEFormat(byte[] fileData) + /// + /// Parses the PE file structure + /// + /// True if parsing was successful, false otherwise + public bool Parse() + { + try { - _fileData = fileData; - SectionHeaders = new List(); - ExportedFunctions = new List(); - ImportDescriptors = new List(); - - // Initialize parsers - _dosHeaderParser = new DOSHeaderParser(); - _fileHeaderParser = new FileHeaderParser(); - _optionalHeaderParser = new OptionalHeaderParser(); - _sectionHeaderParser = new SectionHeaderParser(); - - // Initialize properties to avoid nullability warnings - DosHeader = new DOSHeader(); - FileHeader = new FileHeader(); - OptionalHeader = new OptionalHeader(); - ExportDirectory = new ExportDirectory(); - - // These will be initialized during Parse() - _peUtility = null!; - _exportDirectoryParser = null!; - _importDescriptorParser = null!; - } - - /// - /// Parses the PE file structure - /// - /// True if parsing was successful, false otherwise - public bool Parse() - { - try + using (MemoryStream stream = new MemoryStream(_fileData)) + using (BinaryReader reader = new BinaryReader(stream)) { - using (MemoryStream stream = new MemoryStream(_fileData)) - using (BinaryReader reader = new BinaryReader(stream)) + // Parse DOS header + DosHeader = _dosHeaderParser.Parse(reader); + + // Move to PE header + reader.BaseStream.Seek(DosHeader.e_lfanew, SeekOrigin.Begin); + + // Verify PE signature + uint peSignature = reader.ReadUInt32(); + if (peSignature != PE_SIGNATURE) { - // Parse DOS header - DosHeader = _dosHeaderParser.Parse(reader); + throw new InvalidDataException("Invalid PE signature"); + } - // Move to PE header - reader.BaseStream.Seek(DosHeader.e_lfanew, SeekOrigin.Begin); + // Parse File Header + FileHeader = _fileHeaderParser.Parse(reader); - // Verify PE signature - uint peSignature = reader.ReadUInt32(); - if (peSignature != PE_SIGNATURE) - { - throw new InvalidDataException("Invalid PE signature"); - } + // Parse Optional Header + OptionalHeader = _optionalHeaderParser.Parse(reader); + Is64Bit = OptionalHeader.Is64Bit(); - // Parse File Header - FileHeader = _fileHeaderParser.Parse(reader); + // Parse Section Headers + for (int i = 0; i < FileHeader.NumberOfSections; i++) + { + SectionHeaders.Add(_sectionHeaderParser.Parse(reader)); + } - // Parse Optional Header - OptionalHeader = _optionalHeaderParser.Parse(reader); - Is64Bit = OptionalHeader.Is64Bit(); + // Initialize utility after section headers are parsed + _peUtility = new PEUtility(SectionHeaders, OptionalHeader.SizeOfHeaders); + _exportDirectoryParser = new ExportDirectoryParser(_peUtility); + _importDescriptorParser = new ImportDescriptorParser(_peUtility); - // Parse Section Headers - for (int i = 0; i < FileHeader.NumberOfSections; i++) - { - SectionHeaders.Add(_sectionHeaderParser.Parse(reader)); - } - - // Initialize utility after section headers are parsed - _peUtility = new PEUtility(SectionHeaders, OptionalHeader.SizeOfHeaders); - _exportDirectoryParser = new ExportDirectoryParser(_peUtility); - _importDescriptorParser = new ImportDescriptorParser(_peUtility); - - // Parse Export Directory - if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_EXPORT && - OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress != 0) - { - uint exportDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - uint exportDirSize = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + // Parse Export Directory + if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_EXPORT && + OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress != 0) + { + uint exportDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + uint exportDirSize = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; - ExportDirectory = _exportDirectoryParser.Parse(reader, exportDirRva); - ExportedFunctions = _exportDirectoryParser.ParseExportedFunctions(reader, ExportDirectory, exportDirRva, exportDirSize); - } + ExportDirectory = _exportDirectoryParser.Parse(reader, exportDirRva); + ExportedFunctions = _exportDirectoryParser.ParseExportedFunctions(reader, ExportDirectory, exportDirRva, exportDirSize); + } - // Parse Import Descriptors - if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_IMPORT && - OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0) - { - uint importDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - ImportDescriptors = _importDescriptorParser.Parse(reader, importDirRva); - } + // Parse Import Descriptors + if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_IMPORT && + OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0) + { + uint importDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + ImportDescriptors = _importDescriptorParser.Parse(reader, importDirRva); } + } - return true; - } - catch (Exception ex) - { - Console.WriteLine($"Error parsing PE file: {ex.Message}"); - return false; - } + return true; } - - /// - /// Gets the raw data for a specific section - /// - /// Index of the section - /// Byte array containing the section data - public byte[] GetSectionData(int sectionIndex) + catch (Exception ex) { - if (sectionIndex < 0 || sectionIndex >= SectionHeaders.Count) - { - throw new ArgumentOutOfRangeException(nameof(sectionIndex)); - } - - SectionHeader section = SectionHeaders[sectionIndex]; - byte[] sectionData = new byte[section.SizeOfRawData]; - - Array.Copy(_fileData, section.PointerToRawData, sectionData, 0, section.SizeOfRawData); - - return sectionData; - } - - /// - /// Gets the raw data for a section by name - /// - /// Name of the section - /// Byte array containing the section data - public byte[] GetSectionData(string sectionName) - { - for (int i = 0; i < SectionHeaders.Count; i++) - { - if (SectionHeaders[i].Name == sectionName) - { - return GetSectionData(i); - } - } - - throw new ArgumentException($"Section '{sectionName}' not found"); - } - - /// - /// Gets all code sections - /// - /// List of section indices that contain code - public List GetCodeSections() - { - List codeSections = new List(); - - for (int i = 0; i < SectionHeaders.Count; i++) - { - if (SectionHeaders[i].ContainsCode()) - { - codeSections.Add(i); - } - } - - return codeSections; - } - - /// - /// Checks if a section contains code - /// - /// The section to check - /// True if the section contains code, false otherwise - public bool IsSectionContainsCode(SectionHeader section) - { - return section.ContainsCode(); + Console.WriteLine($"Error parsing PE file: {ex.Message}"); + return false; } } -} + + /// + /// Gets the raw data for a specific section + /// + /// Index of the section + /// Byte array containing the section data + public byte[] GetSectionData(int sectionIndex) + { + if (sectionIndex < 0 || sectionIndex >= SectionHeaders.Count) + { + throw new ArgumentOutOfRangeException(nameof(sectionIndex)); + } + + SectionHeader section = SectionHeaders[sectionIndex]; + byte[] sectionData = new byte[section.SizeOfRawData]; + + Array.Copy(_fileData, section.PointerToRawData, sectionData, 0, section.SizeOfRawData); + + return sectionData; + } + + /// + /// Gets the raw data for a section by name + /// + /// Name of the section + /// Byte array containing the section data + public byte[] GetSectionData(string sectionName) + { + for (int i = 0; i < SectionHeaders.Count; i++) + { + if (SectionHeaders[i].Name == sectionName) + { + return GetSectionData(i); + } + } + + throw new ArgumentException($"Section '{sectionName}' not found"); + } + + /// + /// Gets all code sections + /// + /// List of section indices that contain code + public List GetCodeSections() + { + List codeSections = new List(); + + for (int i = 0; i < SectionHeaders.Count; i++) + { + if (SectionHeaders[i].ContainsCode()) + { + codeSections.Add(i); + } + } + + return codeSections; + } + + /// + /// Checks if a section contains code + /// + /// The section to check + /// True if the section contains code, false otherwise + public bool IsSectionContainsCode(SectionHeader section) + { + return section.ContainsCode(); + } +} \ No newline at end of file diff --git a/X86Disassembler/PE/PEUtility.cs b/X86Disassembler/PE/PEUtility.cs index eea571a..8f9de86 100644 --- a/X86Disassembler/PE/PEUtility.cs +++ b/X86Disassembler/PE/PEUtility.cs @@ -1,62 +1,58 @@ -using System; -using System.Collections.Generic; +namespace X86Disassembler.PE; -namespace X86Disassembler.PE +/// +/// Utility class for PE format operations +/// +public class PEUtility { + private readonly List _sectionHeaders; + private readonly uint _sizeOfHeaders; + /// - /// Utility class for PE format operations + /// Initialize a new instance of the PEUtility class /// - public class PEUtility + /// The section headers + /// The size of the headers + public PEUtility(List sectionHeaders, uint sizeOfHeaders) { - private readonly List _sectionHeaders; - private readonly uint _sizeOfHeaders; + _sectionHeaders = sectionHeaders; + _sizeOfHeaders = sizeOfHeaders; + } - /// - /// Initialize a new instance of the PEUtility class - /// - /// The section headers - /// The size of the headers - public PEUtility(List sectionHeaders, uint sizeOfHeaders) + /// + /// Converts a Relative Virtual Address (RVA) to a file offset + /// + /// The RVA to convert + /// The corresponding file offset + public uint RvaToOffset(uint rva) + { + if (rva == 0) { - _sectionHeaders = sectionHeaders; - _sizeOfHeaders = sizeOfHeaders; + return 0; } - - /// - /// Converts a Relative Virtual Address (RVA) to a file offset - /// - /// The RVA to convert - /// The corresponding file offset - public uint RvaToOffset(uint rva) - { - if (rva == 0) - { - return 0; - } - foreach (var section in _sectionHeaders) + foreach (var section in _sectionHeaders) + { + // Check if the RVA is within this section + if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) { - // Check if the RVA is within this section - if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize) - { - // Calculate the offset within the section - uint offsetInSection = rva - section.VirtualAddress; + // Calculate the offset within the section + uint offsetInSection = rva - section.VirtualAddress; - // Make sure we don't exceed the raw data size - if (offsetInSection < section.SizeOfRawData) - { - return section.PointerToRawData + offsetInSection; - } + // Make sure we don't exceed the raw data size + if (offsetInSection < section.SizeOfRawData) + { + return section.PointerToRawData + offsetInSection; } } - - // If the RVA is not within any section, it might be in the headers - if (rva < _sizeOfHeaders) - { - return rva; - } - - throw new ArgumentException($"RVA {rva:X8} is not within any section"); } + + // If the RVA is not within any section, it might be in the headers + if (rva < _sizeOfHeaders) + { + return rva; + } + + throw new ArgumentException($"RVA {rva:X8} is not within any section"); } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/DOSHeaderParser.cs b/X86Disassembler/PE/Parsers/DOSHeaderParser.cs index 3cc5427..9699920 100644 --- a/X86Disassembler/PE/Parsers/DOSHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/DOSHeaderParser.cs @@ -1,63 +1,59 @@ -using System; -using System.IO; +namespace X86Disassembler.PE.Parsers; -namespace X86Disassembler.PE.Parsers +/// +/// Parser for the DOS header of a PE file +/// +public class DOSHeaderParser : IParser { + // DOS Header constants + private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' + /// - /// Parser for the DOS header of a PE file + /// Parse the DOS header from the binary reader /// - public class DOSHeaderParser : IParser + /// The binary reader positioned at the start of the DOS header + /// The parsed DOS header + public DOSHeader Parse(BinaryReader reader) { - // DOS Header constants - private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' + DOSHeader header = new DOSHeader(); - /// - /// Parse the DOS header from the binary reader - /// - /// The binary reader positioned at the start of the DOS header - /// The parsed DOS header - public DOSHeader Parse(BinaryReader reader) + header.e_magic = reader.ReadUInt16(); + if (header.e_magic != DOS_SIGNATURE) { - DOSHeader header = new DOSHeader(); - - header.e_magic = reader.ReadUInt16(); - if (header.e_magic != DOS_SIGNATURE) - { - throw new InvalidDataException("Invalid DOS signature (MZ)"); - } - - header.e_cblp = reader.ReadUInt16(); - header.e_cp = reader.ReadUInt16(); - header.e_crlc = reader.ReadUInt16(); - header.e_cparhdr = reader.ReadUInt16(); - header.e_minalloc = reader.ReadUInt16(); - header.e_maxalloc = reader.ReadUInt16(); - header.e_ss = reader.ReadUInt16(); - header.e_sp = reader.ReadUInt16(); - header.e_csum = reader.ReadUInt16(); - header.e_ip = reader.ReadUInt16(); - header.e_cs = reader.ReadUInt16(); - header.e_lfarlc = reader.ReadUInt16(); - header.e_ovno = reader.ReadUInt16(); - - header.e_res = new ushort[4]; - for (int i = 0; i < 4; i++) - { - header.e_res[i] = reader.ReadUInt16(); - } - - header.e_oemid = reader.ReadUInt16(); - header.e_oeminfo = reader.ReadUInt16(); - - header.e_res2 = new ushort[10]; - for (int i = 0; i < 10; i++) - { - header.e_res2[i] = reader.ReadUInt16(); - } - - header.e_lfanew = reader.ReadUInt32(); - - return header; + throw new InvalidDataException("Invalid DOS signature (MZ)"); } + + header.e_cblp = reader.ReadUInt16(); + header.e_cp = reader.ReadUInt16(); + header.e_crlc = reader.ReadUInt16(); + header.e_cparhdr = reader.ReadUInt16(); + header.e_minalloc = reader.ReadUInt16(); + header.e_maxalloc = reader.ReadUInt16(); + header.e_ss = reader.ReadUInt16(); + header.e_sp = reader.ReadUInt16(); + header.e_csum = reader.ReadUInt16(); + header.e_ip = reader.ReadUInt16(); + header.e_cs = reader.ReadUInt16(); + header.e_lfarlc = reader.ReadUInt16(); + header.e_ovno = reader.ReadUInt16(); + + header.e_res = new ushort[4]; + for (int i = 0; i < 4; i++) + { + header.e_res[i] = reader.ReadUInt16(); + } + + header.e_oemid = reader.ReadUInt16(); + header.e_oeminfo = reader.ReadUInt16(); + + header.e_res2 = new ushort[10]; + for (int i = 0; i < 10; i++) + { + header.e_res2[i] = reader.ReadUInt16(); + } + + header.e_lfanew = reader.ReadUInt32(); + + return header; } } diff --git a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs index 2e71f8b..666947e 100644 --- a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs +++ b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs @@ -1,176 +1,172 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Text; -namespace X86Disassembler.PE.Parsers +namespace X86Disassembler.PE.Parsers; + +/// +/// Parser for the Export Directory of a PE file +/// +public class ExportDirectoryParser { - /// - /// Parser for the Export Directory of a PE file - /// - public class ExportDirectoryParser + private readonly PEUtility _utility; + + public ExportDirectoryParser(PEUtility utility) { - private readonly PEUtility _utility; + _utility = utility; + } + + /// + /// Parse the Export Directory from the binary reader + /// + /// The binary reader + /// The RVA of the Export Directory + /// The parsed Export Directory + public ExportDirectory Parse(BinaryReader reader, uint rva) + { + ExportDirectory directory = new ExportDirectory(); - public ExportDirectoryParser(PEUtility utility) + reader.BaseStream.Seek(_utility.RvaToOffset(rva), SeekOrigin.Begin); + + directory.Characteristics = reader.ReadUInt32(); + directory.TimeDateStamp = reader.ReadUInt32(); + directory.MajorVersion = reader.ReadUInt16(); + directory.MinorVersion = reader.ReadUInt16(); + directory.Name = reader.ReadUInt32(); + directory.Base = reader.ReadUInt32(); + directory.NumberOfFunctions = reader.ReadUInt32(); + directory.NumberOfNames = reader.ReadUInt32(); + directory.AddressOfFunctions = reader.ReadUInt32(); + directory.AddressOfNames = reader.ReadUInt32(); + directory.AddressOfNameOrdinals = reader.ReadUInt32(); + + // Read the DLL name + try { - _utility = utility; + uint dllNameRVA = directory.Name; + uint dllNameOffset = _utility.RvaToOffset(dllNameRVA); + reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin); + + // Read the null-terminated ASCII string + StringBuilder nameBuilder = new StringBuilder(); + byte b; + + while ((b = reader.ReadByte()) != 0) + { + nameBuilder.Append((char)b); + } + + directory.DllName = nameBuilder.ToString(); + } + catch (Exception) + { + directory.DllName = "Unknown"; } - /// - /// Parse the Export Directory from the binary reader - /// - /// The binary reader - /// The RVA of the Export Directory - /// The parsed Export Directory - public ExportDirectory Parse(BinaryReader reader, uint rva) - { - ExportDirectory directory = new ExportDirectory(); - - reader.BaseStream.Seek(_utility.RvaToOffset(rva), SeekOrigin.Begin); - - directory.Characteristics = reader.ReadUInt32(); - directory.TimeDateStamp = reader.ReadUInt32(); - directory.MajorVersion = reader.ReadUInt16(); - directory.MinorVersion = reader.ReadUInt16(); - directory.Name = reader.ReadUInt32(); - directory.Base = reader.ReadUInt32(); - directory.NumberOfFunctions = reader.ReadUInt32(); - directory.NumberOfNames = reader.ReadUInt32(); - directory.AddressOfFunctions = reader.ReadUInt32(); - directory.AddressOfNames = reader.ReadUInt32(); - directory.AddressOfNameOrdinals = reader.ReadUInt32(); - - // Read the DLL name - try - { - uint dllNameRVA = directory.Name; - uint dllNameOffset = _utility.RvaToOffset(dllNameRVA); - reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin); - - // Read the null-terminated ASCII string - StringBuilder nameBuilder = new StringBuilder(); - byte b; - - while ((b = reader.ReadByte()) != 0) - { - nameBuilder.Append((char)b); - } - - directory.DllName = nameBuilder.ToString(); - } - catch (Exception) - { - directory.DllName = "Unknown"; - } - - return directory; - } + return directory; + } + + /// + /// Parse the exported functions using the export directory information + /// + /// The binary reader + /// The Export Directory + /// The RVA of the Export Directory + /// The size of the Export Directory + /// List of exported functions + public List ParseExportedFunctions(BinaryReader reader, ExportDirectory directory, uint exportDirRva, uint exportDirSize) + { + List exportedFunctions = new List(); - /// - /// Parse the exported functions using the export directory information - /// - /// The binary reader - /// The Export Directory - /// The RVA of the Export Directory - /// The size of the Export Directory - /// List of exported functions - public List ParseExportedFunctions(BinaryReader reader, ExportDirectory directory, uint exportDirRva, uint exportDirSize) + if (directory == null) { - List exportedFunctions = new List(); - - if (directory == null) - { - return exportedFunctions; - } - - // Read the array of function addresses (RVAs) - uint[] functionRVAs = new uint[directory.NumberOfFunctions]; - reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfFunctions), SeekOrigin.Begin); - for (int i = 0; i < directory.NumberOfFunctions; i++) - { - functionRVAs[i] = reader.ReadUInt32(); - } - - // Read the array of name RVAs - uint[] nameRVAs = new uint[directory.NumberOfNames]; - reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNames), SeekOrigin.Begin); - for (int i = 0; i < directory.NumberOfNames; i++) - { - nameRVAs[i] = reader.ReadUInt32(); - } - - // Read the array of name ordinals - ushort[] nameOrdinals = new ushort[directory.NumberOfNames]; - reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNameOrdinals), SeekOrigin.Begin); - for (int i = 0; i < directory.NumberOfNames; i++) - { - nameOrdinals[i] = reader.ReadUInt16(); - } - - // Create a dictionary to map ordinals to names - Dictionary ordinalToName = new Dictionary(); - for (int i = 0; i < directory.NumberOfNames; i++) - { - // Read the function name - reader.BaseStream.Seek(_utility.RvaToOffset(nameRVAs[i]), SeekOrigin.Begin); - List nameBytes = new List(); - byte b; - while ((b = reader.ReadByte()) != 0) - { - nameBytes.Add(b); - } - string name = Encoding.ASCII.GetString(nameBytes.ToArray()); - - // Map the ordinal to the name - ordinalToName[nameOrdinals[i]] = name; - } - - // Create the exported functions - for (ushort i = 0; i < directory.NumberOfFunctions; i++) - { - uint functionRVA = functionRVAs[i]; - if (functionRVA == 0) - { - continue; // Skip empty entries - } - - ExportedFunction function = new ExportedFunction(); - function.Ordinal = (ushort)(i + directory.Base); - function.Address = functionRVA; - - // Check if this function has a name - if (ordinalToName.TryGetValue(i, out string? name)) - { - function.Name = name ?? $"Ordinal_{function.Ordinal}"; - } - else - { - function.Name = $"Ordinal_{function.Ordinal}"; - } - - // Check if this is a forwarder - uint exportDirEnd = exportDirRva + exportDirSize; - - if (functionRVA >= exportDirRva && functionRVA < exportDirEnd) - { - function.IsForwarder = true; - - // Read the forwarder string - reader.BaseStream.Seek(_utility.RvaToOffset(functionRVA), SeekOrigin.Begin); - List forwarderBytes = new List(); - byte b; - while ((b = reader.ReadByte()) != 0) - { - forwarderBytes.Add(b); - } - function.ForwarderName = Encoding.ASCII.GetString(forwarderBytes.ToArray()); - } - - exportedFunctions.Add(function); - } - return exportedFunctions; } + + // Read the array of function addresses (RVAs) + uint[] functionRVAs = new uint[directory.NumberOfFunctions]; + reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfFunctions), SeekOrigin.Begin); + for (int i = 0; i < directory.NumberOfFunctions; i++) + { + functionRVAs[i] = reader.ReadUInt32(); + } + + // Read the array of name RVAs + uint[] nameRVAs = new uint[directory.NumberOfNames]; + reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNames), SeekOrigin.Begin); + for (int i = 0; i < directory.NumberOfNames; i++) + { + nameRVAs[i] = reader.ReadUInt32(); + } + + // Read the array of name ordinals + ushort[] nameOrdinals = new ushort[directory.NumberOfNames]; + reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNameOrdinals), SeekOrigin.Begin); + for (int i = 0; i < directory.NumberOfNames; i++) + { + nameOrdinals[i] = reader.ReadUInt16(); + } + + // Create a dictionary to map ordinals to names + Dictionary ordinalToName = new Dictionary(); + for (int i = 0; i < directory.NumberOfNames; i++) + { + // Read the function name + reader.BaseStream.Seek(_utility.RvaToOffset(nameRVAs[i]), SeekOrigin.Begin); + List nameBytes = new List(); + byte b; + while ((b = reader.ReadByte()) != 0) + { + nameBytes.Add(b); + } + string name = Encoding.ASCII.GetString(nameBytes.ToArray()); + + // Map the ordinal to the name + ordinalToName[nameOrdinals[i]] = name; + } + + // Create the exported functions + for (ushort i = 0; i < directory.NumberOfFunctions; i++) + { + uint functionRVA = functionRVAs[i]; + if (functionRVA == 0) + { + continue; // Skip empty entries + } + + ExportedFunction function = new ExportedFunction(); + function.Ordinal = (ushort)(i + directory.Base); + function.Address = functionRVA; + + // Check if this function has a name + if (ordinalToName.TryGetValue(i, out string? name)) + { + function.Name = name ?? $"Ordinal_{function.Ordinal}"; + } + else + { + function.Name = $"Ordinal_{function.Ordinal}"; + } + + // Check if this is a forwarder + uint exportDirEnd = exportDirRva + exportDirSize; + + if (functionRVA >= exportDirRva && functionRVA < exportDirEnd) + { + function.IsForwarder = true; + + // Read the forwarder string + reader.BaseStream.Seek(_utility.RvaToOffset(functionRVA), SeekOrigin.Begin); + List forwarderBytes = new List(); + byte b; + while ((b = reader.ReadByte()) != 0) + { + forwarderBytes.Add(b); + } + function.ForwarderName = Encoding.ASCII.GetString(forwarderBytes.ToArray()); + } + + exportedFunctions.Add(function); + } + + return exportedFunctions; } } diff --git a/X86Disassembler/PE/Parsers/FileHeaderParser.cs b/X86Disassembler/PE/Parsers/FileHeaderParser.cs index 6e4602a..4358815 100644 --- a/X86Disassembler/PE/Parsers/FileHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/FileHeaderParser.cs @@ -1,30 +1,27 @@ -using System.IO; +namespace X86Disassembler.PE.Parsers; -namespace X86Disassembler.PE.Parsers +/// +/// Parser for the File header of a PE file +/// +public class FileHeaderParser : IParser { /// - /// Parser for the File header of a PE file + /// Parse the File header from the binary reader /// - public class FileHeaderParser : IParser + /// The binary reader positioned at the start of the File header + /// The parsed File header + public FileHeader Parse(BinaryReader reader) { - /// - /// Parse the File header from the binary reader - /// - /// The binary reader positioned at the start of the File header - /// The parsed File header - public FileHeader Parse(BinaryReader reader) - { - FileHeader header = new FileHeader(); - - header.Machine = reader.ReadUInt16(); - header.NumberOfSections = reader.ReadUInt16(); - header.TimeDateStamp = reader.ReadUInt32(); - header.PointerToSymbolTable = reader.ReadUInt32(); - header.NumberOfSymbols = reader.ReadUInt32(); - header.SizeOfOptionalHeader = reader.ReadUInt16(); - header.Characteristics = reader.ReadUInt16(); - - return header; - } + FileHeader header = new FileHeader(); + + header.Machine = reader.ReadUInt16(); + header.NumberOfSections = reader.ReadUInt16(); + header.TimeDateStamp = reader.ReadUInt32(); + header.PointerToSymbolTable = reader.ReadUInt32(); + header.NumberOfSymbols = reader.ReadUInt32(); + header.SizeOfOptionalHeader = reader.ReadUInt16(); + header.Characteristics = reader.ReadUInt16(); + + return header; } } diff --git a/X86Disassembler/PE/Parsers/IParser.cs b/X86Disassembler/PE/Parsers/IParser.cs index 72e3b39..cb817e9 100644 --- a/X86Disassembler/PE/Parsers/IParser.cs +++ b/X86Disassembler/PE/Parsers/IParser.cs @@ -1,18 +1,15 @@ -using System.IO; +namespace X86Disassembler.PE.Parsers; -namespace X86Disassembler.PE.Parsers +/// +/// Interface for PE format component parsers +/// +/// The type of component to parse +public interface IParser { /// - /// Interface for PE format component parsers + /// Parse a component from the binary reader /// - /// The type of component to parse - public interface IParser - { - /// - /// Parse a component from the binary reader - /// - /// The binary reader positioned at the start of the component - /// The parsed component - T Parse(BinaryReader reader); - } + /// The binary reader positioned at the start of the component + /// The parsed component + T Parse(BinaryReader reader); } diff --git a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs index ed7f741..b6f2f11 100644 --- a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs +++ b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs @@ -1,192 +1,188 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Text; -namespace X86Disassembler.PE.Parsers +namespace X86Disassembler.PE.Parsers; + +/// +/// Parser for Import Descriptors in a PE file +/// +public class ImportDescriptorParser { - /// - /// Parser for Import Descriptors in a PE file - /// - public class ImportDescriptorParser + private readonly PEUtility _utility; + + public ImportDescriptorParser(PEUtility utility) { - private readonly PEUtility _utility; + _utility = utility; + } + + /// + /// Parse the Import Descriptors from the binary reader + /// + /// The binary reader + /// The RVA of the Import Directory + /// List of Import Descriptors + public List Parse(BinaryReader reader, uint rva) + { + List descriptors = new List(); - public ImportDescriptorParser(PEUtility utility) + try { - _utility = utility; + uint importTableOffset = _utility.RvaToOffset(rva); + reader.BaseStream.Seek(importTableOffset, SeekOrigin.Begin); + + int descriptorCount = 0; + + while (true) + { + descriptorCount++; + + // Read the import descriptor + uint originalFirstThunk = reader.ReadUInt32(); + uint timeDateStamp = reader.ReadUInt32(); + uint forwarderChain = reader.ReadUInt32(); + uint nameRva = reader.ReadUInt32(); + uint firstThunk = reader.ReadUInt32(); + + // Check if we've reached the end of the import descriptors + if (originalFirstThunk == 0 && nameRva == 0 && firstThunk == 0) + { + break; + } + + ImportDescriptor descriptor = new ImportDescriptor + { + OriginalFirstThunk = originalFirstThunk, + TimeDateStamp = timeDateStamp, + ForwarderChain = forwarderChain, + Name = nameRva, + FirstThunk = firstThunk, + DllName = "Unknown" // Default name in case we can't read it + }; + + // Try to read the DLL name + try + { + if (nameRva != 0) + { + uint nameOffset = _utility.RvaToOffset(nameRva); + reader.BaseStream.Seek(nameOffset, SeekOrigin.Begin); + + // Read the null-terminated ASCII string + StringBuilder nameBuilder = new StringBuilder(); + byte b; + + while ((b = reader.ReadByte()) != 0) + { + nameBuilder.Append((char)b); + } + + descriptor.DllName = nameBuilder.ToString(); + } + } + catch (Exception) + { + // If we can't read the name, keep the default "Unknown" + } + + // Parse the imported functions + ParseImportedFunctions(reader, descriptor); + + descriptors.Add(descriptor); + + // Return to the import table to read the next descriptor + reader.BaseStream.Seek(importTableOffset + (descriptorCount * 20), SeekOrigin.Begin); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing import descriptors: {ex.Message}"); + // Return whatever descriptors we've managed to parse } - /// - /// Parse the Import Descriptors from the binary reader - /// - /// The binary reader - /// The RVA of the Import Directory - /// List of Import Descriptors - public List Parse(BinaryReader reader, uint rva) + return descriptors; + } + + /// + /// Parse the imported functions for a given import descriptor + /// + /// The binary reader + /// The Import Descriptor + private void ParseImportedFunctions(BinaryReader reader, ImportDescriptor descriptor) + { + try { - List descriptors = new List(); + // Use OriginalFirstThunk if available, otherwise use FirstThunk + uint thunkRva = descriptor.OriginalFirstThunk != 0 ? descriptor.OriginalFirstThunk : descriptor.FirstThunk; - try + if (thunkRva == 0) { - uint importTableOffset = _utility.RvaToOffset(rva); - reader.BaseStream.Seek(importTableOffset, SeekOrigin.Begin); + return; // No functions to parse + } + + uint thunkOffset = _utility.RvaToOffset(thunkRva); + int functionCount = 0; + + while (true) + { + reader.BaseStream.Seek(thunkOffset + (functionCount * 4), SeekOrigin.Begin); + uint thunkData = reader.ReadUInt32(); - int descriptorCount = 0; - - while (true) + if (thunkData == 0) { - descriptorCount++; - - // Read the import descriptor - uint originalFirstThunk = reader.ReadUInt32(); - uint timeDateStamp = reader.ReadUInt32(); - uint forwarderChain = reader.ReadUInt32(); - uint nameRva = reader.ReadUInt32(); - uint firstThunk = reader.ReadUInt32(); - - // Check if we've reached the end of the import descriptors - if (originalFirstThunk == 0 && nameRva == 0 && firstThunk == 0) - { - break; - } - - ImportDescriptor descriptor = new ImportDescriptor - { - OriginalFirstThunk = originalFirstThunk, - TimeDateStamp = timeDateStamp, - ForwarderChain = forwarderChain, - Name = nameRva, - FirstThunk = firstThunk, - DllName = "Unknown" // Default name in case we can't read it - }; - - // Try to read the DLL name + break; // End of the function list + } + + ImportedFunction function = new ImportedFunction + { + ThunkRVA = thunkRva + (uint)(functionCount * 4) + }; + + // Check if imported by ordinal (high bit set) + if ((thunkData & 0x80000000) != 0) + { + function.IsOrdinal = true; + function.Ordinal = (ushort)(thunkData & 0xFFFF); + function.Name = $"Ordinal {function.Ordinal}"; + } + else + { + // Imported by name - the thunkData is an RVA to a hint/name structure try { - if (nameRva != 0) + uint hintNameOffset = _utility.RvaToOffset(thunkData); + reader.BaseStream.Seek(hintNameOffset, SeekOrigin.Begin); + + // Read the hint (2 bytes) + function.Hint = reader.ReadUInt16(); + + // Read the function name (null-terminated ASCII string) + StringBuilder nameBuilder = new StringBuilder(); + byte b; + + while ((b = reader.ReadByte()) != 0) { - uint nameOffset = _utility.RvaToOffset(nameRva); - reader.BaseStream.Seek(nameOffset, SeekOrigin.Begin); - - // Read the null-terminated ASCII string - StringBuilder nameBuilder = new StringBuilder(); - byte b; - - while ((b = reader.ReadByte()) != 0) - { - nameBuilder.Append((char)b); - } - - descriptor.DllName = nameBuilder.ToString(); + nameBuilder.Append((char)b); } - } - catch (Exception) - { - // If we can't read the name, keep the default "Unknown" - } - - // Parse the imported functions - ParseImportedFunctions(reader, descriptor); - - descriptors.Add(descriptor); - - // Return to the import table to read the next descriptor - reader.BaseStream.Seek(importTableOffset + (descriptorCount * 20), SeekOrigin.Begin); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error parsing import descriptors: {ex.Message}"); - // Return whatever descriptors we've managed to parse - } - - return descriptors; - } - - /// - /// Parse the imported functions for a given import descriptor - /// - /// The binary reader - /// The Import Descriptor - private void ParseImportedFunctions(BinaryReader reader, ImportDescriptor descriptor) - { - try - { - // Use OriginalFirstThunk if available, otherwise use FirstThunk - uint thunkRva = descriptor.OriginalFirstThunk != 0 ? descriptor.OriginalFirstThunk : descriptor.FirstThunk; - - if (thunkRva == 0) - { - return; // No functions to parse - } - - uint thunkOffset = _utility.RvaToOffset(thunkRva); - int functionCount = 0; - - while (true) - { - reader.BaseStream.Seek(thunkOffset + (functionCount * 4), SeekOrigin.Begin); - uint thunkData = reader.ReadUInt32(); - - if (thunkData == 0) - { - break; // End of the function list - } - - ImportedFunction function = new ImportedFunction - { - ThunkRVA = thunkRva + (uint)(functionCount * 4) - }; - - // Check if imported by ordinal (high bit set) - if ((thunkData & 0x80000000) != 0) - { - function.IsOrdinal = true; - function.Ordinal = (ushort)(thunkData & 0xFFFF); - function.Name = $"Ordinal {function.Ordinal}"; - } - else - { - // Imported by name - the thunkData is an RVA to a hint/name structure - try - { - uint hintNameOffset = _utility.RvaToOffset(thunkData); - reader.BaseStream.Seek(hintNameOffset, SeekOrigin.Begin); - - // Read the hint (2 bytes) - function.Hint = reader.ReadUInt16(); - - // Read the function name (null-terminated ASCII string) - StringBuilder nameBuilder = new StringBuilder(); - byte b; - - while ((b = reader.ReadByte()) != 0) - { - nameBuilder.Append((char)b); - } - - function.Name = nameBuilder.ToString(); - - if (string.IsNullOrEmpty(function.Name)) - { - function.Name = $"Function_at_{thunkData:X8}"; - } - } - catch (Exception) + + function.Name = nameBuilder.ToString(); + + if (string.IsNullOrEmpty(function.Name)) { function.Name = $"Function_at_{thunkData:X8}"; } } - - descriptor.Functions.Add(function); - functionCount++; + catch (Exception) + { + function.Name = $"Function_at_{thunkData:X8}"; + } } + + descriptor.Functions.Add(function); + functionCount++; } - catch (Exception ex) - { - Console.WriteLine($"Error parsing imported functions for {descriptor.DllName}: {ex.Message}"); - } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing imported functions for {descriptor.DllName}: {ex.Message}"); } } } diff --git a/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs b/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs index c1f1a0a..7f75a51 100644 --- a/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs @@ -1,114 +1,110 @@ -using System; -using System.IO; +namespace X86Disassembler.PE.Parsers; -namespace X86Disassembler.PE.Parsers +/// +/// Parser for the Optional header of a PE file +/// +public class OptionalHeaderParser : IParser { + // Optional Header Magic values + private const ushort PE32_MAGIC = 0x10B; // 32-bit executable + private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable + /// - /// Parser for the Optional header of a PE file + /// Parse the Optional header from the binary reader /// - public class OptionalHeaderParser : IParser + /// The binary reader positioned at the start of the Optional header + /// The parsed Optional header + public OptionalHeader Parse(BinaryReader reader) { - // Optional Header Magic values - private const ushort PE32_MAGIC = 0x10B; // 32-bit executable - private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable - - /// - /// Parse the Optional header from the binary reader - /// - /// The binary reader positioned at the start of the Optional header - /// The parsed Optional header - public OptionalHeader Parse(BinaryReader reader) + OptionalHeader header = new OptionalHeader(); + bool is64Bit; + + // Standard fields + header.Magic = reader.ReadUInt16(); + + // Determine if this is a PE32 or PE32+ file + is64Bit = header.Magic == PE32PLUS_MAGIC; + + header.MajorLinkerVersion = reader.ReadByte(); + header.MinorLinkerVersion = reader.ReadByte(); + header.SizeOfCode = reader.ReadUInt32(); + header.SizeOfInitializedData = reader.ReadUInt32(); + header.SizeOfUninitializedData = reader.ReadUInt32(); + header.AddressOfEntryPoint = reader.ReadUInt32(); + header.BaseOfCode = reader.ReadUInt32(); + + // PE32 has BaseOfData, PE32+ doesn't + if (!is64Bit) { - OptionalHeader header = new OptionalHeader(); - bool is64Bit; - - // Standard fields - header.Magic = reader.ReadUInt16(); - - // Determine if this is a PE32 or PE32+ file - is64Bit = header.Magic == PE32PLUS_MAGIC; - - header.MajorLinkerVersion = reader.ReadByte(); - header.MinorLinkerVersion = reader.ReadByte(); - header.SizeOfCode = reader.ReadUInt32(); - header.SizeOfInitializedData = reader.ReadUInt32(); - header.SizeOfUninitializedData = reader.ReadUInt32(); - header.AddressOfEntryPoint = reader.ReadUInt32(); - header.BaseOfCode = reader.ReadUInt32(); - - // PE32 has BaseOfData, PE32+ doesn't - if (!is64Bit) - { - header.BaseOfData = reader.ReadUInt32(); - } - - // Windows-specific fields - if (is64Bit) - { - header.ImageBase = reader.ReadUInt64(); - } - else - { - header.ImageBase = reader.ReadUInt32(); - } - - header.SectionAlignment = reader.ReadUInt32(); - header.FileAlignment = reader.ReadUInt32(); - header.MajorOperatingSystemVersion = reader.ReadUInt16(); - header.MinorOperatingSystemVersion = reader.ReadUInt16(); - header.MajorImageVersion = reader.ReadUInt16(); - header.MinorImageVersion = reader.ReadUInt16(); - header.MajorSubsystemVersion = reader.ReadUInt16(); - header.MinorSubsystemVersion = reader.ReadUInt16(); - header.Win32VersionValue = reader.ReadUInt32(); - header.SizeOfImage = reader.ReadUInt32(); - header.SizeOfHeaders = reader.ReadUInt32(); - header.CheckSum = reader.ReadUInt32(); - header.Subsystem = reader.ReadUInt16(); - header.DllCharacteristics = reader.ReadUInt16(); - - // Size fields differ between PE32 and PE32+ - if (is64Bit) - { - header.SizeOfStackReserve = reader.ReadUInt64(); - header.SizeOfStackCommit = reader.ReadUInt64(); - header.SizeOfHeapReserve = reader.ReadUInt64(); - header.SizeOfHeapCommit = reader.ReadUInt64(); - } - else - { - header.SizeOfStackReserve = reader.ReadUInt32(); - header.SizeOfStackCommit = reader.ReadUInt32(); - header.SizeOfHeapReserve = reader.ReadUInt32(); - header.SizeOfHeapCommit = reader.ReadUInt32(); - } - - header.LoaderFlags = reader.ReadUInt32(); - header.NumberOfRvaAndSizes = reader.ReadUInt32(); - - // Data directories - int numDirectories = (int)Math.Min(header.NumberOfRvaAndSizes, 16); // Maximum of 16 directories - header.DataDirectories = new DataDirectory[numDirectories]; - - for (int i = 0; i < numDirectories; i++) - { - DataDirectory dir = new DataDirectory(); - dir.VirtualAddress = reader.ReadUInt32(); - dir.Size = reader.ReadUInt32(); - header.DataDirectories[i] = dir; - } - - return header; + header.BaseOfData = reader.ReadUInt32(); } - - /// - /// Determines if the PE file is 64-bit based on the Optional header - /// - /// The Optional header - /// True if the PE file is 64-bit, false otherwise - public bool Is64Bit(OptionalHeader header) + + // Windows-specific fields + if (is64Bit) { - return header.Magic == PE32PLUS_MAGIC; + header.ImageBase = reader.ReadUInt64(); } + else + { + header.ImageBase = reader.ReadUInt32(); + } + + header.SectionAlignment = reader.ReadUInt32(); + header.FileAlignment = reader.ReadUInt32(); + header.MajorOperatingSystemVersion = reader.ReadUInt16(); + header.MinorOperatingSystemVersion = reader.ReadUInt16(); + header.MajorImageVersion = reader.ReadUInt16(); + header.MinorImageVersion = reader.ReadUInt16(); + header.MajorSubsystemVersion = reader.ReadUInt16(); + header.MinorSubsystemVersion = reader.ReadUInt16(); + header.Win32VersionValue = reader.ReadUInt32(); + header.SizeOfImage = reader.ReadUInt32(); + header.SizeOfHeaders = reader.ReadUInt32(); + header.CheckSum = reader.ReadUInt32(); + header.Subsystem = reader.ReadUInt16(); + header.DllCharacteristics = reader.ReadUInt16(); + + // Size fields differ between PE32 and PE32+ + if (is64Bit) + { + header.SizeOfStackReserve = reader.ReadUInt64(); + header.SizeOfStackCommit = reader.ReadUInt64(); + header.SizeOfHeapReserve = reader.ReadUInt64(); + header.SizeOfHeapCommit = reader.ReadUInt64(); + } + else + { + header.SizeOfStackReserve = reader.ReadUInt32(); + header.SizeOfStackCommit = reader.ReadUInt32(); + header.SizeOfHeapReserve = reader.ReadUInt32(); + header.SizeOfHeapCommit = reader.ReadUInt32(); + } + + header.LoaderFlags = reader.ReadUInt32(); + header.NumberOfRvaAndSizes = reader.ReadUInt32(); + + // Data directories + int numDirectories = (int)Math.Min(header.NumberOfRvaAndSizes, 16); // Maximum of 16 directories + header.DataDirectories = new DataDirectory[numDirectories]; + + for (int i = 0; i < numDirectories; i++) + { + DataDirectory dir = new DataDirectory(); + dir.VirtualAddress = reader.ReadUInt32(); + dir.Size = reader.ReadUInt32(); + header.DataDirectories[i] = dir; + } + + return header; } -} + + /// + /// Determines if the PE file is 64-bit based on the Optional header + /// + /// The Optional header + /// True if the PE file is 64-bit, false otherwise + public bool Is64Bit(OptionalHeader header) + { + return header.Magic == PE32PLUS_MAGIC; + } +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/SectionHeaderParser.cs b/X86Disassembler/PE/Parsers/SectionHeaderParser.cs index 8106c65..d55f358 100644 --- a/X86Disassembler/PE/Parsers/SectionHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/SectionHeaderParser.cs @@ -1,38 +1,36 @@ -using System.IO; using System.Text; -namespace X86Disassembler.PE.Parsers +namespace X86Disassembler.PE.Parsers; + +/// +/// Parser for section headers in a PE file +/// +public class SectionHeaderParser : IParser { /// - /// Parser for section headers in a PE file + /// Parse a section header from the binary reader /// - public class SectionHeaderParser : IParser + /// The binary reader positioned at the start of the section header + /// The parsed section header + public SectionHeader Parse(BinaryReader reader) { - /// - /// Parse a section header from the binary reader - /// - /// The binary reader positioned at the start of the section header - /// The parsed section header - public SectionHeader Parse(BinaryReader reader) - { - SectionHeader header = new SectionHeader(); - - // Read section name (8 bytes) - byte[] nameBytes = reader.ReadBytes(8); - // Convert to string, removing any null characters - header.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0'); - - header.VirtualSize = reader.ReadUInt32(); - header.VirtualAddress = reader.ReadUInt32(); - header.SizeOfRawData = reader.ReadUInt32(); - header.PointerToRawData = reader.ReadUInt32(); - header.PointerToRelocations = reader.ReadUInt32(); - header.PointerToLinenumbers = reader.ReadUInt32(); - header.NumberOfRelocations = reader.ReadUInt16(); - header.NumberOfLinenumbers = reader.ReadUInt16(); - header.Characteristics = reader.ReadUInt32(); - - return header; - } + SectionHeader header = new SectionHeader(); + + // Read section name (8 bytes) + byte[] nameBytes = reader.ReadBytes(8); + // Convert to string, removing any null characters + header.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0'); + + header.VirtualSize = reader.ReadUInt32(); + header.VirtualAddress = reader.ReadUInt32(); + header.SizeOfRawData = reader.ReadUInt32(); + header.PointerToRawData = reader.ReadUInt32(); + header.PointerToRelocations = reader.ReadUInt32(); + header.PointerToLinenumbers = reader.ReadUInt32(); + header.NumberOfRelocations = reader.ReadUInt16(); + header.NumberOfLinenumbers = reader.ReadUInt16(); + header.Characteristics = reader.ReadUInt32(); + + return header; } } diff --git a/X86Disassembler/PE/SectionHeader.cs b/X86Disassembler/PE/SectionHeader.cs index 62cfb9a..f2fac15 100644 --- a/X86Disassembler/PE/SectionHeader.cs +++ b/X86Disassembler/PE/SectionHeader.cs @@ -1,71 +1,70 @@ -namespace X86Disassembler.PE +namespace X86Disassembler.PE; + +/// +/// Represents a section header in a PE file +/// +public class SectionHeader { + // Section characteristics flags + private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code + private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable + private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable + private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable + + public string Name; // Section name + public uint VirtualSize; // Virtual size + public uint VirtualAddress; // Virtual address + public uint SizeOfRawData; // Size of raw data + public uint PointerToRawData; // Pointer to raw data + public uint PointerToRelocations; // Pointer to relocations + public uint PointerToLinenumbers; // Pointer to line numbers + public ushort NumberOfRelocations; // Number of relocations + public ushort NumberOfLinenumbers; // Number of line numbers + public uint Characteristics; // Characteristics + /// - /// Represents a section header in a PE file + /// Initializes a new instance of the SectionHeader class /// - public class SectionHeader + public SectionHeader() { - // Section characteristics flags - private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code - private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable - private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable - private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable - - public string Name; // Section name - public uint VirtualSize; // Virtual size - public uint VirtualAddress; // Virtual address - public uint SizeOfRawData; // Size of raw data - public uint PointerToRawData; // Pointer to raw data - public uint PointerToRelocations; // Pointer to relocations - public uint PointerToLinenumbers; // Pointer to line numbers - public ushort NumberOfRelocations; // Number of relocations - public ushort NumberOfLinenumbers; // Number of line numbers - public uint Characteristics; // Characteristics - - /// - /// Initializes a new instance of the SectionHeader class - /// - public SectionHeader() - { - // Initialize string field to avoid nullability warning - Name = string.Empty; - } - - /// - /// Checks if the section contains code - /// - /// True if the section contains code, false otherwise - public bool ContainsCode() - { - return (Characteristics & IMAGE_SCN_CNT_CODE) != 0 || - (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; - } - - /// - /// Checks if the section is readable - /// - /// True if the section is readable, false otherwise - public bool IsReadable() - { - return (Characteristics & IMAGE_SCN_MEM_READ) != 0; - } - - /// - /// Checks if the section is writable - /// - /// True if the section is writable, false otherwise - public bool IsWritable() - { - return (Characteristics & IMAGE_SCN_MEM_WRITE) != 0; - } - - /// - /// Checks if the section is executable - /// - /// True if the section is executable, false otherwise - public bool IsExecutable() - { - return (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; - } + // Initialize string field to avoid nullability warning + Name = string.Empty; } -} + + /// + /// Checks if the section contains code + /// + /// True if the section contains code, false otherwise + public bool ContainsCode() + { + return (Characteristics & IMAGE_SCN_CNT_CODE) != 0 || + (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + } + + /// + /// Checks if the section is readable + /// + /// True if the section is readable, false otherwise + public bool IsReadable() + { + return (Characteristics & IMAGE_SCN_MEM_READ) != 0; + } + + /// + /// Checks if the section is writable + /// + /// True if the section is writable, false otherwise + public bool IsWritable() + { + return (Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + } + + /// + /// Checks if the section is executable + /// + /// True if the section is executable, false otherwise + public bool IsExecutable() + { + return (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + } +} \ No newline at end of file diff --git a/X86Disassembler/Program.cs b/X86Disassembler/Program.cs index cacd76c..1077eeb 100644 --- a/X86Disassembler/Program.cs +++ b/X86Disassembler/Program.cs @@ -1,148 +1,145 @@ -using System; -using System.IO; using X86Disassembler.PE; -namespace X86Disassembler +namespace X86Disassembler; + +internal class Program { - internal 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 + + static void Main(string[] args) { - // 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 - - static void Main(string[] args) + Console.WriteLine("X86 Disassembler and Decompiler"); + Console.WriteLine("--------------------------------"); + + Console.WriteLine($"Loading file: {DllPath}"); + + // Load the DLL file + byte[] binaryData = File.ReadAllBytes(DllPath); + + Console.WriteLine($"Successfully loaded {DllPath}"); + Console.WriteLine($"File size: {binaryData.Length} bytes"); + + // Create the PE format parser + PEFormat peFile = new PEFormat(binaryData); + + // Parse the PE format + Console.WriteLine("\nParsing PE format..."); + if (!peFile.Parse()) { - Console.WriteLine("X86 Disassembler and Decompiler"); - Console.WriteLine("--------------------------------"); - - Console.WriteLine($"Loading file: {DllPath}"); - - // Load the DLL file - byte[] binaryData = File.ReadAllBytes(DllPath); - - Console.WriteLine($"Successfully loaded {DllPath}"); - Console.WriteLine($"File size: {binaryData.Length} bytes"); - - // Create the PE format parser - PEFormat peFile = new PEFormat(binaryData); - - // Parse the PE format - Console.WriteLine("\nParsing PE format..."); - if (!peFile.Parse()) - { - Console.WriteLine("Failed to parse PE file. Exiting."); - return; - } - - // Display basic PE information - DisplayPEInfo(peFile); - - // Display exported functions - DisplayExportedFunctions(peFile); - - // Display imported functions - DisplayImportedFunctions(peFile); - - // Find code sections for disassembly - var codeSections = peFile.GetCodeSections(); - Console.WriteLine($"\nFound {codeSections.Count} code section(s):"); - - foreach (int sectionIndex in codeSections) - { - var section = peFile.SectionHeaders[sectionIndex]; - Console.WriteLine($" - {section.Name}: Size={section.SizeOfRawData} bytes, RVA=0x{section.VirtualAddress:X8}"); - - // Get the section data for disassembly - byte[] sectionData = peFile.GetSectionData(sectionIndex); - - // TODO: Implement disassembling logic here - // This is where we would pass the section data to our disassembler - } - - Console.WriteLine("\nPress any key to exit..."); - Console.ReadKey(); + Console.WriteLine("Failed to parse PE file. Exiting."); + return; } - - private static void DisplayPEInfo(PEFormat peFile) - { - Console.WriteLine("\nPE File Information:"); - Console.WriteLine($"Architecture: {(peFile.Is64Bit ? "64-bit" : "32-bit")}"); - Console.WriteLine($"Entry Point: 0x{peFile.OptionalHeader.AddressOfEntryPoint:X8}"); - Console.WriteLine($"Image Base: 0x{peFile.OptionalHeader.ImageBase:X}"); - Console.WriteLine($"Number of Sections: {peFile.FileHeader.NumberOfSections}"); - // Display section information - Console.WriteLine("\nSections:"); - for (int i = 0; i < peFile.SectionHeaders.Count; i++) - { - var section = peFile.SectionHeaders[i]; - string flags = ""; + // Display basic PE information + DisplayPEInfo(peFile); + + // Display exported functions + DisplayExportedFunctions(peFile); + + // Display imported functions + DisplayImportedFunctions(peFile); + + // Find code sections for disassembly + var codeSections = peFile.GetCodeSections(); + Console.WriteLine($"\nFound {codeSections.Count} code section(s):"); + + foreach (int sectionIndex in codeSections) + { + var section = peFile.SectionHeaders[sectionIndex]; + Console.WriteLine($" - {section.Name}: Size={section.SizeOfRawData} bytes, RVA=0x{section.VirtualAddress:X8}"); - if ((section.Characteristics & 0x00000020) != 0) flags += "Code "; // IMAGE_SCN_CNT_CODE - if ((section.Characteristics & 0x20000000) != 0) flags += "Exec "; // IMAGE_SCN_MEM_EXECUTE - if ((section.Characteristics & 0x40000000) != 0) flags += "Read "; // IMAGE_SCN_MEM_READ - if ((section.Characteristics & 0x80000000) != 0) flags += "Write"; // IMAGE_SCN_MEM_WRITE + // Get the section data for disassembly + byte[] sectionData = peFile.GetSectionData(sectionIndex); - Console.WriteLine($" {i}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.SizeOfRawData,-8} [{flags}]"); - } + // TODO: Implement disassembling logic here + // This is where we would pass the section data to our disassembler } + + Console.WriteLine("\nPress any key to exit..."); + Console.ReadKey(); + } - private static void DisplayExportedFunctions(PEFormat peFile) + private static void DisplayPEInfo(PEFormat peFile) + { + Console.WriteLine("\nPE File Information:"); + Console.WriteLine($"Architecture: {(peFile.Is64Bit ? "64-bit" : "32-bit")}"); + Console.WriteLine($"Entry Point: 0x{peFile.OptionalHeader.AddressOfEntryPoint:X8}"); + Console.WriteLine($"Image Base: 0x{peFile.OptionalHeader.ImageBase:X}"); + Console.WriteLine($"Number of Sections: {peFile.FileHeader.NumberOfSections}"); + + // Display section information + Console.WriteLine("\nSections:"); + for (int i = 0; i < peFile.SectionHeaders.Count; i++) { - if (peFile.ExportDirectory == null) - { - Console.WriteLine("\nNo exported functions found."); - return; - } - - Console.WriteLine("\nExported Functions:"); - Console.WriteLine($"DLL Name: {peFile.ExportDirectory.DllName}"); - Console.WriteLine($"Number of Functions: {peFile.ExportDirectory.NumberOfFunctions}"); - Console.WriteLine($"Number of Names: {peFile.ExportDirectory.NumberOfNames}"); - - // Display all exported functions - for (int i = 0; i < peFile.ExportedFunctions.Count; i++) - { - var function = peFile.ExportedFunctions[i]; - Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.Address:X8})"); - } - } - - private static void DisplayImportedFunctions(PEFormat peFile) - { - if (peFile.ImportDescriptors.Count == 0) - { - Console.WriteLine("\nNo imported functions found."); - return; - } - - Console.WriteLine("\nImported Functions:"); - Console.WriteLine($"Number of Imported DLLs: {peFile.ImportDescriptors.Count}"); - - // Display all imported DLLs and their functions - for (int i = 0; i < peFile.ImportDescriptors.Count; i++) - { - var descriptor = peFile.ImportDescriptors[i]; - Console.WriteLine($" DLL: {descriptor.DllName}"); + var section = peFile.SectionHeaders[i]; + string flags = ""; - // Display all functions from this DLL - for (int j = 0; j < descriptor.Functions.Count; j++) + if ((section.Characteristics & 0x00000020) != 0) flags += "Code "; // IMAGE_SCN_CNT_CODE + if ((section.Characteristics & 0x20000000) != 0) flags += "Exec "; // IMAGE_SCN_MEM_EXECUTE + if ((section.Characteristics & 0x40000000) != 0) flags += "Read "; // IMAGE_SCN_MEM_READ + if ((section.Characteristics & 0x80000000) != 0) flags += "Write"; // IMAGE_SCN_MEM_WRITE + + Console.WriteLine($" {i}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.SizeOfRawData,-8} [{flags}]"); + } + } + + private static void DisplayExportedFunctions(PEFormat peFile) + { + if (peFile.ExportDirectory == null) + { + Console.WriteLine("\nNo exported functions found."); + return; + } + + Console.WriteLine("\nExported Functions:"); + Console.WriteLine($"DLL Name: {peFile.ExportDirectory.DllName}"); + Console.WriteLine($"Number of Functions: {peFile.ExportDirectory.NumberOfFunctions}"); + Console.WriteLine($"Number of Names: {peFile.ExportDirectory.NumberOfNames}"); + + // Display all exported functions + for (int i = 0; i < peFile.ExportedFunctions.Count; i++) + { + var function = peFile.ExportedFunctions[i]; + Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.Address:X8})"); + } + } + + private static void DisplayImportedFunctions(PEFormat peFile) + { + if (peFile.ImportDescriptors.Count == 0) + { + Console.WriteLine("\nNo imported functions found."); + return; + } + + Console.WriteLine("\nImported Functions:"); + Console.WriteLine($"Number of Imported DLLs: {peFile.ImportDescriptors.Count}"); + + // Display all imported DLLs and their functions + for (int i = 0; i < peFile.ImportDescriptors.Count; i++) + { + var descriptor = peFile.ImportDescriptors[i]; + Console.WriteLine($" DLL: {descriptor.DllName}"); + + // Display all functions from this DLL + for (int j = 0; j < descriptor.Functions.Count; j++) + { + var function = descriptor.Functions[j]; + if (function.IsOrdinal) { - var function = descriptor.Functions[j]; - if (function.IsOrdinal) - { - Console.WriteLine($" {j}: Ordinal {function.Ordinal}"); - } - else - { - Console.WriteLine($" {j}: {function.Name} (Hint={function.Hint})"); - } + Console.WriteLine($" {j}: Ordinal {function.Ordinal}"); } - - if (i < peFile.ImportDescriptors.Count - 1) + else { - Console.WriteLine(); // Add a blank line between DLLs for better readability + Console.WriteLine($" {j}: {function.Name} (Hint={function.Hint})"); } } + + if (i < peFile.ImportDescriptors.Count - 1) + { + Console.WriteLine(); // Add a blank line between DLLs for better readability + } } } -} +} \ No newline at end of file