diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs
index eed564df..9e3d67c7 100644
--- a/BurnOutSharp.Builder/PortableExecutable.cs
+++ b/BurnOutSharp.Builder/PortableExecutable.cs
@@ -179,13 +179,13 @@ namespace BurnOutSharp.Builder
if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress != 0)
{
// If the offset for the export table doesn't exist
- int tableAddress = initialOffset
+ int exportTableAddress = initialOffset
+ (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (tableAddress >= data.Length)
+ if (exportTableAddress >= data.Length)
return executable;
// Try to parse the export table
- var exportTable = ParseExportTable(data, tableAddress, executable.SectionTable);
+ var exportTable = ParseExportTable(data, exportTableAddress, executable.SectionTable);
if (exportTable == null)
return null;
@@ -195,6 +195,28 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Import Table
+
+ // Should also be in the '.idata' section
+ if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress != 0)
+ {
+ // If the offset for the import table doesn't exist
+ int importTableAddress = initialOffset
+ + (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (importTableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the import table
+ var importTable = ParseImportTable(data, importTableAddress, optionalHeader.Magic, executable.SectionTable);
+ if (importTable == null)
+ return null;
+
+ // Set the import table
+ executable.ImportTable = importTable;
+ }
+
+ #endregion
+
#region Resource Directory Table
// Should also be in the '.rsrc' section
@@ -692,12 +714,12 @@ namespace BurnOutSharp.Builder
}
///
- /// Parse a byte array into a export directory table
+ /// Parse a byte array into a export table
///
/// Byte array to parse
/// Offset into the byte array
/// Section table to use for virtual address translation
- /// Filled export directory table on success, null on error
+ /// Filled export table on success, null on error
private static ExportTable ParseExportTable(byte[] data, int offset, SectionHeader[] sections)
{
// TODO: Use marshalling here instead of building
@@ -798,6 +820,152 @@ namespace BurnOutSharp.Builder
return exportTable;
}
+ ///
+ /// Parse a byte array into a import table
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// Optional header magic number indicating PE32 or PE32+
+ /// Section table to use for virtual address translation
+ /// Filled import table on success, null on error
+ private static ImportTable ParseImportTable(byte[] data, int offset, OptionalHeaderMagicNumber magic, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var importTable = new ImportTable();
+
+ // Import directory table
+ var importDirectoryTable = new List();
+
+ // Loop until the last item (all nulls) are found
+ while (true)
+ {
+ var importDirectoryTableEntry = new ImportDirectoryTableEntry();
+
+ importDirectoryTableEntry.ImportLookupTableRVA = data.ReadUInt32(ref offset);
+ importDirectoryTableEntry.TimeDateStamp = data.ReadUInt32(ref offset);
+ importDirectoryTableEntry.ForwarderChain = data.ReadUInt32(ref offset);
+ importDirectoryTableEntry.NameRVA = data.ReadUInt32(ref offset);
+ importDirectoryTableEntry.ImportAddressTableRVA = data.ReadUInt32(ref offset);
+
+ importDirectoryTable.Add(importDirectoryTableEntry);
+
+ // All zero values means the last entry
+ if (importDirectoryTableEntry.ImportLookupTableRVA == 0
+ && importDirectoryTableEntry.TimeDateStamp == 0
+ && importDirectoryTableEntry.ForwarderChain == 0
+ && importDirectoryTableEntry.NameRVA == 0
+ && importDirectoryTableEntry.ImportAddressTableRVA == 0)
+ break;
+ }
+
+ importTable.ImportDirectoryTable = importDirectoryTable.ToArray();
+
+ // Names
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.NameRVA != 0)
+ {
+ int nameAddress = (int)importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections);
+ string name = data.ReadString(ref nameAddress, System.Text.Encoding.ASCII);
+ importDirectoryTableEntry.Name = name;
+ }
+ }
+
+ // Lookup tables
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importLookupTable = new Dictionary();
+
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.ImportLookupTableRVA != 0)
+ {
+ int tableAddress = (int)importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections);
+ var entryLookupTable = new List();
+
+ while (true)
+ {
+ var entryLookupTableEntry = new ImportLookupTableEntry();
+
+ if (magic == OptionalHeaderMagicNumber.PE32)
+ {
+ uint entryValue = data.ReadUInt32(ref tableAddress);
+ entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0;
+ if (entryLookupTableEntry.OrdinalNameFlag)
+ entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000);
+ else
+ entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000);
+ }
+ else if (magic == OptionalHeaderMagicNumber.PE32Plus)
+ {
+ ulong entryValue = data.ReadUInt64(ref tableAddress);
+ entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0;
+ if (entryLookupTableEntry.OrdinalNameFlag)
+ entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000);
+ else
+ entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000);
+ }
+
+ entryLookupTable.Add(entryLookupTableEntry);
+
+ // All zero values means the last entry
+ if (entryLookupTableEntry.OrdinalNameFlag == false
+ && entryLookupTableEntry.OrdinalNumber == 0
+ && entryLookupTableEntry.HintNameTableRVA == 0)
+ break;
+ }
+
+ importLookupTable[i] = entryLookupTable.ToArray();
+ }
+
+ importTable.ImportLookupTables = importLookupTable;
+ }
+
+ // Address tables
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importLookupTable = new Dictionary();
+
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.ImportAddressTableRVA != 0)
+ {
+ int tableAddress = (int)importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections);
+ var entryAddressTable = new List();
+
+ while (true)
+ {
+ var entryLookupTableEntry = new ImportAddressTableEntry();
+
+ if (magic == OptionalHeaderMagicNumber.PE32)
+ {
+ uint entryValue = data.ReadUInt32(ref tableAddress);
+ entryLookupTableEntry.Address_PE32 = entryValue;
+ }
+ else if (magic == OptionalHeaderMagicNumber.PE32Plus)
+ {
+ ulong entryValue = data.ReadUInt64(ref tableAddress);
+ entryLookupTableEntry.Address_PE32Plus = entryValue;
+ }
+
+ entryAddressTable.Add(entryLookupTableEntry);
+
+ // All zero values means the last entry
+ if (entryLookupTableEntry.Address_PE32 == 0
+ && entryLookupTableEntry.Address_PE32Plus == 0)
+ break;
+ }
+
+ importLookupTable[i] = entryAddressTable.ToArray();
+ }
+
+ importTable.ImportAddressTables = importLookupTable;
+ }
+
+ // TODO: Figure out how to find the hint/name table
+
+ return importTable;
+ }
+
///
/// Parse a byte array into a resource directory table
///
@@ -1123,6 +1291,29 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Import Table
+
+ // Should also be in the '.idata' section
+ if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress != 0)
+ {
+ // If the offset for the import table doesn't exist
+ int importTableAddress = initialOffset
+ + (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (importTableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the import table
+ data.Seek(importTableAddress, SeekOrigin.Begin);
+ var importTable = ParseImportTable(data, optionalHeader.Magic, executable.SectionTable);
+ if (importTable == null)
+ return null;
+
+ // Set the import table
+ executable.ImportTable = importTable;
+ }
+
+ #endregion
+
#region Resource Directory Table
// Should also be in the '.rsrc' section
@@ -1614,11 +1805,11 @@ namespace BurnOutSharp.Builder
}
///
- /// Parse a Stream into a export directory table
+ /// Parse a Stream into a export table
///
/// Stream to parse
/// Section table to use for virtual address translation
- /// Filled export directory table on success, null on error
+ /// Filled export table on success, null on error
private static ExportTable ParseExportTable(Stream data, SectionHeader[] sections)
{
// TODO: Use marshalling here instead of building
@@ -1729,6 +1920,157 @@ namespace BurnOutSharp.Builder
return exportTable;
}
+ ///
+ /// Parse a Stream into a import table
+ ///
+ /// Stream to parse
+ /// Optional header magic number indicating PE32 or PE32+
+ /// Section table to use for virtual address translation
+ /// Filled import table on success, null on error
+ private static ImportTable ParseImportTable(Stream data, OptionalHeaderMagicNumber magic, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var importTable = new ImportTable();
+
+ // Import directory table
+ var importDirectoryTable = new List();
+
+ // Loop until the last item (all nulls) are found
+ while (true)
+ {
+ var importDirectoryTableEntry = new ImportDirectoryTableEntry();
+
+ importDirectoryTableEntry.ImportLookupTableRVA = data.ReadUInt32();
+ importDirectoryTableEntry.TimeDateStamp = data.ReadUInt32();
+ importDirectoryTableEntry.ForwarderChain = data.ReadUInt32();
+ importDirectoryTableEntry.NameRVA = data.ReadUInt32();
+ importDirectoryTableEntry.ImportAddressTableRVA = data.ReadUInt32();
+
+ importDirectoryTable.Add(importDirectoryTableEntry);
+
+ // All zero values means the last entry
+ if (importDirectoryTableEntry.ImportLookupTableRVA == 0
+ && importDirectoryTableEntry.TimeDateStamp == 0
+ && importDirectoryTableEntry.ForwarderChain == 0
+ && importDirectoryTableEntry.NameRVA == 0
+ && importDirectoryTableEntry.ImportAddressTableRVA == 0)
+ break;
+ }
+
+ importTable.ImportDirectoryTable = importDirectoryTable.ToArray();
+
+ // Names
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.NameRVA != 0)
+ {
+ uint nameAddress = importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections);
+ data.Seek(nameAddress, SeekOrigin.Begin);
+
+ string name = data.ReadString(System.Text.Encoding.ASCII);
+ importDirectoryTableEntry.Name = name;
+ }
+ }
+
+ // Lookup tables
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importLookupTable = new Dictionary();
+
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.ImportLookupTableRVA != 0)
+ {
+ uint tableAddress = importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections);
+ data.Seek(tableAddress, SeekOrigin.Begin);
+
+ var entryLookupTable = new List();
+
+ while (true)
+ {
+ var entryLookupTableEntry = new ImportLookupTableEntry();
+
+ if (magic == OptionalHeaderMagicNumber.PE32)
+ {
+ uint entryValue = data.ReadUInt32();
+ entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0;
+ if (entryLookupTableEntry.OrdinalNameFlag)
+ entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000);
+ else
+ entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000);
+ }
+ else if (magic == OptionalHeaderMagicNumber.PE32Plus)
+ {
+ ulong entryValue = data.ReadUInt64();
+ entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0;
+ if (entryLookupTableEntry.OrdinalNameFlag)
+ entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000);
+ else
+ entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000);
+ }
+
+ entryLookupTable.Add(entryLookupTableEntry);
+
+ // All zero values means the last entry
+ if (entryLookupTableEntry.OrdinalNameFlag == false
+ && entryLookupTableEntry.OrdinalNumber == 0
+ && entryLookupTableEntry.HintNameTableRVA == 0)
+ break;
+ }
+
+ importLookupTable[i] = entryLookupTable.ToArray();
+ }
+
+ importTable.ImportLookupTables = importLookupTable;
+ }
+
+ // Address tables
+ for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
+ {
+ var importLookupTable = new Dictionary();
+
+ var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
+ if (importDirectoryTableEntry.ImportAddressTableRVA != 0)
+ {
+ uint tableAddress = importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections);
+ data.Seek(tableAddress, SeekOrigin.Begin);
+
+ var entryAddressTable = new List();
+
+ while (true)
+ {
+ var entryLookupTableEntry = new ImportAddressTableEntry();
+
+ if (magic == OptionalHeaderMagicNumber.PE32)
+ {
+ uint entryValue = data.ReadUInt32();
+ entryLookupTableEntry.Address_PE32 = entryValue;
+ }
+ else if (magic == OptionalHeaderMagicNumber.PE32Plus)
+ {
+ ulong entryValue = data.ReadUInt64();
+ entryLookupTableEntry.Address_PE32Plus = entryValue;
+ }
+
+ entryAddressTable.Add(entryLookupTableEntry);
+
+ // All zero values means the last entry
+ if (entryLookupTableEntry.Address_PE32 == 0
+ && entryLookupTableEntry.Address_PE32Plus == 0)
+ break;
+ }
+
+ importLookupTable[i] = entryAddressTable.ToArray();
+ }
+
+ importTable.ImportAddressTables = importLookupTable;
+ }
+
+ // TODO: Figure out how to find the hint/name table
+
+ return importTable;
+ }
+
///
/// Parse a Stream into a resource directory table
///
diff --git a/BurnOutSharp.Models/PortableExecutable/ImportDirectoryTableEntry.cs b/BurnOutSharp.Models/PortableExecutable/ImportDirectoryTableEntry.cs
index 2c65d4aa..3278e284 100644
--- a/BurnOutSharp.Models/PortableExecutable/ImportDirectoryTableEntry.cs
+++ b/BurnOutSharp.Models/PortableExecutable/ImportDirectoryTableEntry.cs
@@ -39,10 +39,15 @@ namespace BurnOutSharp.Models.PortableExecutable
///
public uint NameRVA;
+ ///
+ /// ASCII string that contains the name of the DLL.
+ ///
+ public string Name;
+
///
/// The RVA of the import address table. The contents of this table are identical
/// to the contents of the import lookup table until the image is bound.
///
- public uint ImportAddressTableRVAThunkTable;
+ public uint ImportAddressTableRVA;
}
}
diff --git a/BurnOutSharp.Models/PortableExecutable/ImportTable.cs b/BurnOutSharp.Models/PortableExecutable/ImportTable.cs
index 8cb88587..38d53b45 100644
--- a/BurnOutSharp.Models/PortableExecutable/ImportTable.cs
+++ b/BurnOutSharp.Models/PortableExecutable/ImportTable.cs
@@ -1,4 +1,6 @@
-namespace BurnOutSharp.Models.PortableExecutable
+using System.Collections.Generic;
+
+namespace BurnOutSharp.Models.PortableExecutable
{
///
/// All image files that import symbols, including virtually all executable (EXE) files,
@@ -27,13 +29,13 @@
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of 64-bit
/// numbers for PE32+.
///
- public ImportLookupTableEntry[,] ImportLookupTable;
+ public Dictionary ImportLookupTables;
///
/// These addresses are the actual memory addresses of the symbols, although technically
- /// they are still called "virtual addresses.
+ /// they are still called "virtual addresses".
///
- public ImportAddressTableEntry[,] ImportAddressTable;
+ public Dictionary ImportAddressTables;
///
/// One hint/name table suffices for the entire import section.