diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs
index 92319770..6661b60f 100644
--- a/BurnOutSharp.Builder/PortableExecutable.cs
+++ b/BurnOutSharp.Builder/PortableExecutable.cs
@@ -173,6 +173,28 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Export Table
+
+ // Should also be in the '.rsrc' section
+ if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress != 0)
+ {
+ // If the offset for the export table doesn't exist
+ int tableAddress = initialOffset
+ + (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (tableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the export table
+ var exportTable = ParseExportTable(data, tableAddress, executable.SectionTable);
+ if (exportTable == null)
+ return null;
+
+ // Set the export table
+ executable.ExportTable = exportTable;
+ }
+
+ #endregion
+
#region Resource Directory Table
// Should also be in the '.rsrc' section
@@ -669,6 +691,113 @@ namespace BurnOutSharp.Builder
return delayLoadDirectoryTable;
}
+ ///
+ /// Parse a byte array into a export directory 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
+ private static ExportTable ParseExportTable(byte[] data, int offset, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var exportTable = new ExportTable();
+
+ var exportDirectoryTable = new ExportDirectoryTable();
+
+ exportDirectoryTable.ExportFlags = data.ReadUInt32(ref offset);
+ exportDirectoryTable.TimeDateStamp = data.ReadUInt32(ref offset);
+ exportDirectoryTable.MajorVersion = data.ReadUInt16(ref offset);
+ exportDirectoryTable.MinorVersion = data.ReadUInt16(ref offset);
+ exportDirectoryTable.NameRVA = data.ReadUInt32(ref offset);
+ exportDirectoryTable.OrdinalBase = data.ReadUInt32(ref offset);
+ exportDirectoryTable.AddressTableEntries = data.ReadUInt32(ref offset);
+ exportDirectoryTable.NumberOfNamePointers = data.ReadUInt32(ref offset);
+ exportDirectoryTable.ExportAddressTableRVA = data.ReadUInt32(ref offset);
+ exportDirectoryTable.NamePointerRVA = data.ReadUInt32(ref offset);
+ exportDirectoryTable.OrdinalTableRVA = data.ReadUInt32(ref offset);
+
+ exportTable.ExportDirectoryTable = exportDirectoryTable;
+
+ // Name
+ if (exportDirectoryTable.NameRVA != 0)
+ {
+ offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
+ string name = data.ReadString(ref offset, System.Text.Encoding.ASCII);
+ exportDirectoryTable.Name = name;
+ }
+
+ // Address table
+ if (exportDirectoryTable.AddressTableEntries != 0 && exportDirectoryTable.ExportAddressTableRVA != 0)
+ {
+ offset = (int)exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections);
+ var exportAddressTable = new ExportAddressTableEntry[exportDirectoryTable.AddressTableEntries];
+
+ for (int i = 0; i < exportDirectoryTable.AddressTableEntries; i++)
+ {
+ var addressTableEntry = new ExportAddressTableEntry();
+
+ // TODO: Use the optional header address and length to determine if export or forwarder
+ addressTableEntry.ExportRVA = data.ReadUInt32(ref offset);
+ addressTableEntry.ForwarderRVA = addressTableEntry.ExportRVA;
+
+ exportAddressTable[i] = addressTableEntry;
+ }
+
+ exportTable.ExportAddressTable = exportAddressTable;
+ }
+
+ // Name pointer table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NamePointerRVA != 0)
+ {
+ offset = (int)exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections);
+ var namePointerTable = new ExportNamePointerTable();
+
+ namePointerTable.Pointers = new uint[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ uint pointer = data.ReadUInt32(ref offset);
+ namePointerTable.Pointers[i] = pointer;
+ }
+
+ exportTable.NamePointerTable = namePointerTable;
+ }
+
+ // Ordinal table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.OrdinalTableRVA != 0)
+ {
+ offset = (int)exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections);
+ var exportOrdinalTable = new ExportOrdinalTable();
+
+ exportOrdinalTable.Indexes = new ushort[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ ushort pointer = data.ReadUInt16(ref offset);
+ exportOrdinalTable.Indexes[i] = pointer;
+ }
+
+ exportTable.OrdinalTable = exportOrdinalTable;
+ }
+
+ // Name table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NameRVA != 0)
+ {
+ offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
+ var exportNameTable = new ExportNameTable();
+
+ exportNameTable.Strings = new string[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ string str = data.ReadString(ref offset, System.Text.Encoding.ASCII);
+ exportNameTable.Strings[i] = str;
+ }
+
+ exportTable.ExportNameTable = exportNameTable;
+ }
+
+ return exportTable;
+ }
+
///
/// Parse a byte array into a resource directory table
///
@@ -971,6 +1100,29 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Export Table
+
+ // Should also be in the '.edata' section
+ if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress != 0)
+ {
+ // If the offset for the export table doesn't exist
+ int exportTableAddress = initialOffset
+ + (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (exportTableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the export table
+ data.Seek(exportTableAddress, SeekOrigin.Begin);
+ var exportTable = ParseExportTable(data, executable.SectionTable);
+ if (exportTable == null)
+ return null;
+
+ // Set the export table
+ executable.ExportTable = exportTable;
+ }
+
+ #endregion
+
#region Resource Directory Table
// Should also be in the '.rsrc' section
@@ -1461,6 +1613,122 @@ namespace BurnOutSharp.Builder
return delayLoadDirectoryTable;
}
+ ///
+ /// Parse a Stream into a export directory table
+ ///
+ /// Stream to parse
+ /// Section table to use for virtual address translation
+ /// Filled export directory table on success, null on error
+ private static ExportTable ParseExportTable(Stream data, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var exportTable = new ExportTable();
+
+ var exportDirectoryTable = new ExportDirectoryTable();
+
+ exportDirectoryTable.ExportFlags = data.ReadUInt32();
+ exportDirectoryTable.TimeDateStamp = data.ReadUInt32();
+ exportDirectoryTable.MajorVersion = data.ReadUInt16();
+ exportDirectoryTable.MinorVersion = data.ReadUInt16();
+ exportDirectoryTable.NameRVA = data.ReadUInt32();
+ exportDirectoryTable.OrdinalBase = data.ReadUInt32();
+ exportDirectoryTable.AddressTableEntries = data.ReadUInt32();
+ exportDirectoryTable.NumberOfNamePointers = data.ReadUInt32();
+ exportDirectoryTable.ExportAddressTableRVA = data.ReadUInt32();
+ exportDirectoryTable.NamePointerRVA = data.ReadUInt32();
+ exportDirectoryTable.OrdinalTableRVA = data.ReadUInt32();
+
+ exportTable.ExportDirectoryTable = exportDirectoryTable;
+
+ // Name
+ if (exportDirectoryTable.NameRVA != 0)
+ {
+ uint nameAddress = exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
+ data.Seek(nameAddress, SeekOrigin.Begin);
+
+ string name = data.ReadString(System.Text.Encoding.ASCII);
+ exportDirectoryTable.Name = name;
+ }
+
+ // Address table
+ if (exportDirectoryTable.AddressTableEntries != 0 && exportDirectoryTable.ExportAddressTableRVA != 0)
+ {
+ uint exportAddressTableAddress = exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections);
+ data.Seek(exportAddressTableAddress, SeekOrigin.Begin);
+
+ var exportAddressTable = new ExportAddressTableEntry[exportDirectoryTable.AddressTableEntries];
+
+ for (int i = 0; i < exportDirectoryTable.AddressTableEntries; i++)
+ {
+ var addressTableEntry = new ExportAddressTableEntry();
+
+ // TODO: Use the optional header address and length to determine if export or forwarder
+ addressTableEntry.ExportRVA = data.ReadUInt32();
+ addressTableEntry.ForwarderRVA = addressTableEntry.ExportRVA;
+
+ exportAddressTable[i] = addressTableEntry;
+ }
+
+ exportTable.ExportAddressTable = exportAddressTable;
+ }
+
+ // Name pointer table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NamePointerRVA != 0)
+ {
+ uint namePointerTableAddress = exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections);
+ data.Seek(namePointerTableAddress, SeekOrigin.Begin);
+
+ var namePointerTable = new ExportNamePointerTable();
+
+ namePointerTable.Pointers = new uint[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ uint pointer = data.ReadUInt32();
+ namePointerTable.Pointers[i] = pointer;
+ }
+
+ exportTable.NamePointerTable = namePointerTable;
+ }
+
+ // Ordinal table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.OrdinalTableRVA != 0)
+ {
+ uint ordinalTableAddress = exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections);
+ data.Seek(ordinalTableAddress, SeekOrigin.Begin);
+
+ var exportOrdinalTable = new ExportOrdinalTable();
+
+ exportOrdinalTable.Indexes = new ushort[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ ushort pointer = data.ReadUInt16();
+ exportOrdinalTable.Indexes[i] = pointer;
+ }
+
+ exportTable.OrdinalTable = exportOrdinalTable;
+ }
+
+ // Name table
+ if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NameRVA != 0)
+ {
+ uint nameTableAddress = exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
+ data.Seek(nameTableAddress, SeekOrigin.Begin);
+
+ var exportNameTable = new ExportNameTable();
+
+ exportNameTable.Strings = new string[exportDirectoryTable.NumberOfNamePointers];
+ for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
+ {
+ string str = data.ReadString();
+ exportNameTable.Strings[i] = str;
+ }
+
+ exportTable.ExportNameTable = exportNameTable;
+ }
+
+ return exportTable;
+ }
+
///
/// Parse a Stream into a resource directory table
///
diff --git a/BurnOutSharp.Models/PortableExecutable/Executable.cs b/BurnOutSharp.Models/PortableExecutable/Executable.cs
index 77899a6e..e1ea7f68 100644
--- a/BurnOutSharp.Models/PortableExecutable/Executable.cs
+++ b/BurnOutSharp.Models/PortableExecutable/Executable.cs
@@ -64,6 +64,11 @@ namespace BurnOutSharp.Models.PortableExecutable
#region Named Sections
+ ///
+ /// Export table (.edata);
+ ///
+ public ExportTable ExportTable { get; set; }
+
///
/// Resource directory table (.rsrc)
///
@@ -86,8 +91,8 @@ namespace BurnOutSharp.Models.PortableExecutable
// - [Export Ordinal Table]
// - [Export Name Table]
// - The .idata Section
- // - Import Lookup Table [has model, but bit-based]
- // - Import Address Table
+ // - Import Lookup Table [has model, but bit-based]
+ // - Import Address Table
// - The .pdata Section [Multiple formats per entry]
// - TLS Callback Functions
// - The .cormeta Section (Object Only)
diff --git a/BurnOutSharp.Models/PortableExecutable/ExportDirectoryTable.cs b/BurnOutSharp.Models/PortableExecutable/ExportDirectoryTable.cs
index bca2ac26..427676bf 100644
--- a/BurnOutSharp.Models/PortableExecutable/ExportDirectoryTable.cs
+++ b/BurnOutSharp.Models/PortableExecutable/ExportDirectoryTable.cs
@@ -39,6 +39,11 @@ namespace BurnOutSharp.Models.PortableExecutable
///
public uint NameRVA;
+ ///
+ /// ASCII string that contains the name of the DLL.
+ ///
+ public string Name;
+
///
/// The starting ordinal number for exports in this image. This field specifies
/// the starting ordinal number for the export address table. It is usually set
diff --git a/BurnOutSharp.Models/PortableExecutable/ExportNameTable.cs b/BurnOutSharp.Models/PortableExecutable/ExportNameTable.cs
index 671f8a54..accf45d9 100644
--- a/BurnOutSharp.Models/PortableExecutable/ExportNameTable.cs
+++ b/BurnOutSharp.Models/PortableExecutable/ExportNameTable.cs
@@ -22,6 +22,6 @@
///
/// A series of null-terminated ASCII strings of variable length.
///
- public string[] Indexes;
+ public string[] Strings;
}
}
diff --git a/BurnOutSharp.Models/PortableExecutable/ExportTable.cs b/BurnOutSharp.Models/PortableExecutable/ExportTable.cs
new file mode 100644
index 00000000..bdbd16ad
--- /dev/null
+++ b/BurnOutSharp.Models/PortableExecutable/ExportTable.cs
@@ -0,0 +1,53 @@
+namespace BurnOutSharp.Models.PortableExecutable
+{
+ ///
+ /// The export data section, named .edata, contains information about symbols that other images
+ /// can access through dynamic linking. Exported symbols are generally found in DLLs, but DLLs
+ /// can also import symbols.
+ ///
+ /// An overview of the general structure of the export section is described below. The tables
+ /// described are usually contiguous in the file in the order shown (though this is not
+ /// required). Only the export directory table and export address table are required to export
+ /// symbols as ordinals. (An ordinal is an export that is accessed directly by its export
+ /// address table index.) The name pointer table, ordinal table, and export name table all
+ /// exist to support use of export names.
+ ///
+ ///
+ public class ExportTable
+ {
+ ///
+ /// A table with just one row (unlike the debug directory). This table indicates the
+ /// locations and sizes of the other export tables.
+ ///
+ public ExportDirectoryTable ExportDirectoryTable;
+
+ ///
+ /// An array of RVAs of exported symbols. These are the actual addresses of the exported
+ /// functions and data within the executable code and data sections. Other image files
+ /// can import a symbol by using an index to this table (an ordinal) or, optionally, by
+ /// using the public name that corresponds to the ordinal if a public name is defined.
+ ///
+ public ExportAddressTableEntry[] ExportAddressTable;
+
+ ///
+ /// An array of pointers to the public export names, sorted in ascending order.
+ ///
+ public ExportNamePointerTable NamePointerTable;
+
+ ///
+ /// An array of the ordinals that correspond to members of the name pointer table. The
+ /// correspondence is by position; therefore, the name pointer table and the ordinal table
+ /// must have the same number of members. Each ordinal is an index into the export address
+ /// table.
+ ///
+ public ExportOrdinalTable OrdinalTable;
+
+ ///
+ /// A series of null-terminated ASCII strings. Members of the name pointer table point into
+ /// this area. These names are the public names through which the symbols are imported and
+ /// exported; they are not necessarily the same as the private names that are used within
+ /// the image file.
+ ///
+ public ExportNameTable ExportNameTable;
+ }
+}