mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-25 07:40:04 +00:00
Add PE resource table parsing (incomplete)
This commit is contained in:
@@ -330,15 +330,16 @@ namespace BurnOutSharp.Builder
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Write extension to parse resource data
|
||||
#region Portable Executable
|
||||
|
||||
/// <summary>
|
||||
/// Convert a virtual address to a physical one
|
||||
/// Convert a relative virtual address to a physical one
|
||||
/// </summary>
|
||||
/// <param name="virtualAddress">Virtual address to convert</param>
|
||||
/// <param name="rva">Relative virtual address to convert</param>
|
||||
/// <param name="sections">Array of sections to check against</param>
|
||||
/// <returns>Physical address, 0 on error</returns>
|
||||
public static uint ConvertVirtualAddress(this uint virtualAddress, Models.PortableExecutable.SectionHeader[] sections)
|
||||
public static uint ConvertVirtualAddress(this uint rva, Models.PortableExecutable.SectionHeader[] sections)
|
||||
{
|
||||
// Loop through all of the sections
|
||||
for (int i = 0; i < sections.Length; i++)
|
||||
@@ -353,8 +354,8 @@ namespace BurnOutSharp.Builder
|
||||
|
||||
// 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;
|
||||
if (rva >= section.VirtualAddress && rva <= section.VirtualAddress + section.VirtualSize)
|
||||
return rva - section.VirtualAddress + section.PointerToRawData;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -114,6 +114,33 @@ namespace BurnOutSharp.Builder
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: COFFStringTable (Only if COFFSymbolTable?)
|
||||
// TODO: AttributeCertificateTable
|
||||
// TODO: DelayLoadDirectoryTable
|
||||
|
||||
// TODO: Port to byte once done
|
||||
#region Resource Directory Table
|
||||
|
||||
// Should also be in the '.rsrc' section
|
||||
if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress != 0)
|
||||
{
|
||||
// If the offset for the resource directory table doesn't exist
|
||||
int tableAddress = initialOffset
|
||||
+ (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the resource directory table
|
||||
var resourceDirectoryTable = ParseResourceDirectoryTable(data, tableAddress, tableAddress, executable.SectionTable);
|
||||
if (resourceDirectoryTable == null)
|
||||
return null;
|
||||
|
||||
// Set the resource directory table
|
||||
executable.ResourceDirectoryTable = resourceDirectoryTable;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Finish implementing PE parsing
|
||||
return executable;
|
||||
}
|
||||
@@ -507,6 +534,146 @@ namespace BurnOutSharp.Builder
|
||||
return coffSymbolTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a resource directory table
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <param name="initialOffset">Initial offset to use in address comparisons</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled resource directory table on success, null on error</returns>
|
||||
private static ResourceDirectoryTable ParseResourceDirectoryTable(byte[] data, int offset, long initialOffset, SectionHeader[] sections)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var resourceDirectoryTable = new ResourceDirectoryTable();
|
||||
|
||||
resourceDirectoryTable.Characteristics = data.ReadUInt32(ref offset);
|
||||
resourceDirectoryTable.TimeDateStamp = data.ReadUInt32(ref offset);
|
||||
resourceDirectoryTable.MajorVersion = data.ReadUInt16(ref offset);
|
||||
resourceDirectoryTable.MinorVersion = data.ReadUInt16(ref offset);
|
||||
resourceDirectoryTable.NumberOfNameEntries = data.ReadUInt16(ref offset);
|
||||
resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16(ref offset);
|
||||
|
||||
// Perform top-level pass of data
|
||||
if (resourceDirectoryTable.NumberOfNameEntries > 0)
|
||||
{
|
||||
resourceDirectoryTable.NameEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfNameEntries];
|
||||
for (int i = 0; i < resourceDirectoryTable.NumberOfNameEntries; i++)
|
||||
{
|
||||
var entry = new ResourceDirectoryEntry();
|
||||
entry.NameOffset = data.ReadUInt32(ref offset);
|
||||
|
||||
uint newOffset = data.ReadUInt32(ref offset);
|
||||
if ((newOffset & 0xF0000000) != 0)
|
||||
entry.SubdirectoryOffset = newOffset & ~0xF0000000;
|
||||
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;
|
||||
|
||||
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 & 0xF0000000) != 0)
|
||||
entry.SubdirectoryOffset = newOffset & ~0xF0000000;
|
||||
else
|
||||
entry.DataEntryOffset = newOffset;
|
||||
|
||||
resourceDirectoryTable.IDEntries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Read all leaves at this level
|
||||
if (resourceDirectoryTable.NumberOfNameEntries > 0)
|
||||
{
|
||||
foreach (var entry in resourceDirectoryTable.NameEntries)
|
||||
{
|
||||
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 offset);
|
||||
resourceDataEntry.Reserved = data.ReadUInt32(ref offset);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (entry.DataEntryOffset != 0)
|
||||
continue;
|
||||
|
||||
int newOffset = (int)(entry.SubdirectoryOffset + initialOffset);
|
||||
entry.Subdirectory = ParseResourceDirectoryTable(data, newOffset, initialOffset, sections);
|
||||
}
|
||||
}
|
||||
|
||||
return resourceDirectoryTable;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
@@ -616,6 +783,34 @@ namespace BurnOutSharp.Builder
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: COFFStringTable (Only if COFFSymbolTable?)
|
||||
// TODO: AttributeCertificateTable
|
||||
// TODO: DelayLoadDirectoryTable
|
||||
|
||||
// TODO: Port to byte once done
|
||||
#region Resource Directory Table
|
||||
|
||||
// Should also be in the '.rsrc' section
|
||||
if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress != 0)
|
||||
{
|
||||
// If the offset for the resource directory table doesn't exist
|
||||
int tableAddress = initialOffset
|
||||
+ (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the resource directory table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var resourceDirectoryTable = ParseResourceDirectoryTable(data, data.Position, executable.SectionTable);
|
||||
if (resourceDirectoryTable == null)
|
||||
return null;
|
||||
|
||||
// Set the resource directory table
|
||||
executable.ResourceDirectoryTable = resourceDirectoryTable;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Finish implementing PE parsing
|
||||
return executable;
|
||||
}
|
||||
@@ -1005,6 +1200,156 @@ namespace BurnOutSharp.Builder
|
||||
return coffSymbolTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource directory table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="initialOffset">Initial offset to use in address comparisons</param>
|
||||
/// <param name="sections">Section table to use for virtual address translation</param>
|
||||
/// <returns>Filled resource directory table on success, null on error</returns>
|
||||
private static ResourceDirectoryTable ParseResourceDirectoryTable(Stream data, long initialOffset, SectionHeader[] sections)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var resourceDirectoryTable = new ResourceDirectoryTable();
|
||||
|
||||
resourceDirectoryTable.Characteristics = data.ReadUInt32();
|
||||
resourceDirectoryTable.TimeDateStamp = data.ReadUInt32();
|
||||
resourceDirectoryTable.MajorVersion = data.ReadUInt16();
|
||||
resourceDirectoryTable.MinorVersion = data.ReadUInt16();
|
||||
resourceDirectoryTable.NumberOfNameEntries = data.ReadUInt16();
|
||||
resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16();
|
||||
|
||||
// Perform top-level pass of data
|
||||
if (resourceDirectoryTable.NumberOfNameEntries > 0)
|
||||
{
|
||||
resourceDirectoryTable.NameEntries = new ResourceDirectoryEntry[resourceDirectoryTable.NumberOfNameEntries];
|
||||
for (int i = 0; i < resourceDirectoryTable.NumberOfNameEntries; i++)
|
||||
{
|
||||
var entry = new ResourceDirectoryEntry();
|
||||
entry.NameOffset = data.ReadUInt32();
|
||||
|
||||
uint offset = data.ReadUInt32();
|
||||
if ((offset & 0xF0000000) != 0)
|
||||
entry.SubdirectoryOffset = offset & ~0xF0000000;
|
||||
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);
|
||||
|
||||
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 & 0xF0000000) != 0)
|
||||
entry.SubdirectoryOffset = offset & ~0xF0000000;
|
||||
else
|
||||
entry.DataEntryOffset = offset;
|
||||
|
||||
resourceDirectoryTable.IDEntries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Read all leaves at this level
|
||||
if (resourceDirectoryTable.NumberOfNameEntries > 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Now go one level lower
|
||||
if (resourceDirectoryTable.NumberOfNameEntries > 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)
|
||||
{
|
||||
if (entry.DataEntryOffset != 0)
|
||||
continue;
|
||||
|
||||
uint offset = entry.SubdirectoryOffset + (uint)initialOffset;
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
entry.Subdirectory = ParseResourceDirectoryTable(data, initialOffset, sections);
|
||||
}
|
||||
}
|
||||
|
||||
return resourceDirectoryTable;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1693,6 +1693,30 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum ResourceType : uint
|
||||
{
|
||||
RT_NEWRESOURCE = 0x2000,
|
||||
RT_ERROR = 0x7FFF,
|
||||
|
||||
RT_CURSOR = 1,
|
||||
RT_BITMAP = 2,
|
||||
RT_ICON = 3,
|
||||
RT_MENU = 4,
|
||||
RT_DIALOG = 5,
|
||||
RT_STRING = 6,
|
||||
RT_FONTDIR = 7,
|
||||
RT_FONT = 8,
|
||||
RT_ACCELERATORS = 9,
|
||||
RT_RCDATA = 10,
|
||||
RT_MESSAGETABLE = 11,
|
||||
RT_GROUP_CURSOR = 12,
|
||||
RT_GROUP_ICON = 14,
|
||||
RT_VERSION = 16,
|
||||
RT_NEWBITMAP = (RT_BITMAP | RT_NEWRESOURCE),
|
||||
RT_NEWMENU = (RT_MENU | RT_NEWRESOURCE),
|
||||
RT_NEWDIALOG = (RT_DIALOG | RT_NEWRESOURCE),
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SectionFlags : uint
|
||||
{
|
||||
|
||||
@@ -56,7 +56,14 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// </summary>
|
||||
public DelayLoadDirectoryTableEntry[] DelayLoadDirectoryTable { get; set; }
|
||||
|
||||
// TODO: Left off at "The .cormeta Section (Object Only)"
|
||||
#region Named Sections
|
||||
|
||||
/// <summary>
|
||||
/// Resource directory table (.rsrc)
|
||||
/// </summary>
|
||||
public ResourceDirectoryTable ResourceDirectoryTable { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Implement and/or document the following non-modeled parts:
|
||||
// - Grouped Sections (Object Only)
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// </summary>
|
||||
public uint Size;
|
||||
|
||||
/// <summary>
|
||||
/// The resource data that is pointed to by the Data RVA field.
|
||||
/// </summary>
|
||||
public byte[] Data;
|
||||
|
||||
/// <summary>
|
||||
/// The code page that is used to decode code point values within the
|
||||
/// resource data. Typically, the code page would be the Unicode code page.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BurnOutSharp.Models.PortableExecutable
|
||||
namespace BurnOutSharp.Models.PortableExecutable
|
||||
{
|
||||
/// <summary>
|
||||
/// A leaf's Type, Name, and Language IDs are determined by the path that is
|
||||
@@ -19,29 +17,52 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// IMAGE_DIRECTORY_ENTRY_RESOURCE DataDirectory.
|
||||
/// </summary>
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public class ResourceDirectoryEntry
|
||||
{
|
||||
#region Offset 0x00
|
||||
|
||||
/// <summary>
|
||||
/// The offset of a string that gives the Type, Name, or Language ID entry,
|
||||
/// depending on level of table.
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public uint NameOffset;
|
||||
public uint NameOffset;
|
||||
|
||||
/// <summary>
|
||||
/// A string that gives the Type, Name, or Language ID entry, depending on
|
||||
/// level of table.
|
||||
/// </summary>
|
||||
public ResourceDirectoryString Name;
|
||||
|
||||
/// <summary>
|
||||
/// A 32-bit integer that identifies the Type, Name, or Language ID entry.
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public uint IntegerID;
|
||||
public uint IntegerID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Offset 0x04
|
||||
|
||||
/// <summary>
|
||||
/// High bit 0. Address of a Resource Data entry (a leaf).
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public uint DataEntryOffset;
|
||||
public uint DataEntryOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Resource data entry (a leaf).
|
||||
/// </summary>
|
||||
public ResourceDataEntry DataEntry;
|
||||
|
||||
/// <summary>
|
||||
/// High bit 1. The lower 31 bits are the address of another resource
|
||||
/// directory table (the next level down).
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public uint SubdirectoryOffset;
|
||||
public uint SubdirectoryOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Another resource directory table (the next level down).
|
||||
/// </summary>
|
||||
public ResourceDirectoryTable Subdirectory;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,5 +51,18 @@ namespace BurnOutSharp.Models.PortableExecutable
|
||||
/// use numeric IDs for Type, Name, or Language entries.
|
||||
/// </summary>
|
||||
public ushort NumberOfIDEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Directory entries immediately following the table that use
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user