mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-10 10:56:57 +00:00
Add PE export table to builder
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a export directory table
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled export directory table on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a resource directory table
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a export directory table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled export directory table on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource directory table
|
||||
/// </summary>
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
|
||||
#region Named Sections
|
||||
|
||||
/// <summary>
|
||||
/// Export table (.edata);
|
||||
/// </summary>
|
||||
public ExportTable ExportTable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource directory table (.rsrc)
|
||||
/// </summary>
|
||||
@@ -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)
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// </summary>
|
||||
public uint NameRVA;
|
||||
|
||||
/// <summary>
|
||||
/// ASCII string that contains the name of the DLL.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
/// <summary>
|
||||
/// A series of null-terminated ASCII strings of variable length.
|
||||
/// </summary>
|
||||
public string[] Indexes;
|
||||
public string[] Strings;
|
||||
}
|
||||
}
|
||||
|
||||
53
BurnOutSharp.Models/PortableExecutable/ExportTable.cs
Normal file
53
BurnOutSharp.Models/PortableExecutable/ExportTable.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace BurnOutSharp.Models.PortableExecutable
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
|
||||
public class ExportTable
|
||||
{
|
||||
/// <summary>
|
||||
/// A table with just one row (unlike the debug directory). This table indicates the
|
||||
/// locations and sizes of the other export tables.
|
||||
/// </summary>
|
||||
public ExportDirectoryTable ExportDirectoryTable;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public ExportAddressTableEntry[] ExportAddressTable;
|
||||
|
||||
/// <summary>
|
||||
/// An array of pointers to the public export names, sorted in ascending order.
|
||||
/// </summary>
|
||||
public ExportNamePointerTable NamePointerTable;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public ExportOrdinalTable OrdinalTable;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public ExportNameTable ExportNameTable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user