mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-18 14:55:05 +00:00
Add initial PE import table parsing
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a export directory table
|
||||
/// Parse a byte array into a export 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>
|
||||
/// <returns>Filled export table on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a import table
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <param name="magic">Optional header magic number indicating PE32 or PE32+</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled import table on success, null on error</returns>
|
||||
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<ImportDirectoryTableEntry>();
|
||||
|
||||
// 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<int, ImportLookupTableEntry[]>();
|
||||
|
||||
var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
|
||||
if (importDirectoryTableEntry.ImportLookupTableRVA != 0)
|
||||
{
|
||||
int tableAddress = (int)importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections);
|
||||
var entryLookupTable = new List<ImportLookupTableEntry>();
|
||||
|
||||
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<int, ImportAddressTableEntry[]>();
|
||||
|
||||
var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
|
||||
if (importDirectoryTableEntry.ImportAddressTableRVA != 0)
|
||||
{
|
||||
int tableAddress = (int)importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections);
|
||||
var entryAddressTable = new List<ImportAddressTableEntry>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a resource directory table
|
||||
/// </summary>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a export directory table
|
||||
/// Parse a Stream into a export 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>
|
||||
/// <returns>Filled export table on success, null on error</returns>
|
||||
private static ExportTable ParseExportTable(Stream data, SectionHeader[] sections)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
@@ -1729,6 +1920,157 @@ namespace BurnOutSharp.Builder
|
||||
return exportTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a import table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="magic">Optional header magic number indicating PE32 or PE32+</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled import table on success, null on error</returns>
|
||||
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<ImportDirectoryTableEntry>();
|
||||
|
||||
// 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<int, ImportLookupTableEntry[]>();
|
||||
|
||||
var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
|
||||
if (importDirectoryTableEntry.ImportLookupTableRVA != 0)
|
||||
{
|
||||
uint tableAddress = importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections);
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
|
||||
var entryLookupTable = new List<ImportLookupTableEntry>();
|
||||
|
||||
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<int, ImportAddressTableEntry[]>();
|
||||
|
||||
var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
|
||||
if (importDirectoryTableEntry.ImportAddressTableRVA != 0)
|
||||
{
|
||||
uint tableAddress = importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections);
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
|
||||
var entryAddressTable = new List<ImportAddressTableEntry>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource directory table
|
||||
/// </summary>
|
||||
|
||||
@@ -39,10 +39,15 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// </summary>
|
||||
public uint NameRVA;
|
||||
|
||||
/// <summary>
|
||||
/// ASCII string that contains the name of the DLL.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public uint ImportAddressTableRVAThunkTable;
|
||||
public uint ImportAddressTableRVA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace BurnOutSharp.Models.PortableExecutable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.Models.PortableExecutable
|
||||
{
|
||||
/// <summary>
|
||||
/// 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+.
|
||||
/// </summary>
|
||||
public ImportLookupTableEntry[,] ImportLookupTable;
|
||||
public Dictionary<int, ImportLookupTableEntry[]> ImportLookupTables;
|
||||
|
||||
/// <summary>
|
||||
/// These addresses are the actual memory addresses of the symbols, although technically
|
||||
/// they are still called "virtual addresses.
|
||||
/// they are still called "virtual addresses".
|
||||
/// </summary>
|
||||
public ImportAddressTableEntry[,] ImportAddressTable;
|
||||
public Dictionary<int, ImportAddressTableEntry[]> ImportAddressTables;
|
||||
|
||||
/// <summary>
|
||||
/// One hint/name table suffices for the entire import section.
|
||||
|
||||
Reference in New Issue
Block a user