diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs
index ed8342ab..b8b28179 100644
--- a/BurnOutSharp.Builder/PortableExecutable.cs
+++ b/BurnOutSharp.Builder/PortableExecutable.cs
@@ -175,6 +175,29 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Base Relocation Table
+
+ // Should also be in a '.reloc' section
+ if (optionalHeader.BaseRelocationTable != null && optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
+ {
+ // If the offset for the base relocation table doesn't exist
+ int baseRelocationTableAddress = initialOffset
+ + (int)optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (baseRelocationTableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the base relocation table
+ int endOffset = (int)(baseRelocationTableAddress + optionalHeader.BaseRelocationTable.Size);
+ var baseRelocationTable = ParseBaseRelocationTable(data, baseRelocationTableAddress, endOffset, executable.SectionTable);
+ if (baseRelocationTable == null)
+ return null;
+
+ // Set the base relocation table
+ executable.BaseRelocationTable = baseRelocationTable;
+ }
+
+ #endregion
+
#region Debug Table
// Should also be in a '.debug' section
@@ -746,7 +769,49 @@ namespace BurnOutSharp.Builder
}
///
- /// Parse a Stream into a debug table
+ /// Parse a byte array into a base relocation table
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// First address not part of the base relocation table
+ /// Section table to use for virtual address translation
+ /// Filled base relocation table on success, null on error
+ private static BaseRelocationBlock[] ParseBaseRelocationTable(byte[] data, int offset, int endOffset, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var baseRelocationTable = new List();
+
+ while (offset < endOffset)
+ {
+ var baseRelocationBlock = new BaseRelocationBlock();
+
+ baseRelocationBlock.PageRVA = data.ReadUInt32(ref offset);
+ baseRelocationBlock.BlockSize = data.ReadUInt32(ref offset);
+
+ var typeOffsetFieldEntries = new List();
+ int totalSize = 8;
+ while (totalSize < baseRelocationBlock.BlockSize)
+ {
+ var baseRelocationTypeOffsetFieldEntry = new BaseRelocationTypeOffsetFieldEntry();
+
+ ushort typeAndOffsetField = data.ReadUInt16(ref offset);
+ baseRelocationTypeOffsetFieldEntry.BaseRelocationType = (BaseRelocationTypes)(typeAndOffsetField >> 12);
+ baseRelocationTypeOffsetFieldEntry.Offset = (ushort)(typeAndOffsetField & 0x0FFF);
+
+ typeOffsetFieldEntries.Add(baseRelocationTypeOffsetFieldEntry);
+ totalSize += 2;
+ }
+
+ baseRelocationBlock.TypeOffsetFieldEntries = typeOffsetFieldEntries.ToArray();
+
+ baseRelocationTable.Add(baseRelocationBlock);
+ }
+
+ return baseRelocationTable.ToArray();
+ }
+
+ ///
+ /// Parse a byte array into a debug table
///
/// Byte array to parse
/// Offset into the byte array
@@ -1329,6 +1394,30 @@ namespace BurnOutSharp.Builder
#endregion
+ #region Base Relocation Table
+
+ // Should also be in a '.reloc' section
+ if (optionalHeader.BaseRelocationTable != null && optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
+ {
+ // If the offset for the base relocation table doesn't exist
+ int baseRelocationTableAddress = initialOffset
+ + (int)optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
+ if (baseRelocationTableAddress >= data.Length)
+ return executable;
+
+ // Try to parse the base relocation table
+ data.Seek(baseRelocationTableAddress, SeekOrigin.Begin);
+ int endOffset = (int)(baseRelocationTableAddress + optionalHeader.BaseRelocationTable.Size);
+ var baseRelocationTable = ParseBaseRelocationTable(data, endOffset, executable.SectionTable);
+ if (baseRelocationTable == null)
+ return null;
+
+ // Set the base relocation table
+ executable.BaseRelocationTable = baseRelocationTable;
+ }
+
+ #endregion
+
#region Debug Table
// Should also be in a '.debug' section
@@ -1895,6 +1984,47 @@ namespace BurnOutSharp.Builder
return delayLoadDirectoryTable;
}
+ ///
+ /// Parse a Stream into a base relocation table
+ ///
+ /// Stream to parse
+ /// First address not part of the base relocation table
+ /// Section table to use for virtual address translation
+ /// Filled base relocation table on success, null on error
+ private static BaseRelocationBlock[] ParseBaseRelocationTable(Stream data, int endOffset, SectionHeader[] sections)
+ {
+ // TODO: Use marshalling here instead of building
+ var baseRelocationTable = new List();
+
+ while (data.Position < endOffset)
+ {
+ var baseRelocationBlock = new BaseRelocationBlock();
+
+ baseRelocationBlock.PageRVA = data.ReadUInt32();
+ baseRelocationBlock.BlockSize = data.ReadUInt32();
+
+ var typeOffsetFieldEntries = new List();
+ int totalSize = 8;
+ while (totalSize < baseRelocationBlock.BlockSize)
+ {
+ var baseRelocationTypeOffsetFieldEntry = new BaseRelocationTypeOffsetFieldEntry();
+
+ ushort typeAndOffsetField = data.ReadUInt16();
+ baseRelocationTypeOffsetFieldEntry.BaseRelocationType = (BaseRelocationTypes)(typeAndOffsetField >> 12);
+ baseRelocationTypeOffsetFieldEntry.Offset = (ushort)(typeAndOffsetField & 0x0FFF);
+
+ typeOffsetFieldEntries.Add(baseRelocationTypeOffsetFieldEntry);
+ totalSize += 2;
+ }
+
+ baseRelocationBlock.TypeOffsetFieldEntries = typeOffsetFieldEntries.ToArray();
+
+ baseRelocationTable.Add(baseRelocationBlock);
+ }
+
+ return baseRelocationTable.ToArray();
+ }
+
///
/// Parse a Stream into a debug table
///
@@ -2254,51 +2384,49 @@ namespace BurnOutSharp.Builder
resourceDirectoryTable.NumberOfNameEntries = data.ReadUInt16();
resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16();
- // Perform top-level pass of data
+ // If we have no entries
int totalEntryCount = resourceDirectoryTable.NumberOfNameEntries + resourceDirectoryTable.NumberOfIDEntries;
- if (totalEntryCount > 0)
+ if (totalEntryCount == 0)
+ return resourceDirectoryTable;
+
+ // Perform top-level pass of data
+ resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount];
+ for (int i = 0; i < totalEntryCount; i++)
{
- resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount];
- for (int i = 0; i < totalEntryCount; i++)
+ var entry = new ResourceDirectoryEntry();
+ uint offset = data.ReadUInt32();
+ if ((offset & 0x80000000) != 0)
+ entry.NameOffset = offset & ~0x80000000;
+ else
+ entry.IntegerID = offset;
+
+ offset = data.ReadUInt32();
+ if ((offset & 0x80000000) != 0)
+ entry.SubdirectoryOffset = offset & ~0x80000000;
+ else
+ entry.DataEntryOffset = offset;
+
+ // Read the name from the offset, if needed
+ if (entry.NameOffset != default)
{
- var entry = new ResourceDirectoryEntry();
- uint offset = data.ReadUInt32();
- if ((offset & 0x80000000) != 0)
- entry.NameOffset = offset & ~0x80000000;
- else
- entry.IntegerID = offset;
-
- offset = data.ReadUInt32();
- if ((offset & 0x80000000) != 0)
- entry.SubdirectoryOffset = offset & ~0x80000000;
- else
- entry.DataEntryOffset = offset;
-
- // Read the name from the offset, if needed
- if (entry.NameOffset != default)
- {
- long currentOffset = data.Position;
- offset = entry.NameOffset + (uint)initialOffset;
- data.Seek(offset, SeekOrigin.Begin);
- var resourceDirectoryString = new ResourceDirectoryString();
- resourceDirectoryString.Length = data.ReadUInt16();
- resourceDirectoryString.UnicodeString = data.ReadBytes(resourceDirectoryString.Length * 2);
- entry.Name = resourceDirectoryString;
- data.Seek(currentOffset, SeekOrigin.Begin);
- }
-
- resourceDirectoryTable.Entries[i] = entry;
+ long currentOffset = data.Position;
+ offset = entry.NameOffset + (uint)initialOffset;
+ data.Seek(offset, SeekOrigin.Begin);
+ var resourceDirectoryString = new ResourceDirectoryString();
+ resourceDirectoryString.Length = data.ReadUInt16();
+ resourceDirectoryString.UnicodeString = data.ReadBytes(resourceDirectoryString.Length * 2);
+ entry.Name = resourceDirectoryString;
+ data.Seek(currentOffset, SeekOrigin.Begin);
}
+
+ resourceDirectoryTable.Entries[i] = entry;
}
- // Read all leaves at this level
- if (totalEntryCount > 0)
+ // Loop through and process the entries
+ foreach (var entry in resourceDirectoryTable.Entries)
{
- foreach (var entry in resourceDirectoryTable.Entries)
+ if (entry.DataEntryOffset != 0)
{
- if (entry.SubdirectoryOffset != 0)
- continue;
-
uint offset = entry.DataEntryOffset + (uint)initialOffset;
data.Seek(offset, SeekOrigin.Begin);
@@ -2318,18 +2446,11 @@ namespace BurnOutSharp.Builder
entry.DataEntry = resourceDataEntry;
}
- }
-
- // Now go one level lower
- if (totalEntryCount > 0)
- {
- foreach (var entry in resourceDirectoryTable.Entries)
+ else if (entry.SubdirectoryOffset != 0)
{
- if (entry.DataEntryOffset != 0)
- continue;
-
uint offset = entry.SubdirectoryOffset + (uint)initialOffset;
data.Seek(offset, SeekOrigin.Begin);
+
entry.Subdirectory = ParseResourceDirectoryTable(data, initialOffset, sections);
}
}
diff --git a/BurnOutSharp.Models/PortableExecutable/BaseRelocationBlock.cs b/BurnOutSharp.Models/PortableExecutable/BaseRelocationBlock.cs
index 0831d315..1f0b1099 100644
--- a/BurnOutSharp.Models/PortableExecutable/BaseRelocationBlock.cs
+++ b/BurnOutSharp.Models/PortableExecutable/BaseRelocationBlock.cs
@@ -46,6 +46,6 @@
/// in the Page RVA field for the block. This offset
/// specifies where the base relocation is to be applied.
///
- public ushort[] TypeOffsetFieldEntries;
+ public BaseRelocationTypeOffsetFieldEntry[] TypeOffsetFieldEntries;
}
}
diff --git a/BurnOutSharp.Models/PortableExecutable/BaseRelocationTypeOffsetFieldEntry.cs b/BurnOutSharp.Models/PortableExecutable/BaseRelocationTypeOffsetFieldEntry.cs
new file mode 100644
index 00000000..8d018c6a
--- /dev/null
+++ b/BurnOutSharp.Models/PortableExecutable/BaseRelocationTypeOffsetFieldEntry.cs
@@ -0,0 +1,22 @@
+namespace BurnOutSharp.Models.PortableExecutable
+{
+ ///
+ /// Type or Offset field entry is a WORD (2 bytes).
+ ///
+ ///
+ public class BaseRelocationTypeOffsetFieldEntry
+ {
+ ///
+ /// Stored in the high 4 bits of the WORD, a value that indicates the type
+ /// of base relocation to be applied. For more information, see
+ ///
+ public BaseRelocationTypes BaseRelocationType;
+
+ ///
+ /// Stored in the remaining 12 bits of the WORD, an offset from the starting
+ /// address that was specified in the Page RVA field for the block. This
+ /// offset specifies where the base relocation is to be applied.
+ ///
+ public ushort Offset;
+ }
+}
diff --git a/BurnOutSharp.Models/PortableExecutable/Executable.cs b/BurnOutSharp.Models/PortableExecutable/Executable.cs
index b5eda76d..0beed375 100644
--- a/BurnOutSharp.Models/PortableExecutable/Executable.cs
+++ b/BurnOutSharp.Models/PortableExecutable/Executable.cs
@@ -68,6 +68,11 @@ namespace BurnOutSharp.Models.PortableExecutable
// the object file contains managed code. The format of the metadata is not
// documented, but can be handed to the CLR interfaces for handling metadata.
+ ///
+ /// Base relocation table (.reloc)
+ ///
+ public BaseRelocationBlock[] BaseRelocationTable { get; set; }
+
///
/// Debug table (.debug*)
///
@@ -125,7 +130,6 @@ namespace BurnOutSharp.Models.PortableExecutable
// - Delay Bound Import Address Table
// - Delay Unload Import Address Table
// - The .pdata Section [Multiple formats per entry]
- // - The .reloc Section (Image Only)
// - The .tls Section
// - TLS Callback Functions
// - [The Load Configuration Structure (Image Only)]
diff --git a/BurnOutSharp.Wrappers/PortableExecutable.cs b/BurnOutSharp.Wrappers/PortableExecutable.cs
index 4e7c6df0..af01b94f 100644
--- a/BurnOutSharp.Wrappers/PortableExecutable.cs
+++ b/BurnOutSharp.Wrappers/PortableExecutable.cs
@@ -791,6 +791,9 @@ namespace BurnOutSharp.Wrappers
PrintCOFFSymbolTable();
PrintAttributeCertificateTable();
PrintDelayLoadDirectoryTable();
+
+ // Named Sections
+ PrintBaseRelocationTable();
PrintDebugTable();
PrintExportTable();
PrintImportTable();
@@ -1310,6 +1313,49 @@ namespace BurnOutSharp.Wrappers
Console.WriteLine();
}
+ ///
+ /// Print base relocation table information
+ ///
+ private void PrintBaseRelocationTable()
+ {
+ Console.WriteLine(" Base Relocation Table Information:");
+ Console.WriteLine(" -------------------------");
+ if (_executable.OptionalHeader?.BaseRelocationTable == null
+ || _executable.OptionalHeader.BaseRelocationTable.VirtualAddress == 0
+ || _executable.BaseRelocationTable == null)
+ {
+ Console.WriteLine(" No base relocation table items");
+ }
+ else
+ {
+ for (int i = 0; i < _executable.BaseRelocationTable.Length; i++)
+ {
+ var baseRelocationTableEntry = _executable.BaseRelocationTable[i];
+ Console.WriteLine($" Base Relocation Table Entry {i}");
+ Console.WriteLine($" Page RVA: {baseRelocationTableEntry.PageRVA}");
+ Console.WriteLine($" Block size: {baseRelocationTableEntry.BlockSize}");
+
+ Console.WriteLine($" Base Relocation Table {i} Type and Offset Information:");
+ Console.WriteLine(" -------------------------");
+ if (baseRelocationTableEntry.TypeOffsetFieldEntries == null || baseRelocationTableEntry.TypeOffsetFieldEntries.Length == 0)
+ {
+ Console.WriteLine(" No base relocation table type and offset entries");
+ }
+ else
+ {
+ for (int j = 0; j < baseRelocationTableEntry.TypeOffsetFieldEntries.Length; j++)
+ {
+ var typeOffsetFieldEntry = baseRelocationTableEntry.TypeOffsetFieldEntries[j];
+ Console.WriteLine($" Type and Offset Entry {j}");
+ Console.WriteLine($" Type: {typeOffsetFieldEntry.BaseRelocationType}");
+ Console.WriteLine($" Offset: {typeOffsetFieldEntry.Offset}");
+ }
+ }
+ }
+ }
+ Console.WriteLine();
+ }
+
///
/// Print debug table information
///