diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs
index b6116213..31544743 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using BurnOutSharp.Tools;
@@ -21,14 +22,24 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries
///
/// The variable-length Unicode string data, word-aligned.
///
- public char[] UnicodeString;
+ public string UnicodeString;
public static ResourceDirectoryString Deserialize(Stream stream)
{
var rds = new ResourceDirectoryString();
rds.Length = stream.ReadUInt16();
- rds.UnicodeString = stream.ReadChars(rds.Length, Encoding.Unicode);
+ rds.UnicodeString = new string(stream.ReadChars(rds.Length, Encoding.Unicode));
+
+ return rds;
+ }
+
+ public static ResourceDirectoryString Deserialize(byte[] content, int offset)
+ {
+ var rds = new ResourceDirectoryString();
+
+ rds.Length = BitConverter.ToUInt16(content, offset); offset += 2;
+ rds.UnicodeString = Encoding.Unicode.GetString(content, offset, rds.Length);
return rds;
}
diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs
index b48383a3..334addd3 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs
@@ -22,12 +22,17 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries
///
/// The offset of a string that gives the Type, Name, or Language ID entry, depending on level of table.
///
- public uint NameOffset;
+ public uint NameOffset => (uint)(IntegerId & (1 << 32));
+
+ ///
+ /// The string that gives the Type, Name, or Language ID entry, depending on level of table pointed to by NameOffset
+ ///
+ public ResourceDirectoryString Name;
///
/// A 32-bit integer that identifies the Type, Name, or Language ID entry.
///
- public uint IntegerId => NameOffset;
+ public uint IntegerId;
///
/// High bit 0. Address of a Resource Data entry (a leaf).
@@ -37,42 +42,66 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries
///
/// High bit 1. The lower 31 bits are the address of another resource directory table (the next level down).
///
- public uint SubdirectoryOffset => DataEntryOffset;
+ public uint SubdirectoryOffset => (uint)(DataEntryOffset & (1 << 32));
///
/// Resource Data entry (a leaf).
///
public ResourceDataEntry DataEntry;
+ private bool NameFieldIsIntegerId = false;
+
+ ///
+ /// Determine if an entry has a name or integer identifier
+ ///
+ public bool IsIntegerIDEntry() => (NameOffset & (1 << 32)) == 0;
+
///
/// Determine if an entry represents a leaf or another directory table
///
- public bool IsResourceDataEntry() => (DataEntryOffset & (1 << 31)) == 0;
+ public bool IsResourceDataEntry() => (DataEntryOffset & (1 << 32)) == 0;
- public static ResourceDirectoryTableEntry Deserialize(Stream stream, SectionHeader[] sections)
+ public static ResourceDirectoryTableEntry Deserialize(Stream stream, long sectionStart)
{
var rdte = new ResourceDirectoryTableEntry();
- rdte.NameOffset = stream.ReadUInt32();
- rdte.DataEntryOffset = stream.ReadUInt32();
+ rdte.IntegerId = stream.ReadUInt32();
+ if (!rdte.IsIntegerIDEntry())
+ {
+ long lastPosition = stream.Position;
+ int nameAddress = (int)(rdte.NameOffset + sectionStart);
+ if (nameAddress >= 0 && nameAddress < stream.Length)
+ {
+ try
+ {
+ stream.Seek(nameAddress, SeekOrigin.Begin);
+ rdte.Name = ResourceDirectoryString.Deserialize(stream);
+ }
+ catch { }
+ finally
+ {
+ stream.Seek(lastPosition, SeekOrigin.Begin);
+ }
+ }
+ }
- // Read in the data if we have a leaf
+ rdte.DataEntryOffset = stream.ReadUInt32();
if (rdte.IsResourceDataEntry())
{
long lastPosition = stream.Position;
- try
+ int dataEntryAddress = (int)(rdte.DataEntryOffset + sectionStart);
+ if (dataEntryAddress > 0 && dataEntryAddress < stream.Length)
{
- int dataEntryAddress = (int)ConvertVirtualAddress(rdte.DataEntryOffset, sections);
- if (dataEntryAddress > 0)
+ try
{
stream.Seek(dataEntryAddress, SeekOrigin.Begin);
rdte.DataEntry = ResourceDataEntry.Deserialize(stream);
}
- }
- catch { }
- finally
- {
- stream.Seek(lastPosition, SeekOrigin.Begin);
+ catch { }
+ finally
+ {
+ stream.Seek(lastPosition, SeekOrigin.Begin);
+ }
}
}
@@ -81,52 +110,41 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries
return rdte;
}
- public static ResourceDirectoryTableEntry Deserialize(byte[] content, int offset, SectionHeader[] sections)
+ public static ResourceDirectoryTableEntry Deserialize(byte[] content, int offset, long sectionStart)
{
var rdte = new ResourceDirectoryTableEntry();
- rdte.NameOffset = BitConverter.ToUInt32(content, offset); offset += 4;
- rdte.DataEntryOffset = BitConverter.ToUInt32(content, offset); offset += 4;
+ rdte.IntegerId = BitConverter.ToUInt32(content, offset); offset += 4;
+ if (!rdte.IsIntegerIDEntry())
+ {
+ int nameAddress = (int)(rdte.NameOffset + sectionStart);
+ if (nameAddress >= 0 && nameAddress < content.Length)
+ {
+ try
+ {
+ rdte.Name = ResourceDirectoryString.Deserialize(content, nameAddress);
+ }
+ catch { }
+ }
+ }
- // Read in the data if we have a leaf
+ rdte.DataEntryOffset = BitConverter.ToUInt32(content, offset); offset += 4;
if (rdte.IsResourceDataEntry())
{
- try
+ int dataEntryAddress = (int)(rdte.DataEntryOffset + sectionStart);
+ if (dataEntryAddress > 0 && dataEntryAddress < content.Length)
{
- int dataEntryAddress = (int)ConvertVirtualAddress(rdte.DataEntryOffset, sections);
- if (dataEntryAddress > 0)
+ try
+ {
rdte.DataEntry = ResourceDataEntry.Deserialize(content, dataEntryAddress);
+ }
+ catch { }
}
- catch { }
}
// TODO: Add parsing for further directory table entries in the tree
return rdte;
}
-
- ///
- /// Convert a virtual address to a physical one
- ///
- /// Virtual address to convert
- /// Array of sections to check against
- /// Physical address, 0 on error
- private static uint ConvertVirtualAddress(uint virtualAddress, SectionHeader[] sections)
- {
- // Loop through all of the sections
- for (int i = 0; i < sections.Length; i++)
- {
- // If the section is invalid, just skip it
- if (sections[i] == null)
- continue;
-
- // Attempt to derive the physical address from the current section
- var section = sections[i];
- if (virtualAddress >= section.VirtualAddress && virtualAddress <= section.VirtualAddress + section.VirtualSize)
- return section.PointerToRawData + virtualAddress - section.VirtualAddress;
- }
-
- return 0;
- }
}
}
\ No newline at end of file
diff --git a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
index 5a796d11..7a63a5a5 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
@@ -83,7 +83,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft
///
/// Name of the section to check for
/// True to enable exact matching of names, false for starts-with
- /// Tuple of contains and index
+ /// True if the section is in the executable, false otherwise
public bool ContainsSection(string sectionName, bool exact = false)
{
// Get all section names first
@@ -100,6 +100,27 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return sectionNames.Any(n => n.Trim('\0').StartsWith(sectionName));
}
+ ///
+ /// Get the section based on name, if possible
+ ///
+ /// Name of the section to check for
+ /// True to enable exact matching of names, false for starts-with
+ /// Section data on success, null on error
+ public SectionHeader GetSection(string sectionName, bool exact = false)
+ {
+ // If we have no sections, we can't do anything
+ if (SectionTable == null || !SectionTable.Any())
+ return null;
+
+ // If we're checking exactly, return only exact matches (with nulls trimmed)
+ if (exact)
+ return SectionTable.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').Equals(sectionName));
+
+ // Otherwise, check if section name starts with the value
+ else
+ return SectionTable.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').StartsWith(sectionName));
+ }
+
///
/// Get the list of section names
///
@@ -170,6 +191,33 @@ namespace BurnOutSharp.ExecutableType.Microsoft
// stream.Seek(tableAddress, SeekOrigin.Begin);
// pex.ResourceSection = ResourceSection.Deserialize(stream, pex.SectionTable);
// }
+
+ // // Export Table
+ // var table = pex.GetSection(".edata", true);
+ // if (table != null && table.VirtualSize > 0)
+ // {
+ // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ // stream.Seek(tableAddress, SeekOrigin.Begin);
+ // pex.ExportTable = ExportDataSection.Deserialize(stream);
+ // }
+
+ // // Import Table
+ // table = pex.GetSection(".idata", true);
+ // if (table != null && table.VirtualSize > 0)
+ // {
+ // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ // stream.Seek(tableAddress, SeekOrigin.Begin);
+ // pex.ImportTable = ImportDataSection.Deserialize(stream, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0);
+ // }
+
+ // Resource Table
+ var table = pex.GetSection(".rsrc", true);
+ if (table != null && table.VirtualSize > 0)
+ {
+ int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ stream.Seek(tableAddress, SeekOrigin.Begin);
+ pex.ResourceSection = ResourceSection.Deserialize(stream);
+ }
}
catch (Exception ex)
{
@@ -216,30 +264,29 @@ namespace BurnOutSharp.ExecutableType.Microsoft
pex.SectionTable[i] = SectionHeader.Deserialize(content, offset); offset += 40;
}
- // TODO: Uncomment these as the directories are understod and implemented
// // Export Table
- // var table = pex.SectionTable[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT];
- // if (table.VirtualSize > 0)
+ // var table = pex.GetSection(".edata", true);
+ // if (table != null && table.VirtualSize > 0)
// {
- // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
// pex.ExportTable = ExportDataSection.Deserialize(content, tableAddress);
// }
// // Import Table
- // table = pex.SectionTable[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_IMPORT];
- // if (table.VirtualSize > 0)
+ // table = pex.GetSection(".idata", true);
+ // if (table != null && table.VirtualSize > 0)
// {
- // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
// pex.ImportTable = ImportDataSection.Deserialize(content, tableAddress, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0);
// }
- // // Resource Table
- // var table = pex.SectionTable[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_RESOURCE];
- // if (table.VirtualSize > 0)
- // {
- // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
- // pex.ResourceSection = ResourceSection.Deserialize(content, tableAddress, pex.SectionTable);
- // }
+ // Resource Table
+ var table = pex.GetSection(".rsrc", true);
+ if (table != null && table.VirtualSize > 0)
+ {
+ int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable);
+ pex.ResourceSection = ResourceSection.Deserialize(content, tableAddress);
+ }
}
}
catch (Exception ex)
@@ -250,5 +297,29 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return pex;
}
+
+ ///
+ /// Convert a virtual address to a physical one
+ ///
+ /// Virtual address to convert
+ /// Array of sections to check against
+ /// Physical address, 0 on error
+ public static uint ConvertVirtualAddress(uint virtualAddress, SectionHeader[] sections)
+ {
+ // Loop through all of the sections
+ for (int i = 0; i < sections.Length; i++)
+ {
+ // If the section is invalid, just skip it
+ if (sections[i] == null)
+ continue;
+
+ // Attempt to derive the physical address from the current section
+ var section = sections[i];
+ if (virtualAddress >= section.VirtualAddress && virtualAddress <= section.VirtualAddress + section.VirtualSize)
+ return section.PointerToRawData + virtualAddress - section.VirtualAddress;
+ }
+
+ return 0;
+ }
}
}
\ No newline at end of file
diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs
index 23fd4c5c..197b414f 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs
@@ -48,10 +48,10 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections
var eds = new ExportDataSection();
eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(stream);
- eds.ExportAddressTable = ExportAddressTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
- eds.NamePointerTable = ExportNamePointerTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
- eds.OrdinalTable = ExportOrdinalTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
- //eds.ExportNameTable = ExportNameTable.Deserialize(stream); // TODO: set this table based on the NamePointerTable value
+ // eds.ExportAddressTable = ExportAddressTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
+ // eds.NamePointerTable = ExportNamePointerTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
+ // eds.OrdinalTable = ExportOrdinalTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
+ // eds.ExportNameTable = ExportNameTable.Deserialize(stream); // TODO: set this table based on the NamePointerTable value
return eds;
}
@@ -63,10 +63,10 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections
unsafe
{
eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(content, offset); offset += Marshal.SizeOf(eds.ExportDirectoryTable);
- eds.ExportAddressTable = ExportAddressTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: Figure out where this count comes from
- eds.NamePointerTable = ExportNamePointerTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.NamePointerTable); // TODO: Figure out where this count comes from
- eds.OrdinalTable = ExportOrdinalTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.OrdinalTable); // TODO: Figure out where this count comes from
- //eds.ExportNameTable = ExportNameTable.Deserialize(stream); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: set this table based on the NamePointerTable value
+ // eds.ExportAddressTable = ExportAddressTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: Figure out where this count comes from
+ // eds.NamePointerTable = ExportNamePointerTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.NamePointerTable); // TODO: Figure out where this count comes from
+ // eds.OrdinalTable = ExportOrdinalTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.OrdinalTable); // TODO: Figure out where this count comes from
+ // eds.ExportNameTable = ExportNameTable.Deserialize(stream); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: set this table based on the NamePointerTable value
}
return eds;
diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs
index 134bada8..cfea6738 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs
@@ -22,22 +22,24 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections
///
public ResourceDirectoryTable ResourceDirectoryTable;
- public static ResourceSection Deserialize(Stream stream, SectionHeader[] sections)
+ public static ResourceSection Deserialize(Stream stream)
{
var rs = new ResourceSection();
- rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(stream, sections);
+ long sectionStart = stream.Position;
+ rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(stream, sectionStart);
return rs;
}
- public static ResourceSection Deserialize(byte[] content, int offset, SectionHeader[] sections)
+ public static ResourceSection Deserialize(byte[] content, int offset)
{
var rs = new ResourceSection();
unsafe
{
- rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, offset, sections); offset += Marshal.SizeOf(rs.ResourceDirectoryTable);
+ long sectionStart = offset;
+ rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, offset, sectionStart); offset += Marshal.SizeOf(rs.ResourceDirectoryTable);
}
return rs;
diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs
index 411e261b..3110ff61 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs
@@ -68,7 +68,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables
// TODO: Determine how to store or reference the resource directory strings
// that immediately follow the last directory entry but before the data
- public static ResourceDirectoryTable Deserialize(Stream stream, SectionHeader[] sections)
+ public static ResourceDirectoryTable Deserialize(Stream stream, long sectionStart)
{
var rdt = new ResourceDirectoryTable();
@@ -82,19 +82,19 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables
rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries];
for (int i = 0; i < rdt.NumberOfNamedEntries; i++)
{
- rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sections);
+ rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sectionStart);
}
rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries];
for (int i = 0; i < rdt.NumberOfIdEntries; i++)
{
- rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sections);
+ rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sectionStart);
}
return rdt;
}
- public static ResourceDirectoryTable Deserialize(byte[] content, int offset, SectionHeader[] sections)
+ public static ResourceDirectoryTable Deserialize(byte[] content, int offset, long sectionStart)
{
var rdt = new ResourceDirectoryTable();
@@ -108,13 +108,13 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables
rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries];
for (int i = 0; i < rdt.NumberOfNamedEntries; i++)
{
- rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sections); offset += 8;
+ rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sectionStart); offset += 8;
}
rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries];
for (int i = 0; i < rdt.NumberOfIdEntries; i++)
{
- rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sections); offset += 8;
+ rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sectionStart); offset += 8;
}
return rdt;