using System; using System.Collections.Generic; using System.IO; using System.Text; using X86Disassembler.PE.Parsers; 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; } /// /// Initializes a new instance of the PEFormat class /// /// The raw file data public PEFormat(byte[] fileData) { _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(); } /// /// 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)) { // 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 = _optionalHeaderParser.Is64Bit(OptionalHeader); // 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)); } 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 (_sectionHeaderParser.IsSectionContainsCode(SectionHeaders[i])) { 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 _sectionHeaderParser.IsSectionContainsCode(section); } } }