diff --git a/X86Disassembler/PE/DOSHeader.cs b/X86Disassembler/PE/DOSHeader.cs deleted file mode 100644 index 9e321bb..0000000 --- a/X86Disassembler/PE/DOSHeader.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// 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 - - /// - /// 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/DataDirectory.cs b/X86Disassembler/PE/DataDirectory.cs deleted file mode 100644 index c16221b..0000000 --- a/X86Disassembler/PE/DataDirectory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace X86Disassembler.PE; -/// -/// Represents a data directory in the optional header -/// -public class DataDirectory -{ - public uint VirtualAddress; // RVA of the table - public uint Size; // Size of the table -} diff --git a/X86Disassembler/PE/ExportDirectory.cs b/X86Disassembler/PE/ExportDirectory.cs deleted file mode 100644 index f6aec2d..0000000 --- a/X86Disassembler/PE/ExportDirectory.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// 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 DllNameRva; // 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; - } -} \ No newline at end of file diff --git a/X86Disassembler/PE/ExportedFunction.cs b/X86Disassembler/PE/ExportedFunction.cs deleted file mode 100644 index ec65763..0000000 --- a/X86Disassembler/PE/ExportedFunction.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// Represents an exported function in a PE file -/// -public class ExportedFunction -{ - public string Name; // Function name - public ushort Ordinal; // Function ordinal - public uint AddressRva; // Function RVA - public bool IsForwarder; // True if this is a forwarder - public string ForwarderName; // Name of the forwarded function - - /// - /// Initializes a new instance of the ExportedFunction class - /// - public ExportedFunction() - { - // Initialize string fields to avoid nullability warnings - Name = string.Empty; - ForwarderName = string.Empty; - } -} diff --git a/X86Disassembler/PE/FileHeader.cs b/X86Disassembler/PE/FileHeader.cs deleted file mode 100644 index 24364f8..0000000 --- a/X86Disassembler/PE/FileHeader.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// 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 -} \ No newline at end of file diff --git a/X86Disassembler/PE/ImportDescriptor.cs b/X86Disassembler/PE/ImportDescriptor.cs deleted file mode 100644 index 69366b9..0000000 --- a/X86Disassembler/PE/ImportDescriptor.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// Represents an Import Descriptor in a PE file -/// -public class ImportDescriptor -{ - public uint OriginalFirstThunkRva; // RVA to original first thunk - public uint TimeDateStamp; // Time and date stamp - public uint ForwarderChain; // Forwarder chain - public uint DllNameRva; // RVA to the name of the DLL - public string DllName; // The actual name of the DLL - public uint FirstThunkRva; // 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; - } -} \ No newline at end of file diff --git a/X86Disassembler/PE/ImportedFunction.cs b/X86Disassembler/PE/ImportedFunction.cs deleted file mode 100644 index b2b5370..0000000 --- a/X86Disassembler/PE/ImportedFunction.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace X86Disassembler.PE; - -/// -/// Represents an imported function in a PE file -/// -public class ImportedFunction -{ - public string Name; // Function name - public ushort Hint; // Hint value - public bool IsOrdinal; // True if imported by ordinal - public ushort Ordinal; // Ordinal value (if imported by ordinal) - public uint ThunkRva; // RVA of the thunk for this function - - /// - /// Initializes a new instance of the ImportedFunction class - /// - public ImportedFunction() - { - // Initialize string field to avoid nullability warning - Name = string.Empty; - } -} diff --git a/X86Disassembler/PE/OptionalHeader.cs b/X86Disassembler/PE/OptionalHeader.cs deleted file mode 100644 index 13ea623..0000000 --- a/X86Disassembler/PE/OptionalHeader.cs +++ /dev/null @@ -1,72 +0,0 @@ -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 ulong 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 ulong SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+) - public ulong SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+) - public ulong SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+) - public ulong 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 array to avoid nullability warning - DataDirectories = []; - } - - /// - /// 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/PEUtility.cs b/X86Disassembler/PE/PEUtility.cs index 8f9de86..0f7d9e2 100644 --- a/X86Disassembler/PE/PEUtility.cs +++ b/X86Disassembler/PE/PEUtility.cs @@ -1,3 +1,5 @@ +using X86Disassembler.PE.Types; + namespace X86Disassembler.PE; /// @@ -7,7 +9,7 @@ public class PEUtility { private readonly List _sectionHeaders; private readonly uint _sizeOfHeaders; - + /// /// Initialize a new instance of the PEUtility class /// @@ -18,7 +20,7 @@ public class PEUtility _sectionHeaders = sectionHeaders; _sizeOfHeaders = sizeOfHeaders; } - + /// /// Converts a Relative Virtual Address (RVA) to a file offset /// @@ -30,7 +32,7 @@ public class PEUtility { return 0; } - + foreach (var section in _sectionHeaders) { // Check if the RVA is within this section @@ -38,7 +40,7 @@ public class PEUtility { // 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) { @@ -46,13 +48,13 @@ public class PEUtility } } } - + // 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 9699920..5b38a3b 100644 --- a/X86Disassembler/PE/Parsers/DOSHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/DOSHeaderParser.cs @@ -1,3 +1,5 @@ +using X86Disassembler.PE.Types; + namespace X86Disassembler.PE.Parsers; /// @@ -6,23 +8,18 @@ namespace X86Disassembler.PE.Parsers; public class DOSHeaderParser : IParser { // DOS Header constants - private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' - - /// - /// Parse the DOS header from the binary reader - /// - /// The binary reader positioned at the start of the DOS header - /// The parsed DOS header + private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' + public DOSHeader Parse(BinaryReader reader) { - DOSHeader header = new DOSHeader(); - + var 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(); @@ -36,24 +33,24 @@ public class DOSHeaderParser : IParser 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; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs index c8ba43f..c7a693d 100644 --- a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs +++ b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs @@ -1,4 +1,5 @@ using System.Text; +using X86Disassembler.PE.Types; namespace X86Disassembler.PE.Parsers; @@ -8,12 +9,12 @@ namespace X86Disassembler.PE.Parsers; public class ExportDirectoryParser { private readonly PEUtility _utility; - + public ExportDirectoryParser(PEUtility utility) { _utility = utility; } - + /// /// Parse the Export Directory from the binary reader /// @@ -23,9 +24,9 @@ public class ExportDirectoryParser 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(); @@ -37,33 +38,24 @@ public class ExportDirectoryParser directory.AddressOfFunctions = reader.ReadUInt32(); directory.AddressOfNames = reader.ReadUInt32(); directory.AddressOfNameOrdinals = reader.ReadUInt32(); - - // Read the DLL name - try + + uint dllNameOffset = _utility.RvaToOffset(directory.DllNameRva); + reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin); + + // Read the null-terminated ASCII string + var nameBuilder = new StringBuilder(); + byte b; + + while ((b = reader.ReadByte()) != 0) { - uint dllNameRVA = directory.DllNameRva; - 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(); + nameBuilder.Append((char) b); } - catch (Exception) - { - directory.DllName = "Unknown"; - } - + + directory.DllName = nameBuilder.ToString(); + return directory; } - + /// /// Parse the exported functions using the export directory information /// @@ -75,12 +67,7 @@ public class ExportDirectoryParser public List ParseExportedFunctions(BinaryReader reader, ExportDirectory directory, uint exportDirRva, uint exportDirSize) { 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); @@ -88,7 +75,7 @@ public class ExportDirectoryParser { 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); @@ -96,7 +83,7 @@ public class ExportDirectoryParser { 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); @@ -104,25 +91,26 @@ public class ExportDirectoryParser { 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(); + var nameBuilder = new StringBuilder(); byte b; while ((b = reader.ReadByte()) != 0) { - nameBytes.Add(b); + nameBuilder.Append((char) b); } - string name = Encoding.ASCII.GetString(nameBytes.ToArray()); - + + string name = nameBuilder.ToString(); + // Map the ordinal to the name ordinalToName[nameOrdinals[i]] = name; } - + // Create the exported functions for (ushort i = 0; i < directory.NumberOfFunctions; i++) { @@ -131,42 +119,43 @@ public class ExportDirectoryParser { continue; // Skip empty entries } - + ExportedFunction function = new ExportedFunction(); - function.Ordinal = (ushort)(i + directory.Base); + function.Ordinal = (ushort) (i + directory.Base); function.AddressRva = functionRVA; - + // Check if this function has a name if (ordinalToName.TryGetValue(i, out string? name)) { - function.Name = name ?? $"Ordinal_{function.Ordinal}"; + function.Name = name; } 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(); + var forwarderBuilder = new StringBuilder(); byte b; while ((b = reader.ReadByte()) != 0) { - forwarderBytes.Add(b); + forwarderBuilder.Append((char) b); } - function.ForwarderName = Encoding.ASCII.GetString(forwarderBytes.ToArray()); + + function.ForwarderName = forwarderBuilder.ToString(); } - + exportedFunctions.Add(function); } - + return exportedFunctions; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/FileHeaderParser.cs b/X86Disassembler/PE/Parsers/FileHeaderParser.cs index 4358815..23d411b 100644 --- a/X86Disassembler/PE/Parsers/FileHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/FileHeaderParser.cs @@ -1,3 +1,5 @@ +using X86Disassembler.PE.Types; + namespace X86Disassembler.PE.Parsers; /// @@ -12,7 +14,7 @@ public class FileHeaderParser : IParser /// The parsed File header public FileHeader Parse(BinaryReader reader) { - FileHeader header = new FileHeader(); + var header = new FileHeader(); header.Machine = reader.ReadUInt16(); header.NumberOfSections = reader.ReadUInt16(); @@ -24,4 +26,4 @@ public class FileHeaderParser : IParser return header; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs index 50bedd8..3f60896 100644 --- a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs +++ b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs @@ -1,4 +1,5 @@ using System.Text; +using X86Disassembler.PE.Types; namespace X86Disassembler.PE.Parsers; @@ -8,12 +9,12 @@ namespace X86Disassembler.PE.Parsers; public class ImportDescriptorParser { private readonly PEUtility _utility; - + public ImportDescriptorParser(PEUtility utility) { _utility = utility; } - + /// /// Parse the Import Descriptors from the binary reader /// @@ -22,85 +23,69 @@ public class ImportDescriptorParser /// List of Import Descriptors public List Parse(BinaryReader reader, uint rva) { - List descriptors = new List(); - - try + var descriptors = new List(); + + uint importTableOffset = _utility.RvaToOffset(rva); + reader.BaseStream.Seek(importTableOffset, SeekOrigin.Begin); + + int descriptorCount = 0; + + while (true) { - 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) { - 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 - { - OriginalFirstThunkRva = originalFirstThunk, - TimeDateStamp = timeDateStamp, - ForwarderChain = forwarderChain, - DllNameRva = nameRva, - FirstThunkRva = 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); + break; } + + ImportDescriptor descriptor = new ImportDescriptor + { + OriginalFirstThunkRva = originalFirstThunk, + TimeDateStamp = timeDateStamp, + ForwarderChain = forwarderChain, + DllNameRva = nameRva, + FirstThunkRva = firstThunk, + DllName = "Unknown" + }; + + 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(); + } + + // 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 /// @@ -108,81 +93,70 @@ public class ImportDescriptorParser /// The Import Descriptor private void ParseImportedFunctions(BinaryReader reader, ImportDescriptor descriptor) { - try + // Use OriginalFirstThunk if available, otherwise use FirstThunk + uint thunkRva = descriptor.OriginalFirstThunkRva != 0 + ? descriptor.OriginalFirstThunkRva + : descriptor.FirstThunkRva; + + if (thunkRva == 0) { - // Use OriginalFirstThunk if available, otherwise use FirstThunk - uint thunkRva = descriptor.OriginalFirstThunkRva != 0 ? descriptor.OriginalFirstThunkRva : descriptor.FirstThunkRva; - - 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 = $"Function_at_{thunkData:X8}"; - } - } - - descriptor.Functions.Add(function); - functionCount++; - } + return; // No functions to parse } - catch (Exception ex) + + uint thunkOffset = _utility.RvaToOffset(thunkRva); + int functionCount = 0; + + while (true) { - Console.WriteLine($"Error parsing imported functions for {descriptor.DllName}: {ex.Message}"); + 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 + + 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}"; + } + } + + descriptor.Functions.Add(function); + functionCount++; } } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs b/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs index 7f75a51..7d89c7d 100644 --- a/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/OptionalHeaderParser.cs @@ -1,3 +1,5 @@ +using X86Disassembler.PE.Types; + namespace X86Disassembler.PE.Parsers; /// @@ -6,9 +8,9 @@ namespace X86Disassembler.PE.Parsers; 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 - + 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 /// @@ -16,15 +18,14 @@ public class OptionalHeaderParser : IParser /// The parsed Optional header public OptionalHeader Parse(BinaryReader reader) { - OptionalHeader header = new OptionalHeader(); - bool is64Bit; - + var header = new OptionalHeader(); + // Standard fields header.Magic = reader.ReadUInt16(); - + // Determine if this is a PE32 or PE32+ file - is64Bit = header.Magic == PE32PLUS_MAGIC; - + var is64Bit = header.Magic == PE32PLUS_MAGIC; + header.MajorLinkerVersion = reader.ReadByte(); header.MinorLinkerVersion = reader.ReadByte(); header.SizeOfCode = reader.ReadUInt32(); @@ -32,23 +33,18 @@ public class OptionalHeaderParser : IParser 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.ImageBase = is64Bit + ? reader.ReadUInt64() + : reader.ReadUInt32(); + header.SectionAlignment = reader.ReadUInt32(); header.FileAlignment = reader.ReadUInt32(); header.MajorOperatingSystemVersion = reader.ReadUInt16(); @@ -63,7 +59,7 @@ public class OptionalHeaderParser : IParser header.CheckSum = reader.ReadUInt32(); header.Subsystem = reader.ReadUInt16(); header.DllCharacteristics = reader.ReadUInt16(); - + // Size fields differ between PE32 and PE32+ if (is64Bit) { @@ -79,32 +75,21 @@ public class OptionalHeaderParser : IParser 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++) + header.DataDirectories = new DataDirectory[header.NumberOfRvaAndSizes]; + + for (int i = 0; i < header.NumberOfRvaAndSizes; i++) { - DataDirectory dir = new DataDirectory(); + var 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 d55f358..a9ba822 100644 --- a/X86Disassembler/PE/Parsers/SectionHeaderParser.cs +++ b/X86Disassembler/PE/Parsers/SectionHeaderParser.cs @@ -1,4 +1,5 @@ using System.Text; +using X86Disassembler.PE.Types; namespace X86Disassembler.PE.Parsers; @@ -14,13 +15,14 @@ public class SectionHeaderParser : IParser /// The parsed section header public SectionHeader Parse(BinaryReader reader) { - SectionHeader header = new SectionHeader(); - + var header = new SectionHeader(); + // Read section name (8 bytes) - byte[] nameBytes = reader.ReadBytes(8); + var nameBytes = reader.ReadBytes(8); // Convert to string, removing any null characters - header.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0'); - + header.Name = Encoding.ASCII.GetString(nameBytes) + .TrimEnd('\0'); + header.VirtualSize = reader.ReadUInt32(); header.VirtualAddress = reader.ReadUInt32(); header.SizeOfRawData = reader.ReadUInt32(); @@ -30,7 +32,7 @@ public class SectionHeaderParser : IParser header.NumberOfRelocations = reader.ReadUInt16(); header.NumberOfLinenumbers = reader.ReadUInt16(); header.Characteristics = reader.ReadUInt32(); - + return header; } -} +} \ No newline at end of file diff --git a/X86Disassembler/PE/PeFile.cs b/X86Disassembler/PE/PeFile.cs index 684cc38..e5e8a37 100644 --- a/X86Disassembler/PE/PeFile.cs +++ b/X86Disassembler/PE/PeFile.cs @@ -1,4 +1,5 @@ using X86Disassembler.PE.Parsers; +using X86Disassembler.PE.Types; namespace X86Disassembler.PE; @@ -69,9 +70,9 @@ public class PeFile public PeFile(byte[] fileData) { _fileData = fileData; - SectionHeaders = new List(); - ExportedFunctions = new List(); - ImportDescriptors = new List(); + SectionHeaders = []; + ExportedFunctions = []; + ImportDescriptors = []; // Initialize parsers _dosHeaderParser = new DOSHeaderParser(); @@ -94,150 +95,64 @@ public class PeFile /// /// Parses the PE file structure /// - /// True if parsing was successful, false otherwise - public bool Parse() + public void Parse() { - try + using var stream = new MemoryStream(_fileData); + using var 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) { - 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) - { - throw new InvalidDataException("Invalid PE signature"); - } - - // Parse File Header - FileHeader = _fileHeaderParser.Parse(reader); - - // Parse Optional Header - OptionalHeader = _optionalHeaderParser.Parse(reader); - Is64Bit = OptionalHeader.Is64Bit(); - - // 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; - - 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); - } - } - - return true; - } - catch (Exception ex) - { - 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)); + throw new InvalidDataException("Invalid PE signature"); } - SectionHeader section = SectionHeaders[sectionIndex]; - byte[] sectionData = new byte[section.SizeOfRawData]; + // Parse File Header + FileHeader = _fileHeaderParser.Parse(reader); - Array.Copy( - _fileData, - section.PointerToRawData, - sectionData, - 0, - section.SizeOfRawData - ); + // Parse Optional Header + OptionalHeader = _optionalHeaderParser.Parse(reader); + Is64Bit = OptionalHeader.Is64Bit(); - 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++) + // Parse Section Headers + for (int i = 0; i < FileHeader.NumberOfSections; i++) { - if (SectionHeaders[i].Name == sectionName) - { - return GetSectionData(i); - } + SectionHeaders.Add(_sectionHeaderParser.Parse(reader)); } - throw new ArgumentException($"Section '{sectionName}' not found"); - } + // Initialize utility after section headers are parsed + _peUtility = new PEUtility(SectionHeaders, OptionalHeader.SizeOfHeaders); + _exportDirectoryParser = new ExportDirectoryParser(_peUtility); + _importDescriptorParser = new ImportDescriptorParser(_peUtility); - /// - /// 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++) + // Parse Export Directory + if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_EXPORT && + OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress != 0) { - if (SectionHeaders[i] - .ContainsCode()) - { - codeSections.Add(i); - } + 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 + ); } - 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(); + // 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); + } } } \ No newline at end of file diff --git a/X86Disassembler/PE/SectionHeader.cs b/X86Disassembler/PE/SectionHeader.cs deleted file mode 100644 index f2fac15..0000000 --- a/X86Disassembler/PE/SectionHeader.cs +++ /dev/null @@ -1,70 +0,0 @@ -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 - - /// - /// 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; - } -} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/DOSHeader.cs b/X86Disassembler/PE/Types/DOSHeader.cs new file mode 100644 index 0000000..06e4a2d --- /dev/null +++ b/X86Disassembler/PE/Types/DOSHeader.cs @@ -0,0 +1,27 @@ +namespace X86Disassembler.PE.Types; + +/// +/// 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 = new ushort[4]; // Reserved words + public ushort e_oemid; // OEM identifier (for e_oeminfo) + public ushort e_oeminfo; // OEM information; e_oemid specific + public ushort[] e_res2 = new ushort[10]; // Reserved words + public uint e_lfanew; // File address of new exe header +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/DataDirectory.cs b/X86Disassembler/PE/Types/DataDirectory.cs new file mode 100644 index 0000000..93e70da --- /dev/null +++ b/X86Disassembler/PE/Types/DataDirectory.cs @@ -0,0 +1,10 @@ +namespace X86Disassembler.PE.Types; + +/// +/// Represents a data directory in the optional header +/// +public class DataDirectory +{ + public uint VirtualAddress; // RVA of the table + public uint Size; // Size of the table +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/ExportDirectory.cs b/X86Disassembler/PE/Types/ExportDirectory.cs new file mode 100644 index 0000000..1d670f8 --- /dev/null +++ b/X86Disassembler/PE/Types/ExportDirectory.cs @@ -0,0 +1,20 @@ +namespace X86Disassembler.PE.Types; + +/// +/// 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 DllNameRva; // 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 +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/ExportedFunction.cs b/X86Disassembler/PE/Types/ExportedFunction.cs new file mode 100644 index 0000000..6083a15 --- /dev/null +++ b/X86Disassembler/PE/Types/ExportedFunction.cs @@ -0,0 +1,13 @@ +namespace X86Disassembler.PE.Types; + +/// +/// Represents an exported function in a PE file +/// +public class ExportedFunction +{ + public string Name = ""; // Function name + public ushort Ordinal; // Function ordinal + public uint AddressRva; // Function RVA + public bool IsForwarder; // True if this is a forwarder + public string ForwarderName = ""; // Name of the forwarded function +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/FileHeader.cs b/X86Disassembler/PE/Types/FileHeader.cs new file mode 100644 index 0000000..7da8e2d --- /dev/null +++ b/X86Disassembler/PE/Types/FileHeader.cs @@ -0,0 +1,15 @@ +namespace X86Disassembler.PE.Types; + +/// +/// 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 +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/ImportDescriptor.cs b/X86Disassembler/PE/Types/ImportDescriptor.cs new file mode 100644 index 0000000..7728feb --- /dev/null +++ b/X86Disassembler/PE/Types/ImportDescriptor.cs @@ -0,0 +1,16 @@ +namespace X86Disassembler.PE.Types; + +/// +/// Represents an Import Descriptor in a PE file +/// +public class ImportDescriptor +{ + public uint OriginalFirstThunkRva; // RVA to original first thunk + public uint TimeDateStamp; // Time and date stamp + public uint ForwarderChain; // Forwarder chain + public uint DllNameRva; // RVA to the name of the DLL + public string DllName = ""; // The actual name of the DLL + public uint FirstThunkRva; // RVA to first thunk + + public List Functions = []; +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/ImportedFunction.cs b/X86Disassembler/PE/Types/ImportedFunction.cs new file mode 100644 index 0000000..e72c796 --- /dev/null +++ b/X86Disassembler/PE/Types/ImportedFunction.cs @@ -0,0 +1,13 @@ +namespace X86Disassembler.PE.Types; + +/// +/// Represents an imported function in a PE file +/// +public class ImportedFunction +{ + public string Name = ""; // Function name + public ushort Hint; // Hint value + public bool IsOrdinal; // True if imported by ordinal + public ushort Ordinal; // Ordinal value (if imported by ordinal) + public uint ThunkRva; // RVA of the thunk for this function +} \ No newline at end of file diff --git a/X86Disassembler/PE/Types/OptionalHeader.cs b/X86Disassembler/PE/Types/OptionalHeader.cs new file mode 100644 index 0000000..da4592f --- /dev/null +++ b/X86Disassembler/PE/Types/OptionalHeader.cs @@ -0,0 +1,56 @@ +namespace X86Disassembler.PE.Types; + +/// +/// 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 ulong 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 ulong SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+) + public ulong SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+) + public ulong SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+) + public ulong 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 + + /// + /// 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/Types/SectionHeader.cs b/X86Disassembler/PE/Types/SectionHeader.cs new file mode 100644 index 0000000..87867cb --- /dev/null +++ b/X86Disassembler/PE/Types/SectionHeader.cs @@ -0,0 +1,45 @@ +namespace X86Disassembler.PE.Types; + +/// +/// 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 + + public bool ContainsCode() + { + return (Characteristics & IMAGE_SCN_CNT_CODE) != 0 || + (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + } + + public bool IsReadable() + { + return (Characteristics & IMAGE_SCN_MEM_READ) != 0; + } + + public bool IsWritable() + { + return (Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + } + + 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 e321f0d..1d440e3 100644 --- a/X86Disassembler/Program.cs +++ b/X86Disassembler/Program.cs @@ -1,55 +1,19 @@ using X86Disassembler.Analysers; using X86Disassembler.PE; using X86Disassembler.ProjectSystem; -using X86Disassembler.X86; namespace X86Disassembler; -/// -/// Main program class -/// public class Program { - // Hardcoded file path for testing private const string FilePath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll"; - /// - /// Main entry point - /// - /// Command line arguments public static void Main(string[] args) { - Console.WriteLine("X86 Disassembler and Decompiler"); - Console.WriteLine("--------------------------------"); - - // Load the file - Console.WriteLine($"Loading file: {FilePath}"); byte[] fileBytes = File.ReadAllBytes(FilePath); - Console.WriteLine($"Successfully loaded {FilePath}"); - Console.WriteLine($"File size: {fileBytes.Length} bytes\n"); - - // Parse the PE format - Console.WriteLine("Parsing PE format...\n"); PeFile peFile = new PeFile(fileBytes); peFile.Parse(); - // Print PE file information - Console.WriteLine("PE File Information:"); - Console.WriteLine($"Architecture: {(peFile.OptionalHeader.Is64Bit() ? "64-bit" : "32-bit")}"); - Console.WriteLine($"Entry Point: 0x{peFile.OptionalHeader.AddressOfEntryPoint:X8}"); - Console.WriteLine($"Image Base: 0x{peFile.OptionalHeader.ImageBase:X8}"); - Console.WriteLine($"Number of Sections: {peFile.FileHeader.NumberOfSections}"); - Console.WriteLine(); - - // Print section information - PrintPeSections(peFile); - - // Print export information - PrintPeExports(peFile); - - // Print import information - PrintPeImports(peFile); - var projectPeFile = new ProjectPeFile() { ImageBase = new VirtualAddress(0, peFile.OptionalHeader.ImageBase), @@ -59,102 +23,5 @@ public class Program Name = Path.GetFileName(FilePath), EntryPointAddress = new FileAbsoluteAddress(peFile.OptionalHeader.AddressOfEntryPoint, peFile.OptionalHeader.ImageBase) }; - - // Find code sections - var codeSections = peFile.SectionHeaders.FindAll(s => s.ContainsCode()); - Console.WriteLine($"Found {codeSections.Count} code section(s):"); - foreach (var section in codeSections) - { - Console.WriteLine($" - {section.Name}: Size={section.VirtualSize} bytes, RVA=0x{section.VirtualAddress:X8}"); - } - - Console.WriteLine(); - - var projectPeFileSections = peFile.SectionHeaders.Select( - x => new ProjectPeFileSection() - { - Name = x.Name, - Flags = (x.ContainsCode() ? SectionFlags.Code : SectionFlags.None) | - (x.IsReadable() ? SectionFlags.Read : SectionFlags.None) | - (x.IsWritable() ? SectionFlags.Write : SectionFlags.None) | - (x.IsExecutable() ? SectionFlags.Exec : SectionFlags.None) , - VirtualAddress = new VirtualAddress(x.VirtualAddress, peFile.OptionalHeader.ImageBase), - Size = x.VirtualSize - } - ).ToList(); - - // Disassemble the first code section - if (codeSections.Count > 0) - { - var section = codeSections[0]; - byte[] codeBytes = peFile.GetSectionData(peFile.SectionHeaders.IndexOf(section)); - - var disassembler = new BlockDisassembler(codeBytes, section.VirtualAddress); - - var asmFunction = disassembler.DisassembleFromAddress(peFile.OptionalHeader.AddressOfEntryPoint); - Console.WriteLine(asmFunction); - } - - // Console.WriteLine("\nPress Enter to exit..."); - // Console.ReadLine(); - } - - private static void PrintPeImports(PeFile peFile) - { - Console.WriteLine("Imported Functions:"); - Console.WriteLine($"Number of Imported DLLs: {peFile.ImportDescriptors.Count}"); - - foreach (var import in peFile.ImportDescriptors) - { - Console.WriteLine($" DLL: {import.DllName}"); - - for (int i = 0; i < import.Functions.Count; i++) - { - var function = import.Functions[i]; - if (function.IsOrdinal) - { - Console.WriteLine($" {i}: Ordinal {function.Ordinal}"); - } - else - { - Console.WriteLine($" {i}: {function.Name} (Hint={function.Hint})"); - } - } - } - - Console.WriteLine(); - } - - private static void PrintPeExports(PeFile peFile) - { - Console.WriteLine("Exported Functions:"); - Console.WriteLine($"DLL Name: {peFile.ExportDirectory.DllName}"); - Console.WriteLine($"Number of Functions: {peFile.ExportDirectory.NumberOfFunctions}"); - Console.WriteLine($"Number of Names: {peFile.ExportDirectory.NumberOfNames}"); - - for (int i = 0; i < peFile.ExportedFunctions.Count; i++) - { - var export = peFile.ExportedFunctions[i]; - Console.WriteLine($" {i}: {export.Name} (Ordinal={export.Ordinal}, RVA=0x{export.AddressRva:X8})"); - } - - Console.WriteLine(); - } - - private static void PrintPeSections(PeFile peFile) - { - Console.WriteLine("Sections:"); - foreach (var section in peFile.SectionHeaders) - { - string flags = ""; - if (section.ContainsCode()) flags += "Code "; - if (section.IsExecutable()) flags += "Exec "; - if (section.IsReadable()) flags += "Read "; - if (section.IsWritable()) flags += "Write"; - - Console.WriteLine($" {peFile.SectionHeaders.IndexOf(section)}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.VirtualSize,-8} [{flags}]"); - } - - Console.WriteLine(); } } \ No newline at end of file diff --git a/X86Disassembler/ProjectSystem/ProjectPeFile.cs b/X86Disassembler/ProjectSystem/ProjectPeFile.cs index 4551a5a..784cc43 100644 --- a/X86Disassembler/ProjectSystem/ProjectPeFile.cs +++ b/X86Disassembler/ProjectSystem/ProjectPeFile.cs @@ -11,25 +11,4 @@ public class ProjectPeFile public Address EntryPointAddress { get; set; } public Address ImageBase { get; set; } -} - -public class ProjectPeFileSection -{ - public string Name { get; set; } - - public Address VirtualAddress { get; set; } - - public ulong Size { get; set; } - - public SectionFlags Flags { get; set; } -} - -[Flags] -public enum SectionFlags -{ - None = 0, - Code = 1, - Exec = 2, - Read = 4, - Write = 8 } \ No newline at end of file diff --git a/X86Disassembler/ProjectSystem/ProjectPeFileSection.cs b/X86Disassembler/ProjectSystem/ProjectPeFileSection.cs new file mode 100644 index 0000000..3c1a90c --- /dev/null +++ b/X86Disassembler/ProjectSystem/ProjectPeFileSection.cs @@ -0,0 +1,14 @@ +using X86Disassembler.Analysers; + +namespace X86Disassembler.ProjectSystem; + +public class ProjectPeFileSection +{ + public string Name { get; set; } + + public Address VirtualAddress { get; set; } + + public ulong Size { get; set; } + + public SectionFlags Flags { get; set; } +} \ No newline at end of file diff --git a/X86Disassembler/ProjectSystem/SectionFlags.cs b/X86Disassembler/ProjectSystem/SectionFlags.cs new file mode 100644 index 0000000..a3139c3 --- /dev/null +++ b/X86Disassembler/ProjectSystem/SectionFlags.cs @@ -0,0 +1,11 @@ +namespace X86Disassembler.ProjectSystem; + +[Flags] +public enum SectionFlags +{ + None = 0, + Code = 1, + Exec = 2, + Read = 4, + Write = 8 +} \ No newline at end of file