Fix resource entry handling

This commit is contained in:
Matt Nadareski
2022-12-03 14:22:54 -08:00
parent 7d2edd315c
commit ce5e2982d2
3 changed files with 242 additions and 341 deletions

View File

@@ -1089,54 +1089,45 @@ namespace BurnOutSharp.Builder
resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16(ref offset);
// Perform top-level pass of data
if (resourceDirectoryTable.NumberOfNameEntries > 0)
int totalEntryCount = resourceDirectoryTable.NumberOfNameEntries + resourceDirectoryTable.NumberOfIDEntries;
if (totalEntryCount > 0)
{
resourceDirectoryTable.NameEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfNameEntries];
for (int i = 0; i < resourceDirectoryTable.NumberOfNameEntries; i++)
resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount];
for (int i = 0; i < totalEntryCount; i++)
{
var entry = new ResourceDirectoryEntry();
entry.NameOffset = data.ReadUInt32(ref offset);
uint newOffset = data.ReadUInt32(ref offset);
if ((newOffset & 0x80000000) != 0)
entry.NameOffset = newOffset & ~0x80000000;
else
entry.IntegerID = newOffset;
newOffset = data.ReadUInt32(ref offset);
if ((newOffset & 0x80000000) != 0)
entry.SubdirectoryOffset = newOffset & ~0x80000000;
else
entry.DataEntryOffset = newOffset;
// Read the name from the offset
int currentOffset = offset;
offset = (int)(entry.NameOffset + initialOffset);
var resourceDirectoryString = new ResourceDirectoryString();
resourceDirectoryString.Length = data.ReadUInt16(ref offset);
resourceDirectoryString.UnicodeString = data.ReadBytes(ref offset, resourceDirectoryString.Length);
entry.Name = resourceDirectoryString;
offset = currentOffset;
// Read the name from the offset, if needed
if (entry.NameOffset != default)
{
int currentOffset = offset;
offset = (int)(entry.NameOffset + initialOffset);
var resourceDirectoryString = new ResourceDirectoryString();
resourceDirectoryString.Length = data.ReadUInt16(ref offset);
resourceDirectoryString.UnicodeString = data.ReadBytes(ref offset, resourceDirectoryString.Length * 2);
entry.Name = resourceDirectoryString;
offset = currentOffset;
}
resourceDirectoryTable.NameEntries[i] = entry;
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
resourceDirectoryTable.IDEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfIDEntries];
for (int i = 0; i < resourceDirectoryTable.NumberOfIDEntries; i++)
{
var entry = new ResourceDirectoryEntry();
entry.IntegerID = data.ReadUInt32(ref offset);
uint newOffset = data.ReadUInt32(ref offset);
if ((newOffset & 0x80000000) != 0)
entry.SubdirectoryOffset = newOffset & ~0x80000000;
else
entry.DataEntryOffset = newOffset;
resourceDirectoryTable.IDEntries[i] = entry;
resourceDirectoryTable.Entries[i] = entry;
}
}
// Read all leaves at this level
if (resourceDirectoryTable.NumberOfNameEntries > 0)
if (totalEntryCount > 0)
{
foreach (var entry in resourceDirectoryTable.NameEntries)
foreach (var entry in resourceDirectoryTable.Entries)
{
if (entry.SubdirectoryOffset != 0)
continue;
@@ -1157,45 +1148,11 @@ namespace BurnOutSharp.Builder
entry.DataEntry = resourceDataEntry;
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
foreach (var entry in resourceDirectoryTable.IDEntries)
{
if (entry.SubdirectoryOffset != 0)
continue;
int newOffset = (int)(entry.DataEntryOffset + initialOffset);
var resourceDataEntry = new ResourceDataEntry();
resourceDataEntry.DataRVA = data.ReadUInt32(ref newOffset);
resourceDataEntry.Size = data.ReadUInt32(ref newOffset);
resourceDataEntry.Codepage = data.ReadUInt32(ref newOffset);
resourceDataEntry.Reserved = data.ReadUInt32(ref newOffset);
// Read the data from the offset
newOffset = (int)resourceDataEntry.DataRVA.ConvertVirtualAddress(sections);
if (newOffset > 0)
resourceDataEntry.Data = data.ReadBytes(ref newOffset, (int)resourceDataEntry.Size);
entry.DataEntry = resourceDataEntry;
}
}
// Now go one level lower
if (resourceDirectoryTable.NumberOfNameEntries > 0)
if (totalEntryCount > 0)
{
foreach (var entry in resourceDirectoryTable.NameEntries)
{
if (entry.DataEntryOffset != 0)
continue;
int newOffset = (int)(entry.SubdirectoryOffset + initialOffset);
entry.Subdirectory = ParseResourceDirectoryTable(data, newOffset, initialOffset, sections);
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
foreach (var entry in resourceDirectoryTable.IDEntries)
foreach (var entry in resourceDirectoryTable.Entries)
{
if (entry.DataEntryOffset != 0)
continue;
@@ -2294,82 +2251,46 @@ namespace BurnOutSharp.Builder
resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16();
// Perform top-level pass of data
if (resourceDirectoryTable.NumberOfNameEntries > 0)
int totalEntryCount = resourceDirectoryTable.NumberOfNameEntries + resourceDirectoryTable.NumberOfIDEntries;
if (totalEntryCount > 0)
{
resourceDirectoryTable.NameEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfNameEntries];
for (int i = 0; i < resourceDirectoryTable.NumberOfNameEntries; i++)
resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount];
for (int i = 0; i < totalEntryCount; i++)
{
var entry = new ResourceDirectoryEntry();
entry.NameOffset = data.ReadUInt32();
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
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);
entry.Name = resourceDirectoryString;
data.Seek(currentOffset, SeekOrigin.Begin);
// 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.NameEntries[i] = entry;
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
resourceDirectoryTable.IDEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfIDEntries];
for (int i = 0; i < resourceDirectoryTable.NumberOfIDEntries; i++)
{
var entry = new ResourceDirectoryEntry();
entry.IntegerID = data.ReadUInt32();
uint offset = data.ReadUInt32();
if ((offset & 0x80000000) != 0)
entry.SubdirectoryOffset = offset & ~0x80000000;
else
entry.DataEntryOffset = offset;
resourceDirectoryTable.IDEntries[i] = entry;
resourceDirectoryTable.Entries[i] = entry;
}
}
// Read all leaves at this level
if (resourceDirectoryTable.NumberOfNameEntries > 0)
if (totalEntryCount > 0)
{
foreach (var entry in resourceDirectoryTable.NameEntries)
{
if (entry.SubdirectoryOffset != 0)
continue;
uint offset = entry.DataEntryOffset + (uint)initialOffset;
data.Seek(offset, SeekOrigin.Begin);
var resourceDataEntry = new ResourceDataEntry();
resourceDataEntry.DataRVA = data.ReadUInt32();
resourceDataEntry.Size = data.ReadUInt32();
resourceDataEntry.Codepage = data.ReadUInt32();
resourceDataEntry.Reserved = data.ReadUInt32();
// Read the data from the offset
offset = resourceDataEntry.DataRVA.ConvertVirtualAddress(sections);
if (offset != 0)
{
data.Seek(offset, SeekOrigin.Begin);
resourceDataEntry.Data = data.ReadBytes((int)resourceDataEntry.Size);
}
entry.DataEntry = resourceDataEntry;
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
foreach (var entry in resourceDirectoryTable.IDEntries)
foreach (var entry in resourceDirectoryTable.Entries)
{
if (entry.SubdirectoryOffset != 0)
continue;
@@ -2396,21 +2317,9 @@ namespace BurnOutSharp.Builder
}
// Now go one level lower
if (resourceDirectoryTable.NumberOfNameEntries > 0)
if (totalEntryCount > 0)
{
foreach (var entry in resourceDirectoryTable.NameEntries)
{
if (entry.DataEntryOffset != 0)
continue;
uint offset = entry.SubdirectoryOffset + (uint)initialOffset;
data.Seek(offset, SeekOrigin.Begin);
entry.Subdirectory = ParseResourceDirectoryTable(data, initialOffset, sections);
}
}
if (resourceDirectoryTable.NumberOfIDEntries > 0)
{
foreach (var entry in resourceDirectoryTable.IDEntries)
foreach (var entry in resourceDirectoryTable.Entries)
{
if (entry.DataEntryOffset != 0)
continue;

View File

@@ -57,12 +57,6 @@ namespace BurnOutSharp.Models.PortableExecutable
/// strings to identify Type, Name, or Language entries (depending on the
/// level of the table).
/// </summary>
public ResourceDirectoryEntry[] NameEntries;
/// <summary>
/// Directory entries immediately following the Name entries that
/// use numeric IDs for Type, Name, or Language entries.
/// </summary>
public ResourceDirectoryEntry[] IDEntries;
public ResourceDirectoryEntry[] Entries;
}
}

View File

@@ -865,7 +865,7 @@ namespace BurnOutSharp.Wrappers
.Where(dit => dit?.TitleResource != null)
.Any(dit => dit.TitleResource.Contains(title));
}
else if (d.ExtendedDialogItemTemplates!= null)
else if (d.ExtendedDialogItemTemplates != null)
{
return d.ExtendedDialogItemTemplates
.Where(edit => edit?.TitleResource != null)
@@ -876,6 +876,18 @@ namespace BurnOutSharp.Wrappers
});
}
/// <summary>
/// Find unparsed resources by type name
/// </summary>
/// <param name="typeName">Type name to check for</param>
/// <returns>Enumerable of matching resources</returns>
public IEnumerable<byte[]> FindResourceByNamedType(string typeName)
{
return ResourceData.Where(kvp => kvp.Key.Contains(typeName))
.Select(kvp => kvp.Value as byte[])
.Where(b => b != null);
}
/// <summary>
/// Find unparsed resources by string value
/// </summary>
@@ -886,7 +898,7 @@ namespace BurnOutSharp.Wrappers
return ResourceData.Select(r => r.Value)
.Select(r => r as byte[])
.Where(b => b != null)
.Where(b =>
.Where(b =>
{
try
{
@@ -966,150 +978,6 @@ namespace BurnOutSharp.Wrappers
return _assemblyManifest;
}
/// <summary>
/// Parse the resource directory table information
/// </summary>
private void ParseResourceDirectoryTable(Models.PortableExecutable.ResourceDirectoryTable table, List<object> types)
{
for (int i = 0; i < table.NumberOfNameEntries; i++)
{
var entry = table.NameEntries[i];
var newTypes = new List<object>(types ?? new List<object>());
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0]));
ParseNameResourceDirectoryEntry(entry, newTypes);
}
for (int i = 0; i < table.NumberOfIDEntries; i++)
{
var entry = table.IDEntries[i];
var newTypes = new List<object>(types ?? new List<object>());
newTypes.Add(entry.IntegerID);
ParseIDResourceDirectoryEntry(entry, newTypes);
}
}
/// <summary>
/// Parse the name resource directory entry information
/// </summary>
private void ParseNameResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List<object> types)
{
if (entry.DataEntry != null)
ParseResourceDataEntry(entry.DataEntry, types);
else if (entry.Subdirectory != null)
ParseResourceDirectoryTable(entry.Subdirectory, types);
}
/// <summary>
/// Parse the ID resource directory entry information
/// </summary>
private void ParseIDResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List<object> types)
{
if (entry.DataEntry != null)
ParseResourceDataEntry(entry.DataEntry, types);
else if (entry.Subdirectory != null)
ParseResourceDirectoryTable(entry.Subdirectory, types);
}
/// <summary>
/// Parse the resource data entry information
/// </summary>
/// <remarks>
/// When caching the version information and assembly manifest, this code assumes that there is only one of each
/// of those resources in the entire exectuable. This means that only the last found version or manifest will
/// ever be cached.
/// </remarks>
private void ParseResourceDataEntry(Models.PortableExecutable.ResourceDataEntry entry, List<object> types)
{
// Create the key and value objects
string key = types == null ? $"UNKNOWN_{Guid.NewGuid()}" : string.Join(", ", types);
object value = entry.Data;
// If we have a known resource type
if (types != null && types.Count > 0 && types[0] is uint resourceType)
{
switch ((Models.PortableExecutable.ResourceType)resourceType)
{
case Models.PortableExecutable.ResourceType.RT_CURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_BITMAP:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MENU:
value = entry.AsMenu();
break;
case Models.PortableExecutable.ResourceType.RT_DIALOG:
value = entry.AsDialogBox();
break;
case Models.PortableExecutable.ResourceType.RT_STRING:
value = entry.AsStringTable();
break;
case Models.PortableExecutable.ResourceType.RT_FONTDIR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_FONT:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ACCELERATOR:
value = entry.AsAcceleratorTableResource();
break;
case Models.PortableExecutable.ResourceType.RT_RCDATA:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MESSAGETABLE:
value = entry.AsMessageResourceData();
break;
case Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_GROUP_ICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_VERSION:
_versionInfo = entry.AsVersionInfo();
value = _versionInfo;
break;
case Models.PortableExecutable.ResourceType.RT_DLGINCLUDE:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_PLUGPLAY:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_VXD:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ANICURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ANIICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_HTML:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MANIFEST:
_assemblyManifest = entry.AsAssemblyManifest();
value = _versionInfo;
break;
default:
value = entry.Data;
break;
}
}
// If we have a custom resource type
else if (types != null && types.Count > 0 && types[0] is string)
{
value = entry.Data;
}
// Add the key and value to the cache
_resourceData[key] = value;
}
#endregion
#region Printing
@@ -1277,96 +1145,112 @@ namespace BurnOutSharp.Wrappers
{
Console.WriteLine(" Export Table (1)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ExportTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ExportTable.Size}");
}
if (_executable.OptionalHeader.ImportTable != null)
{
Console.WriteLine(" Import Table (2)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ImportTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ImportTable.Size}");
}
if (_executable.OptionalHeader.ResourceTable != null)
{
Console.WriteLine(" Resource Table (3)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ResourceTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ResourceTable.Size}");
}
if (_executable.OptionalHeader.ExceptionTable != null)
{
Console.WriteLine(" Exception Table (4)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ExceptionTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ExceptionTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ExceptionTable.Size}");
}
if (_executable.OptionalHeader.CertificateTable != null)
{
Console.WriteLine(" Certificate Table (5)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.CertificateTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.CertificateTable.Size}");
}
if (_executable.OptionalHeader.BaseRelocationTable != null)
{
Console.WriteLine(" Base Relocation Table (6)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.BaseRelocationTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.BaseRelocationTable.Size}");
}
if (_executable.OptionalHeader.Debug != null)
{
Console.WriteLine(" Debug Table (7)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.Debug.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.Debug.Size}");
}
if (_executable.OptionalHeader.NumberOfRvaAndSizes >= 8)
{
Console.WriteLine(" Architecture Table (8)");
Console.WriteLine($" Virtual address: 0");
Console.WriteLine($" Physical address: 0");
Console.WriteLine($" Size: 0");
}
if (_executable.OptionalHeader.GlobalPtr != null)
{
Console.WriteLine(" Global Pointer Register (9)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.GlobalPtr.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.GlobalPtr.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.GlobalPtr.Size}");
}
if (_executable.OptionalHeader.ThreadLocalStorageTable != null)
{
Console.WriteLine(" Thread Local Storage (TLS) Table (10)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ThreadLocalStorageTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ThreadLocalStorageTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ThreadLocalStorageTable.Size}");
}
if (_executable.OptionalHeader.LoadConfigTable != null)
{
Console.WriteLine(" Load Config Table (11)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.LoadConfigTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.LoadConfigTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.LoadConfigTable.Size}");
}
if (_executable.OptionalHeader.BoundImport != null)
{
Console.WriteLine(" Bound Import Table (12)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.BoundImport.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.BoundImport.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.BoundImport.Size}");
}
if (_executable.OptionalHeader.ImportAddressTable != null)
{
Console.WriteLine(" Import Address Table (13)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.ImportAddressTable.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.ImportAddressTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.ImportAddressTable.Size}");
}
if (_executable.OptionalHeader.DelayImportDescriptor != null)
{
Console.WriteLine(" Delay Import Descriptior (14)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.DelayImportDescriptor.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.DelayImportDescriptor.Size}");
}
if (_executable.OptionalHeader.CLRRuntimeHeader != null)
{
Console.WriteLine(" CLR Runtime Header (15)");
Console.WriteLine($" Virtual address: {_executable.OptionalHeader.CLRRuntimeHeader.VirtualAddress}");
Console.WriteLine($" Physical address: {_executable.OptionalHeader.CLRRuntimeHeader.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size: {_executable.OptionalHeader.CLRRuntimeHeader.Size}");
}
if (_executable.OptionalHeader.NumberOfRvaAndSizes >= 16)
{
Console.WriteLine(" Reserved (16)");
Console.WriteLine($" Virtual address: 0");
Console.WriteLine($" Physical address: 0");
Console.WriteLine($" Size: 0");
}
}
@@ -1393,6 +1277,7 @@ namespace BurnOutSharp.Wrappers
Console.WriteLine($" Name = {Encoding.UTF8.GetString(entry.Name)}");
Console.WriteLine($" Virtual size = {entry.VirtualSize}");
Console.WriteLine($" Virtual address = {entry.VirtualAddress}");
Console.WriteLine($" Physical address: {entry.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable)}");
Console.WriteLine($" Size of raw data = {entry.SizeOfRawData}");
Console.WriteLine($" Pointer to raw data = {entry.PointerToRawData}");
Console.WriteLine($" Pointer to relocations = {entry.PointerToRelocations}");
@@ -1953,69 +1838,47 @@ namespace BurnOutSharp.Wrappers
Console.WriteLine($"{padding}Number of ID entries: {table.NumberOfIDEntries}");
Console.WriteLine();
Console.WriteLine($"{padding}Name entries");
Console.WriteLine($"{padding}Entries");
Console.WriteLine($"{padding}-------------------------");
if (table.NumberOfNameEntries == 0)
if (table.NumberOfNameEntries == 0 && table.NumberOfIDEntries == 0)
{
Console.WriteLine($"{padding}No named entries");
Console.WriteLine($"{padding}No entries");
Console.WriteLine();
}
else
{
for (int i = 0; i < table.NumberOfNameEntries; i++)
for (int i = 0; i < table.Entries.Length; i++)
{
var entry = table.NameEntries[i];
var entry = table.Entries[i];
var newTypes = new List<object>(types ?? new List<object>());
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0]));
PrintNameResourceDirectoryEntry(entry, level + 1, newTypes);
}
}
if (entry.Name != null)
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0]));
else
newTypes.Add(entry.IntegerID);
Console.WriteLine($"{padding}ID entries");
Console.WriteLine($"{padding}-------------------------");
if (table.NumberOfIDEntries == 0)
{
Console.WriteLine($"{padding}No ID entries");
Console.WriteLine();
}
else
{
for (int i = 0; i < table.NumberOfIDEntries; i++)
{
var entry = table.IDEntries[i];
var newTypes = new List<object>(types ?? new List<object>());
newTypes.Add(entry.IntegerID);
PrintIDResourceDirectoryEntry(entry, level + 1, newTypes);
PrintResourceDirectoryEntry(entry, level + 1, newTypes);
}
}
}
/// <summary>
/// Pretty print the name resource directory entry information
/// Pretty print the resource directory entry information
/// </summary>
private static void PrintNameResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List<object> types)
private static void PrintResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List<object> types)
{
string padding = new string(' ', (level + 1) * 2);
Console.WriteLine($"{padding}Item level: {level}");
Console.WriteLine($"{padding}Name offset: {entry.NameOffset}");
Console.WriteLine($"{padding}Name ({entry.Name.Length}): {Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0])}");
if (entry.DataEntry != null)
PrintResourceDataEntry(entry.DataEntry, level: level + 1, types);
else if (entry.Subdirectory != null)
PrintResourceDirectoryTable(entry.Subdirectory, level: level + 1, types);
}
if (entry.NameOffset != default)
{
Console.WriteLine($"{padding}Name offset: {entry.NameOffset}");
Console.WriteLine($"{padding}Name ({entry.Name.Length}): {Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0])}");
}
else
{
Console.WriteLine($"{padding}Integer ID: {entry.IntegerID}");
}
/// <summary>
/// Pretty print the ID resource directory entry information
/// </summary>
private static void PrintIDResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List<object> types)
{
string padding = new string(' ', (level + 1) * 2);
Console.WriteLine($"{padding}Item level: {level}");
Console.WriteLine($"{padding}Integer ID: {entry.IntegerID}");
if (entry.DataEntry != null)
PrintResourceDataEntry(entry.DataEntry, level: level + 1, types);
else if (entry.Subdirectory != null)
@@ -2030,7 +1893,7 @@ namespace BurnOutSharp.Wrappers
string padding = new string(' ', (level + 1) * 2);
// TODO: Use ordered list of base types to determine the shape of the data
//Console.WriteLine($"{padding}Base types: {string.Join(", ", types)}");
Console.WriteLine($"{padding}Base types: {string.Join(", ", types)}");
Console.WriteLine($"{padding}Entry level: {level}");
Console.WriteLine($"{padding}Data RVA: {entry.DataRVA}");
@@ -2647,5 +2510,140 @@ namespace BurnOutSharp.Wrappers
}
#endregion
#region Resource Parsing
/// <summary>
/// Parse the resource directory table information
/// </summary>
private void ParseResourceDirectoryTable(Models.PortableExecutable.ResourceDirectoryTable table, List<object> types)
{
int totalEntries = table.NumberOfNameEntries + table.NumberOfIDEntries;
for (int i = 0; i < totalEntries; i++)
{
var entry = table.Entries[i];
var newTypes = new List<object>(types ?? new List<object>());
if (entry.Name != null)
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0]));
else
newTypes.Add(entry.IntegerID);
ParseResourceDirectoryEntry(entry, newTypes);
}
}
/// <summary>
/// Parse the name resource directory entry information
/// </summary>
private void ParseResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List<object> types)
{
if (entry.DataEntry != null)
ParseResourceDataEntry(entry.DataEntry, types);
else if (entry.Subdirectory != null)
ParseResourceDirectoryTable(entry.Subdirectory, types);
}
/// <summary>
/// Parse the resource data entry information
/// </summary>
/// <remarks>
/// When caching the version information and assembly manifest, this code assumes that there is only one of each
/// of those resources in the entire exectuable. This means that only the last found version or manifest will
/// ever be cached.
/// </remarks>
private void ParseResourceDataEntry(Models.PortableExecutable.ResourceDataEntry entry, List<object> types)
{
// Create the key and value objects
string key = types == null ? $"UNKNOWN_{Guid.NewGuid()}" : string.Join(", ", types);
object value = entry.Data;
// If we have a known resource type
if (types != null && types.Count > 0 && types[0] is uint resourceType)
{
switch ((Models.PortableExecutable.ResourceType)resourceType)
{
case Models.PortableExecutable.ResourceType.RT_CURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_BITMAP:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MENU:
value = entry.AsMenu();
break;
case Models.PortableExecutable.ResourceType.RT_DIALOG:
value = entry.AsDialogBox();
break;
case Models.PortableExecutable.ResourceType.RT_STRING:
value = entry.AsStringTable();
break;
case Models.PortableExecutable.ResourceType.RT_FONTDIR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_FONT:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ACCELERATOR:
value = entry.AsAcceleratorTableResource();
break;
case Models.PortableExecutable.ResourceType.RT_RCDATA:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MESSAGETABLE:
value = entry.AsMessageResourceData();
break;
case Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_GROUP_ICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_VERSION:
_versionInfo = entry.AsVersionInfo();
value = _versionInfo;
break;
case Models.PortableExecutable.ResourceType.RT_DLGINCLUDE:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_PLUGPLAY:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_VXD:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ANICURSOR:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_ANIICON:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_HTML:
value = entry.Data;
break;
case Models.PortableExecutable.ResourceType.RT_MANIFEST:
_assemblyManifest = entry.AsAssemblyManifest();
value = _versionInfo;
break;
default:
value = entry.Data;
break;
}
}
// If we have a custom resource type
else if (types != null && types.Count > 0 && types[0] is string)
{
value = entry.Data;
}
// Add the key and value to the cache
_resourceData[key] = value;
}
#endregion
}
}