diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs index 790d56a4..5c6383fa 100644 --- a/BurnOutSharp.Builder/PortableExecutable.cs +++ b/BurnOutSharp.Builder/PortableExecutable.cs @@ -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; diff --git a/BurnOutSharp.Models/PortableExecutable/ResourceDirectoryTable.cs b/BurnOutSharp.Models/PortableExecutable/ResourceDirectoryTable.cs index 5e412e5c..cb5652bf 100644 --- a/BurnOutSharp.Models/PortableExecutable/ResourceDirectoryTable.cs +++ b/BurnOutSharp.Models/PortableExecutable/ResourceDirectoryTable.cs @@ -57,12 +57,6 @@ namespace BurnOutSharp.Models.PortableExecutable /// strings to identify Type, Name, or Language entries (depending on the /// level of the table). /// - public ResourceDirectoryEntry[] NameEntries; - - /// - /// Directory entries immediately following the Name entries that - /// use numeric IDs for Type, Name, or Language entries. - /// - public ResourceDirectoryEntry[] IDEntries; + public ResourceDirectoryEntry[] Entries; } } diff --git a/BurnOutSharp.Wrappers/PortableExecutable.cs b/BurnOutSharp.Wrappers/PortableExecutable.cs index 17a7caa2..5235b1ef 100644 --- a/BurnOutSharp.Wrappers/PortableExecutable.cs +++ b/BurnOutSharp.Wrappers/PortableExecutable.cs @@ -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 }); } + /// + /// Find unparsed resources by type name + /// + /// Type name to check for + /// Enumerable of matching resources + public IEnumerable FindResourceByNamedType(string typeName) + { + return ResourceData.Where(kvp => kvp.Key.Contains(typeName)) + .Select(kvp => kvp.Value as byte[]) + .Where(b => b != null); + } + /// /// Find unparsed resources by string value /// @@ -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; } - /// - /// Parse the resource directory table information - /// - private void ParseResourceDirectoryTable(Models.PortableExecutable.ResourceDirectoryTable table, List types) - { - for (int i = 0; i < table.NumberOfNameEntries; i++) - { - var entry = table.NameEntries[i]; - var newTypes = new List(types ?? new List()); - 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(types ?? new List()); - newTypes.Add(entry.IntegerID); - ParseIDResourceDirectoryEntry(entry, newTypes); - } - } - - /// - /// Parse the name resource directory entry information - /// - private void ParseNameResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List types) - { - if (entry.DataEntry != null) - ParseResourceDataEntry(entry.DataEntry, types); - else if (entry.Subdirectory != null) - ParseResourceDirectoryTable(entry.Subdirectory, types); - } - - /// - /// Parse the ID resource directory entry information - /// - private void ParseIDResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List types) - { - if (entry.DataEntry != null) - ParseResourceDataEntry(entry.DataEntry, types); - else if (entry.Subdirectory != null) - ParseResourceDirectoryTable(entry.Subdirectory, types); - } - - /// - /// Parse the resource data entry information - /// - /// - /// 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. - /// - private void ParseResourceDataEntry(Models.PortableExecutable.ResourceDataEntry entry, List 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(types ?? new List()); - 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(types ?? new List()); - newTypes.Add(entry.IntegerID); - PrintIDResourceDirectoryEntry(entry, level + 1, newTypes); + PrintResourceDirectoryEntry(entry, level + 1, newTypes); } } } /// - /// Pretty print the name resource directory entry information + /// Pretty print the resource directory entry information /// - private static void PrintNameResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List types) + private static void PrintResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List 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}"); + } - /// - /// Pretty print the ID resource directory entry information - /// - private static void PrintIDResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List 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 + + /// + /// Parse the resource directory table information + /// + private void ParseResourceDirectoryTable(Models.PortableExecutable.ResourceDirectoryTable table, List types) + { + int totalEntries = table.NumberOfNameEntries + table.NumberOfIDEntries; + for (int i = 0; i < totalEntries; i++) + { + var entry = table.Entries[i]; + var newTypes = new List(types ?? new List()); + + if (entry.Name != null) + newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString ?? new byte[0])); + else + newTypes.Add(entry.IntegerID); + + ParseResourceDirectoryEntry(entry, newTypes); + } + } + + /// + /// Parse the name resource directory entry information + /// + private void ParseResourceDirectoryEntry(Models.PortableExecutable.ResourceDirectoryEntry entry, List types) + { + if (entry.DataEntry != null) + ParseResourceDataEntry(entry.DataEntry, types); + else if (entry.Subdirectory != null) + ParseResourceDirectoryTable(entry.Subdirectory, types); + } + + /// + /// Parse the resource data entry information + /// + /// + /// 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. + /// + private void ParseResourceDataEntry(Models.PortableExecutable.ResourceDataEntry entry, List 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 } } \ No newline at end of file