diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 0000000..bc3de2d --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,19 @@ +when creating or edditing code, adjust namespace declaration style to oneliner, e.g. "namespace MyNamespace;". + +always separate usings, namespaces, type declarations, methods and properties with empty line. + +always add comments to the code, when the code is not trivial. + +always put classes into separate files. + +always try to build the project you've edited. + +always summarize the changes you've made. + +always add changes to git with descriptive comment, but be concise. + +never use terminal commands to edit code. In case of a failure, write it to user and stop execution. + +never address compiler warnings yourself. If you see a warning, suggest to address it. + +when working with RVA variables, always add that to variable name, e.g. "nameRVA". \ No newline at end of file diff --git a/X86Disassembler/PE/ExportDirectory.cs b/X86Disassembler/PE/ExportDirectory.cs index 4a02d95..f6aec2d 100644 --- a/X86Disassembler/PE/ExportDirectory.cs +++ b/X86Disassembler/PE/ExportDirectory.cs @@ -9,8 +9,8 @@ public class ExportDirectory 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 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 diff --git a/X86Disassembler/PE/ExportedFunction.cs b/X86Disassembler/PE/ExportedFunction.cs index b1cc820..ec65763 100644 --- a/X86Disassembler/PE/ExportedFunction.cs +++ b/X86Disassembler/PE/ExportedFunction.cs @@ -7,7 +7,7 @@ public class ExportedFunction { public string Name; // Function name public ushort Ordinal; // Function ordinal - public uint Address; // Function RVA + public uint AddressRva; // Function RVA public bool IsForwarder; // True if this is a forwarder public string ForwarderName; // Name of the forwarded function diff --git a/X86Disassembler/PE/ImportDescriptor.cs b/X86Disassembler/PE/ImportDescriptor.cs index 56bbf4d..69366b9 100644 --- a/X86Disassembler/PE/ImportDescriptor.cs +++ b/X86Disassembler/PE/ImportDescriptor.cs @@ -5,12 +5,12 @@ namespace X86Disassembler.PE; /// 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 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(); diff --git a/X86Disassembler/PE/ImportedFunction.cs b/X86Disassembler/PE/ImportedFunction.cs index 79e784e..b2b5370 100644 --- a/X86Disassembler/PE/ImportedFunction.cs +++ b/X86Disassembler/PE/ImportedFunction.cs @@ -9,7 +9,7 @@ public class ImportedFunction 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 + public uint ThunkRva; // RVA of the thunk for this function /// /// Initializes a new instance of the ImportedFunction class diff --git a/X86Disassembler/PE/OptionalHeader.cs b/X86Disassembler/PE/OptionalHeader.cs index 50ab235..13ea623 100644 --- a/X86Disassembler/PE/OptionalHeader.cs +++ b/X86Disassembler/PE/OptionalHeader.cs @@ -21,27 +21,27 @@ public class OptionalHeader 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 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 @@ -58,7 +58,7 @@ public class OptionalHeader SizeOfHeapCommit = 0u; // Initialize array to avoid nullability warning - DataDirectories = new DataDirectory[0]; + DataDirectories = []; } /// diff --git a/X86Disassembler/PE/PEFormat.cs b/X86Disassembler/PE/PEFormat.cs index 130a0f8..c2fb98b 100644 --- a/X86Disassembler/PE/PEFormat.cs +++ b/X86Disassembler/PE/PEFormat.cs @@ -8,39 +8,39 @@ namespace X86Disassembler.PE; public class PEFormat { // DOS Header constants - private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ' - private const uint PE_SIGNATURE = 0x00004550; // 'PE\0\0' - + 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 - + 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 - + 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 - + 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; @@ -49,19 +49,19 @@ public class PEFormat 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 /// @@ -72,25 +72,25 @@ public class PEFormat 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 /// @@ -104,55 +104,60 @@ public class PEFormat { // 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 && + 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); + ExportedFunctions = _exportDirectoryParser.ParseExportedFunctions( + reader, + ExportDirectory, + exportDirRva, + exportDirSize + ); } - + // Parse Import Descriptors - if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_IMPORT && + 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) @@ -161,7 +166,7 @@ public class PEFormat return false; } } - + /// /// Gets the raw data for a specific section /// @@ -173,15 +178,21 @@ public class PEFormat { throw new ArgumentOutOfRangeException(nameof(sectionIndex)); } - + SectionHeader section = SectionHeaders[sectionIndex]; byte[] sectionData = new byte[section.SizeOfRawData]; - - Array.Copy(_fileData, section.PointerToRawData, sectionData, 0, section.SizeOfRawData); - + + Array.Copy( + _fileData, + section.PointerToRawData, + sectionData, + 0, + section.SizeOfRawData + ); + return sectionData; } - + /// /// Gets the raw data for a section by name /// @@ -196,10 +207,10 @@ public class PEFormat return GetSectionData(i); } } - + throw new ArgumentException($"Section '{sectionName}' not found"); } - + /// /// Gets all code sections /// @@ -207,18 +218,19 @@ public class PEFormat public List GetCodeSections() { List codeSections = new List(); - + for (int i = 0; i < SectionHeaders.Count; i++) { - if (SectionHeaders[i].ContainsCode()) + if (SectionHeaders[i] + .ContainsCode()) { codeSections.Add(i); } } - + return codeSections; } - + /// /// Checks if a section contains code /// diff --git a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs index 666947e..c8ba43f 100644 --- a/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs +++ b/X86Disassembler/PE/Parsers/ExportDirectoryParser.cs @@ -30,7 +30,7 @@ public class ExportDirectoryParser directory.TimeDateStamp = reader.ReadUInt32(); directory.MajorVersion = reader.ReadUInt16(); directory.MinorVersion = reader.ReadUInt16(); - directory.Name = reader.ReadUInt32(); + directory.DllNameRva = reader.ReadUInt32(); directory.Base = reader.ReadUInt32(); directory.NumberOfFunctions = reader.ReadUInt32(); directory.NumberOfNames = reader.ReadUInt32(); @@ -41,7 +41,7 @@ public class ExportDirectoryParser // Read the DLL name try { - uint dllNameRVA = directory.Name; + uint dllNameRVA = directory.DllNameRva; uint dllNameOffset = _utility.RvaToOffset(dllNameRVA); reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin); @@ -134,7 +134,7 @@ public class ExportDirectoryParser ExportedFunction function = new ExportedFunction(); function.Ordinal = (ushort)(i + directory.Base); - function.Address = functionRVA; + function.AddressRva = functionRVA; // Check if this function has a name if (ordinalToName.TryGetValue(i, out string? name)) diff --git a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs index b6f2f11..50bedd8 100644 --- a/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs +++ b/X86Disassembler/PE/Parsers/ImportDescriptorParser.cs @@ -50,11 +50,11 @@ public class ImportDescriptorParser ImportDescriptor descriptor = new ImportDescriptor { - OriginalFirstThunk = originalFirstThunk, + OriginalFirstThunkRva = originalFirstThunk, TimeDateStamp = timeDateStamp, ForwarderChain = forwarderChain, - Name = nameRva, - FirstThunk = firstThunk, + DllNameRva = nameRva, + FirstThunkRva = firstThunk, DllName = "Unknown" // Default name in case we can't read it }; @@ -111,7 +111,7 @@ public class ImportDescriptorParser try { // Use OriginalFirstThunk if available, otherwise use FirstThunk - uint thunkRva = descriptor.OriginalFirstThunk != 0 ? descriptor.OriginalFirstThunk : descriptor.FirstThunk; + uint thunkRva = descriptor.OriginalFirstThunkRva != 0 ? descriptor.OriginalFirstThunkRva : descriptor.FirstThunkRva; if (thunkRva == 0) { @@ -133,7 +133,7 @@ public class ImportDescriptorParser ImportedFunction function = new ImportedFunction { - ThunkRVA = thunkRva + (uint)(functionCount * 4) + ThunkRva = thunkRva + (uint)(functionCount * 4) }; // Check if imported by ordinal (high bit set) diff --git a/X86Disassembler/Program.cs b/X86Disassembler/Program.cs index 2b4094f..95e75e3 100644 --- a/X86Disassembler/Program.cs +++ b/X86Disassembler/Program.cs @@ -99,7 +99,7 @@ internal class Program for (int i = 0; i < peFormat.ExportedFunctions.Count; i++) { var function = peFormat.ExportedFunctions[i]; - Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.Address:X8})"); + Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.AddressRva:X8})"); } }