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