diff --git a/BurnOutSharp.Builder/BFPK.cs b/BurnOutSharp.Builder/BFPK.cs index 8f6aa46b..06f70f57 100644 --- a/BurnOutSharp.Builder/BFPK.cs +++ b/BurnOutSharp.Builder/BFPK.cs @@ -5,7 +5,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public class BFPK { #region Byte Data @@ -26,95 +25,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new archive to fill - var archive = new Archive(); - - #region Header - - // Try to parse the header - var header = ParseHeader(data, ref offset); - if (header == null) - return null; - - // Set the archive header - archive.Header = header; - - #endregion - - #region Files - - // If we have any files - if (header.Files > 0) - { - var files = new FileEntry[header.Files]; - - // Read all entries in turn - for (int i = 0; i < header.Files; i++) - { - var file = ParseFileEntry(data, ref offset); - if (file == null) - return null; - - files[i] = file; - } - - // Set the files - archive.Files = files; - } - - #endregion - - return archive; - } - - /// - /// Parse a byte array into a header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled header on success, null on error - private static Header ParseHeader(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - Header header = new Header(); - - header.Magic = data.ReadUInt32(ref offset); - if (header.Magic != 0x4b504642) - return null; - - header.Version = data.ReadInt32(ref offset); - header.Files = data.ReadInt32(ref offset); - - return header; - } - - /// - /// Parse a byte array into a file entry - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled file entry on success, null on error - private static FileEntry ParseFileEntry(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - FileEntry fileEntry = new FileEntry(); - - fileEntry.NameSize = data.ReadInt32(ref offset); - if (fileEntry.NameSize > 0) - fileEntry.Name = new string(data.ReadBytes(ref offset, fileEntry.NameSize).Select(b => (char)b).ToArray()); - - fileEntry.UncompressedSize = data.ReadInt32(ref offset); - fileEntry.Offset = data.ReadInt32(ref offset); - if (fileEntry.Offset > 0) - { - int entryOffset = fileEntry.Offset; - fileEntry.CompressedSize = data.ReadInt32(ref entryOffset); - } - - return fileEntry; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseArchive(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/LinearExecutable.cs b/BurnOutSharp.Builder/LinearExecutable.cs index 26613774..35ade8e2 100644 --- a/BurnOutSharp.Builder/LinearExecutable.cs +++ b/BurnOutSharp.Builder/LinearExecutable.cs @@ -3,7 +3,6 @@ using BurnOutSharp.Models.LinearExecutable; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public static class LinearExecutable { #region Byte Data @@ -24,34 +23,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new executable to fill - var executable = new Executable(); - - // Parse the MS-DOS stub - var stub = MSDOS.ParseExecutable(data, offset); - if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0) - return null; - - // Set the MS-DOS stub - executable.Stub = stub; - - // TODO: Implement LE/LX parsing - return null; - } - - /// - /// Parse a byte array into a Linear Executable information block - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled information block on success, null on error - private static InformationBlock ParseInformationBlock(byte[] data, int offset) - { - // TODO: Implement LE/LX information block parsing - return null; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseExecutable(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/MSDOS.cs b/BurnOutSharp.Builder/MSDOS.cs index 8c7c46ed..9bdc064a 100644 --- a/BurnOutSharp.Builder/MSDOS.cs +++ b/BurnOutSharp.Builder/MSDOS.cs @@ -4,7 +4,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public static class MSDOS { #region Byte Data @@ -25,128 +24,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new executable to fill - var executable = new Executable(); - - #region Executable Header - - // Try to parse the executable header - var executableHeader = ParseExecutableHeader(data, offset); - if (executableHeader == null) - return null; - - // Set the executable header - executable.Header = executableHeader; - - #endregion - - #region Relocation Table - - // If the offset for the relocation table doesn't exist - int tableAddress = initialOffset + executableHeader.RelocationTableAddr; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the relocation table - var relocationTable = ParseRelocationTable(data, tableAddress, executableHeader.RelocationItems); - if (relocationTable == null) - return null; - - // Set the relocation table - executable.RelocationTable = relocationTable; - - #endregion - - // Return the executable - return executable; - } - - /// - /// Parse a byte array into an MS-DOS executable header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled executable header on success, null on error - private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset) - { - // TODO: Use marshalling here instead of building - var header = new ExecutableHeader(); - - #region Standard Fields - - header.Magic = new byte[2]; - for (int i = 0; i < header.Magic.Length; i++) - { - header.Magic[i] = data.ReadByte(ref offset); - } - if (header.Magic[0] != 'M' || header.Magic[1] != 'Z') - return null; - - header.LastPageBytes = data.ReadUInt16(ref offset); - header.Pages = data.ReadUInt16(ref offset); - header.RelocationItems = data.ReadUInt16(ref offset); - header.HeaderParagraphSize = data.ReadUInt16(ref offset); - header.MinimumExtraParagraphs = data.ReadUInt16(ref offset); - header.MaximumExtraParagraphs = data.ReadUInt16(ref offset); - header.InitialSSValue = data.ReadUInt16(ref offset); - header.InitialSPValue = data.ReadUInt16(ref offset); - header.Checksum = data.ReadUInt16(ref offset); - header.InitialIPValue = data.ReadUInt16(ref offset); - header.InitialCSValue = data.ReadUInt16(ref offset); - header.RelocationTableAddr = data.ReadUInt16(ref offset); - header.OverlayNumber = data.ReadUInt16(ref offset); - - #endregion - - // If we don't have enough data for PE extensions - if (offset >= data.Length || data.Length - offset < 36) - return header; - - #region PE Extensions - - header.Reserved1 = new ushort[4]; - for (int i = 0; i < header.Reserved1.Length; i++) - { - header.Reserved1[i] = data.ReadUInt16(ref offset); - } - header.OEMIdentifier = data.ReadUInt16(ref offset); - header.OEMInformation = data.ReadUInt16(ref offset); - header.Reserved2 = new ushort[10]; - for (int i = 0; i < header.Reserved2.Length; i++) - { - header.Reserved2[i] = data.ReadUInt16(ref offset); - } - header.NewExeHeaderAddr = data.ReadUInt32(ref offset); - - #endregion - - return header; - } - - /// - /// Parse a byte array into a relocation table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of relocation table entries to read - /// Filled relocation table on success, null on error - private static RelocationEntry[] ParseRelocationTable(byte[] data, int offset, int count) - { - // TODO: Use marshalling here instead of building - var relocationTable = new RelocationEntry[count]; - - for (int i = 0; i < count; i++) - { - var entry = new RelocationEntry(); - entry.Offset = data.ReadUInt16(ref offset); - entry.Segment = data.ReadUInt16(ref offset); - relocationTable[i] = entry; - } - - return relocationTable; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseExecutable(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/MicrosoftCabinet.cs b/BurnOutSharp.Builder/MicrosoftCabinet.cs index 26f3459e..43541d71 100644 --- a/BurnOutSharp.Builder/MicrosoftCabinet.cs +++ b/BurnOutSharp.Builder/MicrosoftCabinet.cs @@ -7,7 +7,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { // TODO: Add multi-cabinet reading - // TODO: Make Stream Data rely on Byte Data public class MicrosoftCabinet { #region Constants @@ -72,234 +71,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new cabinet to fill - var cabinet = new Cabinet(); - - #region Cabinet Header - - // Try to parse the cabinet header - var cabinetHeader = ParseCabinetHeader(data, ref offset); - if (cabinetHeader == null) - return null; - - // Set the cabinet header - cabinet.Header = cabinetHeader; - - #endregion - - #region Folders - - // Set the folder array - cabinet.Folders = new CFFOLDER[cabinetHeader.FolderCount]; - - // Try to parse each folder, if we have any - for (int i = 0; i < cabinetHeader.FolderCount; i++) - { - var folder = ParseFolder(data, ref offset, cabinetHeader, initialOffset); - if (folder == null) - return null; - - // Set the folder - cabinet.Folders[i] = folder; - } - - #endregion - - #region Files - - // Get the files offset - int filesOffset = (int)cabinetHeader.FilesOffset + initialOffset; - if (filesOffset > data.Length) - return null; - - // Set the file array - cabinet.Files = new CFFILE[cabinetHeader.FileCount]; - - // Try to parse each file, if we have any - for (int i = 0; i < cabinetHeader.FileCount; i++) - { - var file = ParseFile(data, ref filesOffset); - if (file == null) - return null; - - // Set the file - cabinet.Files[i] = file; - } - - #endregion - - return cabinet; - } - - /// - /// Parse a byte array into a cabinet header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled cabinet header on success, null on error - private static CFHEADER ParseCabinetHeader(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - CFHEADER header = new CFHEADER(); - - header.Signature = data.ReadUInt32(ref offset); - if (header.Signature != SignatureValue) - return null; - - header.Reserved1 = data.ReadUInt32(ref offset); - if (header.Reserved1 != 0x00000000) - return null; - - header.CabinetSize = data.ReadUInt32(ref offset); - if (header.CabinetSize > MaximumCabSize) - return null; - - header.Reserved2 = data.ReadUInt32(ref offset); - if (header.Reserved2 != 0x00000000) - return null; - - header.FilesOffset = data.ReadUInt32(ref offset); - - header.Reserved3 = data.ReadUInt32(ref offset); - if (header.Reserved3 != 0x00000000) - return null; - - header.VersionMinor = data.ReadByte(ref offset); - header.VersionMajor = data.ReadByte(ref offset); - if (header.VersionMajor != 0x00000001 || header.VersionMinor != 0x00000003) - return null; - - header.FolderCount = data.ReadUInt16(ref offset); - if (header.FolderCount > MaximumFolderCount) - return null; - - header.FileCount = data.ReadUInt16(ref offset); - if (header.FileCount > MaximumFileCount) - return null; - - header.Flags = (HeaderFlags)data.ReadUInt16(ref offset); - header.SetID = data.ReadUInt16(ref offset); - header.CabinetIndex = data.ReadUInt16(ref offset); - - if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT)) - { - header.HeaderReservedSize = data.ReadUInt16(ref offset); - if (header.HeaderReservedSize > 60_000) - return null; - - header.FolderReservedSize = data.ReadByte(ref offset); - header.DataReservedSize = data.ReadByte(ref offset); - - if (header.HeaderReservedSize > 0) - header.ReservedData = data.ReadBytes(ref offset, header.HeaderReservedSize); - } - - if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET)) - { - header.CabinetPrev = data.ReadString(ref offset, Encoding.ASCII); - header.DiskPrev = data.ReadString(ref offset, Encoding.ASCII); - } - - if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET)) - { - header.CabinetNext = data.ReadString(ref offset, Encoding.ASCII); - header.DiskNext = data.ReadString(ref offset, Encoding.ASCII); - } - - return header; - } - - /// - /// Parse a byte array into a folder - /// - /// Byte array to parse - /// Offset into the byte array - /// Cabinet header to get flags and sizes from - /// Initial offset for calculations - /// Filled folder on success, null on error - private static CFFOLDER ParseFolder(byte[] data, ref int offset, CFHEADER header, int initialOffset) - { - // TODO: Use marshalling here instead of building - CFFOLDER folder = new CFFOLDER(); - - folder.CabStartOffset = data.ReadUInt32(ref offset); - folder.DataCount = data.ReadUInt16(ref offset); - folder.CompressionType = (CompressionType)data.ReadUInt16(ref offset); - - if (header.FolderReservedSize > 0) - folder.ReservedData = data.ReadBytes(ref offset, header.FolderReservedSize); - - if (folder.CabStartOffset > 0) - { - int blockPtr = initialOffset + (int)folder.CabStartOffset; - - folder.DataBlocks = new Dictionary(); - for (int i = 0; i < folder.DataCount; i++) - { - int dataStart = blockPtr; - CFDATA dataBlock = ParseDataBlock(data, ref blockPtr, header.DataReservedSize); - folder.DataBlocks[dataStart] = dataBlock; - } - } - - return folder; - } - - /// - /// Parse a byte array into a data block - /// - /// Byte array to parse - /// Offset into the byte array - /// Reserved byte size for data blocks - /// Filled folder on success, null on error - private static CFDATA ParseDataBlock(byte[] data, ref int offset, byte dataReservedSize) - { - // TODO: Use marshalling here instead of building - CFDATA dataBlock = new CFDATA(); - - dataBlock.Checksum = data.ReadUInt32(ref offset); - dataBlock.CompressedSize = data.ReadUInt16(ref offset); - dataBlock.UncompressedSize = data.ReadUInt16(ref offset); - - if (dataBlock.UncompressedSize != 0 && dataBlock.CompressedSize > dataBlock.UncompressedSize) - return null; - - if (dataReservedSize > 0) - dataBlock.ReservedData = data.ReadBytes(ref offset, dataReservedSize); - - if (dataBlock.CompressedSize > 0) - dataBlock.CompressedData = data.ReadBytes(ref offset, dataBlock.CompressedSize); - - return dataBlock; - } - - /// - /// Parse a byte array into a file - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled file on success, null on error - private static CFFILE ParseFile(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - CFFILE file = new CFFILE(); - - file.FileSize = data.ReadUInt32(ref offset); - file.FolderStartOffset = data.ReadUInt32(ref offset); - file.FolderIndex = (FolderIndex)data.ReadUInt16(ref offset); - file.Date = data.ReadUInt16(ref offset); - file.Time = data.ReadUInt16(ref offset); - file.Attributes = (Models.MicrosoftCabinet.FileAttributes)data.ReadUInt16(ref offset); - - if (file.Attributes.HasFlag(Models.MicrosoftCabinet.FileAttributes.NAME_IS_UTF)) - file.Name = data.ReadString(ref offset, Encoding.Unicode); - else - file.Name = data.ReadString(ref offset, Encoding.ASCII); - - return file; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseCabinet(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/MoPaQ.cs b/BurnOutSharp.Builder/MoPaQ.cs index bcdb2645..7b9a6ae9 100644 --- a/BurnOutSharp.Builder/MoPaQ.cs +++ b/BurnOutSharp.Builder/MoPaQ.cs @@ -6,7 +6,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public class MoPaQ { #region Constants @@ -225,519 +224,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new archive to fill - var archive = new Archive(); - - #region User Data - - // Check for User Data - uint possibleSignature = BitConverter.ToUInt32(data, offset); - if (possibleSignature == UserDataSignatureValue) - { - // Save the current position for offset correction - int basePtr = offset; - - // Deserialize the user data, returning null if invalid - var userData = ParseUserData(data, ref offset); - if (userData == null) - return null; - - // Set the user data - archive.UserData = userData; - - // Set the starting position according to the header offset - offset = basePtr + (int)archive.UserData.HeaderOffset; - } - - #endregion - - #region Archive Header - - // Check for the Header - possibleSignature = BitConverter.ToUInt32(data, offset); - if (possibleSignature == ArchiveHeaderSignatureValue) - { - // Try to parse the archive header - var archiveHeader = ParseArchiveHeader(data, ref offset); - if (archiveHeader == null) - return null; - - // Set the archive header - archive.ArchiveHeader = archiveHeader; - } - - #endregion - - #region Hash Table - - // Version 1 - if (archive.ArchiveHeader.FormatVersion == 0) - { - // If we have a hash table - long hashTableOffset = archive.ArchiveHeader.HashTablePosition; - if (hashTableOffset != 0) - { - // Find the ending offset based on size - long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize; - - // Read in the hash table - var hashTable = new List(); - - while (hashTableOffset < hashTableEnd) - { - var hashEntry = ParseHashEntry(data, ref hashTableOffset); - if (hashEntry == null) - return null; - - hashTable.Add(hashEntry); - } - - archive.HashTable = hashTable.ToArray(); - } - } - - // Version 2 and 3 - else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2) - { - // If we have a hash table - long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition; - if (hashTableOffset != 0) - { - // Find the ending offset based on size - long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize; - - // Read in the hash table - var hashTable = new List(); - - while (hashTableOffset < hashTableEnd) - { - var hashEntry = ParseHashEntry(data, ref hashTableOffset); - if (hashEntry == null) - return null; - - hashTable.Add(hashEntry); - } - - archive.HashTable = hashTable.ToArray(); - } - } - - // Version 4 - else if (archive.ArchiveHeader.FormatVersion == 3) - { - // If we have a hash table - long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition; - if (hashTableOffset != 0) - { - // Find the ending offset based on size - long hashTableEnd = hashTableOffset + (long)archive.ArchiveHeader.HashTableSizeLong; - - // Read in the hash table - var hashTable = new List(); - - while (hashTableOffset < hashTableEnd) - { - var hashEntry = ParseHashEntry(data, ref hashTableOffset); - if (hashEntry == null) - return null; - - hashTable.Add(hashEntry); - } - - archive.HashTable = hashTable.ToArray(); - } - } - - #endregion - - #region Block Table - - // Version 1 - if (archive.ArchiveHeader.FormatVersion == 0) - { - // If we have a block table - long blockTableOffset = archive.ArchiveHeader.BlockTablePosition; - if (blockTableOffset != 0) - { - // Find the ending offset based on size - long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize; - - // Read in the block table - var blockTable = new List(); - - while (blockTableOffset < blockTableEnd) - { - var blockEntry = ParseBlockEntry(data, ref blockTableOffset); - if (blockEntry == null) - return null; - - blockTable.Add(blockEntry); - } - - archive.BlockTable = blockTable.ToArray(); - } - } - - // Version 2 and 3 - else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2) - { - // If we have a block table - long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition; - if (blockTableOffset != 0) - { - // Find the ending offset based on size - long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize; - - // Read in the block table - var blockTable = new List(); - - while (blockTableOffset < blockTableEnd) - { - var blockEntry = ParseBlockEntry(data, ref blockTableOffset); - if (blockEntry == null) - return null; - - blockTable.Add(blockEntry); - } - - archive.BlockTable = blockTable.ToArray(); - } - } - - // Version 4 - else if (archive.ArchiveHeader.FormatVersion == 3) - { - // If we have a block table - long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition; - if (blockTableOffset != 0) - { - // Find the ending offset based on size - long blockTableEnd = blockTableOffset + (long)archive.ArchiveHeader.BlockTableSizeLong; - - // Read in the block table - var blockTable = new List(); - - while (blockTableOffset < blockTableEnd) - { - var blockEntry = ParseBlockEntry(data, ref blockTableOffset); - if (blockEntry == null) - return null; - - blockTable.Add(blockEntry); - } - - archive.BlockTable = blockTable.ToArray(); - } - } - - #endregion - - #region Hi-Block Table - - // Version 2, 3, and 4 - if (archive.ArchiveHeader.FormatVersion == 1 - || archive.ArchiveHeader.FormatVersion == 2 - || archive.ArchiveHeader.FormatVersion == 3) - { - // If we have a hi-block table - int hiBlockTableOffset = (int)archive.ArchiveHeader.HiBlockTablePosition; - if (hiBlockTableOffset != 0) - { - // Read in the hi-block table - var hiBlockTable = new List(); - - for (int i = 0; i < archive.BlockTable.Length; i++) - { - short hiBlockEntry = data.ReadInt16(ref hiBlockTableOffset); - hiBlockTable.Add(hiBlockEntry); - } - - archive.HiBlockTable = hiBlockTable.ToArray(); - } - } - - #endregion - - #region BET Table - - // Version 3 and 4 - if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3) - { - // If we have a BET table - int betTableOffset = (int)archive.ArchiveHeader.BetTablePosition; - if (betTableOffset != 0) - { - // Read in the BET table - var betTable = ParseBetTable(data, ref betTableOffset); - if (betTable == null) - return null; - - archive.BetTable = betTable; - } - } - - #endregion - - #region HET Table - - // Version 3 and 4 - if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3) - { - // If we have a HET table - int hetTableOffset = (int)archive.ArchiveHeader.HetTablePosition; - if (hetTableOffset != 0) - { - // Read in the HET table - var hetTable = ParseHetTable(data, ref hetTableOffset); - if (hetTable == null) - return null; - - archive.HetTable = hetTable; - } - } - - #endregion - - return archive; - } - - /// - /// Parse a byte array into a archive header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled archive header on success, null on error - private static ArchiveHeader ParseArchiveHeader(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - ArchiveHeader archiveHeader = new ArchiveHeader(); - - // V1 - Common - archiveHeader.Signature = data.ReadUInt32(ref offset); - if (archiveHeader.Signature != ArchiveHeaderSignatureValue) - return null; - - archiveHeader.HeaderSize = data.ReadUInt32(ref offset); - archiveHeader.ArchiveSize = data.ReadUInt32(ref offset); - archiveHeader.FormatVersion = data.ReadUInt16(ref offset); - archiveHeader.BlockSize = data.ReadUInt16(ref offset); - archiveHeader.HashTablePosition = data.ReadUInt32(ref offset); - archiveHeader.BlockTablePosition = data.ReadUInt32(ref offset); - archiveHeader.HashTableSize = data.ReadUInt32(ref offset); - archiveHeader.BlockTableSize = data.ReadUInt32(ref offset); - - // V2 - if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion2Size) - { - archiveHeader.HiBlockTablePosition = data.ReadUInt64(ref offset); - archiveHeader.HashTablePositionHi = data.ReadUInt16(ref offset); - archiveHeader.BlockTablePositionHi = data.ReadUInt16(ref offset); - } - - // V3 - if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion3Size) - { - archiveHeader.ArchiveSizeLong = data.ReadUInt64(ref offset); - archiveHeader.BetTablePosition = data.ReadUInt64(ref offset); - archiveHeader.HetTablePosition = data.ReadUInt64(ref offset); - } - - // V4 - if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion4Size) - { - archiveHeader.HashTableSizeLong = data.ReadUInt64(ref offset); - archiveHeader.BlockTableSizeLong = data.ReadUInt64(ref offset); - archiveHeader.HiBlockTableSize = data.ReadUInt64(ref offset); - archiveHeader.HetTableSize = data.ReadUInt64(ref offset); - archiveHeader.BetTablesize = data.ReadUInt64(ref offset); - archiveHeader.RawChunkSize = data.ReadUInt32(ref offset); - - archiveHeader.BlockTableMD5 = data.ReadBytes(ref offset, 0x10); - archiveHeader.HashTableMD5 = data.ReadBytes(ref offset, 0x10); - archiveHeader.HiBlockTableMD5 = data.ReadBytes(ref offset, 0x10); - archiveHeader.BetTableMD5 = data.ReadBytes(ref offset, 0x10); - archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10); - archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10); - } - - return archiveHeader; - } - - /// - /// Parse a byte array into a user data object - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled user data on success, null on error - private static UserData ParseUserData(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - UserData userData = new UserData(); - - userData.Signature = data.ReadUInt32(ref offset); - if (userData.Signature != UserDataSignatureValue) - return null; - - userData.UserDataSize = data.ReadUInt32(ref offset); - userData.HeaderOffset = data.ReadUInt32(ref offset); - userData.UserDataHeaderSize = data.ReadUInt32(ref offset); - - return userData; - } - - /// - /// Parse a byte array into a HET table - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled HET table on success, null on error - private static HetTable ParseHetTable(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - HetTable hetTable = new HetTable(); - - // Common Headers - hetTable.Signature = data.ReadUInt32(ref offset); - if (hetTable.Signature != HetTableSignatureValue) - return null; - - hetTable.Version = data.ReadUInt32(ref offset); - hetTable.DataSize = data.ReadUInt32(ref offset); - - // HET-Specific - hetTable.TableSize = data.ReadUInt32(ref offset); - hetTable.MaxFileCount = data.ReadUInt32(ref offset); - hetTable.HashTableSize = data.ReadUInt32(ref offset); - hetTable.TotalIndexSize = data.ReadUInt32(ref offset); - hetTable.IndexSizeExtra = data.ReadUInt32(ref offset); - hetTable.IndexSize = data.ReadUInt32(ref offset); - hetTable.BlockTableSize = data.ReadUInt32(ref offset); - hetTable.HashTable = data.ReadBytes(ref offset, (int)hetTable.HashTableSize); - - // TODO: Populate the file indexes array - hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][]; - - return hetTable; - } - - /// - /// Parse a byte array into a BET table - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled BET table on success, null on error - private static BetTable ParseBetTable(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - BetTable betTable = new BetTable(); - - // Common Headers - betTable.Signature = data.ReadUInt32(ref offset); - if (betTable.Signature != BetTableSignatureValue) - return null; - - betTable.Version = data.ReadUInt32(ref offset); - betTable.DataSize = data.ReadUInt32(ref offset); - - // BET-Specific - betTable.TableSize = data.ReadUInt32(ref offset); - betTable.FileCount = data.ReadUInt32(ref offset); - betTable.Unknown = data.ReadUInt32(ref offset); - betTable.TableEntrySize = data.ReadUInt32(ref offset); - - betTable.FilePositionBitIndex = data.ReadUInt32(ref offset); - betTable.FileSizeBitIndex = data.ReadUInt32(ref offset); - betTable.CompressedSizeBitIndex = data.ReadUInt32(ref offset); - betTable.FlagIndexBitIndex = data.ReadUInt32(ref offset); - betTable.UnknownBitIndex = data.ReadUInt32(ref offset); - - betTable.FilePositionBitCount = data.ReadUInt32(ref offset); - betTable.FileSizeBitCount = data.ReadUInt32(ref offset); - betTable.CompressedSizeBitCount = data.ReadUInt32(ref offset); - betTable.FlagIndexBitCount = data.ReadUInt32(ref offset); - betTable.UnknownBitCount = data.ReadUInt32(ref offset); - - betTable.TotalBetHashSize = data.ReadUInt32(ref offset); - betTable.BetHashSizeExtra = data.ReadUInt32(ref offset); - betTable.BetHashSize = data.ReadUInt32(ref offset); - betTable.BetHashArraySize = data.ReadUInt32(ref offset); - betTable.FlagCount = data.ReadUInt32(ref offset); - - betTable.FlagsArray = new uint[betTable.FlagCount]; - Buffer.BlockCopy(data, offset, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4); - offset += (int)betTable.FlagCount * 4; - - // TODO: Populate the file table - // TODO: Populate the hash table - - return betTable; - } - - /// - /// Parse a byte array into a hash entry - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled hash entry on success, null on error - private static HashEntry ParseHashEntry(byte[] data, ref long offset) - { - // TODO: Use marshalling here instead of building - HashEntry hashEntry = new HashEntry(); - int intOffset = (int)offset; - - hashEntry.NameHashPartA = data.ReadUInt32(ref intOffset); - hashEntry.NameHashPartB = data.ReadUInt32(ref intOffset); - hashEntry.Locale = (Locale)data.ReadUInt16(ref intOffset); - hashEntry.Platform = data.ReadUInt16(ref intOffset); - hashEntry.BlockIndex = data.ReadUInt32(ref intOffset); - - offset = intOffset; - return hashEntry; - } - - /// - /// Parse a byte array into a block entry - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled block entry on success, null on error - private static BlockEntry ParseBlockEntry(byte[] data, ref long offset) - { - // TODO: Use marshalling here instead of building - BlockEntry blockEntry = new BlockEntry(); - int intOffset = (int)offset; - - blockEntry.FilePosition = data.ReadUInt32(ref intOffset); - blockEntry.CompressedSize = data.ReadUInt32(ref intOffset); - blockEntry.UncompressedSize = data.ReadUInt32(ref intOffset); - blockEntry.Flags = (FileFlags)data.ReadUInt32(ref intOffset); - - offset = intOffset; - return blockEntry; - } - - /// - /// Parse a byte array into a patch info - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled patch info on success, null on error - private static PatchInfo ParsePatchInfo(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - PatchInfo patchInfo = new PatchInfo(); - - patchInfo.Length = data.ReadUInt32(ref offset); - patchInfo.Flags = data.ReadUInt32(ref offset); - patchInfo.DataSize = data.ReadUInt32(ref offset); - patchInfo.MD5 = data.ReadBytes(ref offset, 0x10); - - // TODO: Fill the sector offset table - - return patchInfo; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseArchive(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/NewExecutable.cs b/BurnOutSharp.Builder/NewExecutable.cs index ae1a6ea2..a9d05648 100644 --- a/BurnOutSharp.Builder/NewExecutable.cs +++ b/BurnOutSharp.Builder/NewExecutable.cs @@ -6,7 +6,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public static class NewExecutable { #region Byte Data @@ -27,458 +26,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new executable to fill - var executable = new Executable(); - - #region MS-DOS Stub - - // Parse the MS-DOS stub - var stub = MSDOS.ParseExecutable(data, offset); - if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0) - return null; - - // Set the MS-DOS stub - executable.Stub = stub; - - #endregion - - #region Executable Header - - // Try to parse the executable header - offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr); - var executableHeader = ParseExecutableHeader(data, offset); - if (executableHeader == null) - return null; - - // Set the executable header - executable.Header = executableHeader; - - #endregion - - #region Segment Table - - // If the offset for the segment table doesn't exist - int tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.SegmentTableOffset; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the segment table - var segmentTable = ParseSegmentTable(data, tableAddress, executableHeader.FileSegmentCount); - if (segmentTable == null) - return null; - - // Set the segment table - executable.SegmentTable = segmentTable; - - #endregion - - #region Resource Table - - // If the offset for the segment table doesn't exist - tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.SegmentTableOffset; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the resource table - var resourceTable = ParseResourceTable(data, tableAddress, executableHeader.ResourceEntriesCount); - if (resourceTable == null) - return null; - - // Set the resource table - executable.ResourceTable = resourceTable; - - #endregion - - #region Resident-Name Table - - // If the offset for the resident-name table doesn't exist - tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.ResidentNameTableOffset; - int endOffset = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.ModuleReferenceTableOffset; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the resident-name table - var residentNameTable = ParseResidentNameTable(data, tableAddress, endOffset); - if (residentNameTable == null) - return null; - - // Set the resident-name table - executable.ResidentNameTable = residentNameTable; - - #endregion - - #region Module-Reference Table - - // If the offset for the module-reference table doesn't exist - tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.ModuleReferenceTableOffset; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the module-reference table - var moduleReferenceTable = ParseModuleReferenceTable(data, tableAddress, executableHeader.ModuleReferenceTableSize); - if (moduleReferenceTable == null) - return null; - - // Set the module-reference table - executable.ModuleReferenceTable = moduleReferenceTable; - - #endregion - - #region Imported-Name Table - - // If the offset for the imported-name table doesn't exist - tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.ImportedNamesTableOffset; - endOffset = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.EntryTableOffset; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the imported-name table - var importedNameTable = ParseImportedNameTable(data, tableAddress, endOffset); - if (importedNameTable == null) - return null; - - // Set the imported-name table - executable.ImportedNameTable = importedNameTable; - - #endregion - - #region Entry Table - - // If the offset for the entry table doesn't exist - tableAddress = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.EntryTableOffset; - endOffset = initialOffset - + (int)stub.Header.NewExeHeaderAddr - + executableHeader.EntryTableOffset - + executableHeader.EntryTableSize; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the entry table - var entryTable = ParseEntryTable(data, tableAddress, endOffset); - if (entryTable == null) - return null; - - // Set the entry table - executable.EntryTable = entryTable; - - #endregion - - #region Nonresident-Name Table - - // If the offset for the nonresident-name table doesn't exist - tableAddress = initialOffset - + (int)executableHeader.NonResidentNamesTableOffset; - endOffset = initialOffset - + (int)executableHeader.NonResidentNamesTableOffset - + executableHeader.NonResidentNameTableSize; - if (tableAddress >= data.Length) - return executable; - - // Try to parse the nonresident-name table - var nonResidentNameTable = ParseNonResidentNameTable(data, tableAddress, endOffset); - if (nonResidentNameTable == null) - return null; - - // Set the nonresident-name table - executable.NonResidentNameTable = nonResidentNameTable; - - #endregion - - return executable; - } - - /// - /// Parse a byte array into a New Executable header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled executable header on success, null on error - private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset) - { - // TODO: Use marshalling here instead of building - var header = new ExecutableHeader(); - - header.Magic = new byte[2]; - for (int i = 0; i < header.Magic.Length; i++) - { - header.Magic[i] = data.ReadByte(ref offset); - } - if (header.Magic[0] != 'N' || header.Magic[1] != 'E') - return null; - - header.LinkerVersion = data.ReadByte(ref offset); - header.LinkerRevision = data.ReadByte(ref offset); - header.EntryTableOffset = data.ReadUInt16(ref offset); - header.EntryTableSize = data.ReadUInt16(ref offset); - header.CrcChecksum = data.ReadUInt32(ref offset); - header.FlagWord = (HeaderFlag)data.ReadUInt16(ref offset); - header.AutomaticDataSegmentNumber = data.ReadUInt16(ref offset); - header.InitialHeapAlloc = data.ReadUInt16(ref offset); - header.InitialStackAlloc = data.ReadUInt16(ref offset); - header.InitialCSIPSetting = data.ReadUInt32(ref offset); - header.InitialSSSPSetting = data.ReadUInt32(ref offset); - header.FileSegmentCount = data.ReadUInt16(ref offset); - header.ModuleReferenceTableSize = data.ReadUInt16(ref offset); - header.NonResidentNameTableSize = data.ReadUInt16(ref offset); - header.SegmentTableOffset = data.ReadUInt16(ref offset); - header.ResourceTableOffset = data.ReadUInt16(ref offset); - header.ResidentNameTableOffset = data.ReadUInt16(ref offset); - header.ModuleReferenceTableOffset = data.ReadUInt16(ref offset); - header.ImportedNamesTableOffset = data.ReadUInt16(ref offset); - header.NonResidentNamesTableOffset = data.ReadUInt32(ref offset); - header.MovableEntriesCount = data.ReadUInt16(ref offset); - header.SegmentAlignmentShiftCount = data.ReadUInt16(ref offset); - header.ResourceEntriesCount = data.ReadUInt16(ref offset); - header.TargetOperatingSystem = (OperatingSystem)data.ReadByte(ref offset); - header.AdditionalFlags = (OS2Flag)data.ReadByte(ref offset); - header.ReturnThunkOffset = data.ReadUInt16(ref offset); - header.SegmentReferenceThunkOffset = data.ReadUInt16(ref offset); - header.MinCodeSwapAreaSize = data.ReadUInt16(ref offset); - header.WindowsSDKRevision = data.ReadByte(ref offset); - header.WindowsSDKVersion = data.ReadByte(ref offset); - - return header; - } - - /// - /// Parse a byte array into a segment table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of segment table entries to read - /// Filled segment table on success, null on error - private static SegmentTableEntry[] ParseSegmentTable(byte[] data, int offset, int count) - { - // TODO: Use marshalling here instead of building - var segmentTable = new SegmentTableEntry[count]; - - for (int i = 0; i < count; i++) - { - var entry = new SegmentTableEntry(); - entry.Offset = data.ReadUInt16(ref offset); - entry.Length = data.ReadUInt16(ref offset); - entry.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16(ref offset); - entry.MinimumAllocationSize = data.ReadUInt16(ref offset); - segmentTable[i] = entry; - } - - return segmentTable; - } - - /// - /// Parse a byte array into a resource table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of resource table entries to read - /// Filled resource table on success, null on error - private static ResourceTable ParseResourceTable(byte[] data, int offset, int count) - { - int initialOffset = offset; - - // TODO: Use marshalling here instead of building - var resourceTable = new ResourceTable(); - - resourceTable.AlignmentShiftCount = data.ReadUInt16(ref offset); - resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count]; - for (int i = 0; i < resourceTable.ResourceTypes.Length; i++) - { - var entry = new ResourceTypeInformationEntry(); - entry.TypeID = data.ReadUInt16(ref offset); - entry.ResourceCount = data.ReadUInt16(ref offset); - entry.Reserved = data.ReadUInt32(ref offset); - entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount]; - for (int j = 0; j < entry.ResourceCount; j++) - { - // TODO: Should we read and store the resource data? - var resource = new ResourceTypeResourceEntry(); - resource.Offset = data.ReadUInt16(ref offset); - resource.Length = data.ReadUInt16(ref offset); - resource.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16(ref offset); - resource.ResourceID = data.ReadUInt16(ref offset); - resource.Reserved = data.ReadUInt32(ref offset); - entry.Resources[j] = resource; - } - resourceTable.ResourceTypes[i] = entry; - } - - // Get the full list of unique string offsets - var stringOffsets = resourceTable.ResourceTypes - .Where(rt => rt.IsIntegerType() == false) - .Select(rt => rt.TypeID) - .Union(resourceTable.ResourceTypes - .SelectMany(rt => rt.Resources) - .Where(r => r.IsIntegerType() == false) - .Select(r => r.ResourceID)) - .Distinct() - .OrderBy(o => o) - .ToList(); - - // Populate the type and name string dictionary - resourceTable.TypeAndNameStrings = new Dictionary(); - for (int i = 0; i < stringOffsets.Count; i++) - { - int stringOffset = stringOffsets[i] + initialOffset; - var str = new ResourceTypeAndNameString(); - str.Length = data.ReadByte(ref stringOffset); - str.Text = data.ReadBytes(ref stringOffset, str.Length); - resourceTable.TypeAndNameStrings[stringOffsets[i]] = str; - } - - return resourceTable; - } - - /// - /// Parse a byte array into a resident-name table - /// - /// Byte array to parse - /// Offset into the byte array - /// First address not part of the resident-name table - /// Filled resident-name table on success, null on error - private static ResidentNameTableEntry[] ParseResidentNameTable(byte[] data, int offset, int endOffset) - { - // TODO: Use marshalling here instead of building - var residentNameTable = new List(); - - while (offset < endOffset) - { - var entry = new ResidentNameTableEntry(); - entry.Length = data.ReadByte(ref offset); - entry.NameString = data.ReadBytes(ref offset, entry.Length); - entry.OrdinalNumber = data.ReadUInt16(ref offset); - residentNameTable.Add(entry); - } - - return residentNameTable.ToArray(); - } - - /// - /// Parse a byte array into a module-reference table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of module-reference table entries to read - /// Filled module-reference table on success, null on error - private static ModuleReferenceTableEntry[] ParseModuleReferenceTable(byte[] data, int offset, int count) - { - // TODO: Use marshalling here instead of building - var moduleReferenceTable = new ModuleReferenceTableEntry[count]; - - for (int i = 0; i < count; i++) - { - var entry = new ModuleReferenceTableEntry(); - entry.Offset = data.ReadUInt16(ref offset); - moduleReferenceTable[i] = entry; - } - - return moduleReferenceTable; - } - - /// - /// Parse a byte array into an imported-name table - /// - /// Byte array to parse - /// Offset into the byte array - /// First address not part of the imported-name table - /// Filled imported-name table on success, null on error - private static Dictionary ParseImportedNameTable(byte[] data, int offset, int endOffset) - { - // TODO: Use marshalling here instead of building - var importedNameTable = new Dictionary(); - - while (offset < endOffset) - { - ushort currentOffset = (ushort)offset; - var entry = new ImportedNameTableEntry(); - entry.Length = data.ReadByte(ref offset); - entry.NameString = data.ReadBytes(ref offset, entry.Length); - importedNameTable[currentOffset] = entry; - } - - return importedNameTable; - } - - /// - /// Parse a byte array into an entry table - /// - /// Byte array to parse - /// Offset into the byte array - /// First address not part of the entry table - /// Filled entry table on success, null on error - private static EntryTableBundle[] ParseEntryTable(byte[] data, int offset, int endOffset) - { - // TODO: Use marshalling here instead of building - var entryTable = new List(); - - while (offset < endOffset) - { - var entry = new EntryTableBundle(); - entry.EntryCount = data.ReadByte(ref offset); - entry.SegmentIndicator = data.ReadByte(ref offset); - switch (entry.GetEntryType()) - { - case SegmentEntryType.Unused: - break; - - case SegmentEntryType.FixedSegment: - entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByte(ref offset); - entry.FixedOffset = data.ReadUInt16(ref offset); - break; - - case SegmentEntryType.MoveableSegment: - entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByte(ref offset); - entry.MoveableReserved = data.ReadUInt16(ref offset); - entry.MoveableSegmentNumber = data.ReadByte(ref offset); - entry.MoveableOffset = data.ReadUInt16(ref offset); - break; - } - entryTable.Add(entry); - } - - return entryTable.ToArray(); - } - - /// - /// Parse a byte array into a nonresident-name table - /// - /// Byte array to parse - /// Offset into the byte array - /// First address not part of the nonresident-name table - /// Filled nonresident-name table on success, null on error - private static NonResidentNameTableEntry[] ParseNonResidentNameTable(byte[] data, int offset, int endOffset) - { - // TODO: Use marshalling here instead of building - var residentNameTable = new List(); - - while (offset < endOffset) - { - var entry = new NonResidentNameTableEntry(); - entry.Length = data.ReadByte(ref offset); - entry.NameString = data.ReadBytes(ref offset, entry.Length); - entry.OrdinalNumber = data.ReadUInt16(ref offset); - residentNameTable.Add(entry); - } - - return residentNameTable.ToArray(); + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseExecutable(dataStream); } #endregion diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs index 6a82309c..24bfcbdd 100644 --- a/BurnOutSharp.Builder/PortableExecutable.cs +++ b/BurnOutSharp.Builder/PortableExecutable.cs @@ -8,7 +8,6 @@ using BurnOutSharp.Utilities; namespace BurnOutSharp.Builder { - // TODO: Make Stream Data rely on Byte Data public static class PortableExecutable { #region Byte Data @@ -29,1227 +28,10 @@ namespace BurnOutSharp.Builder if (offset < 0 || offset >= data.Length) return null; - // Cache the current offset - int initialOffset = offset; - - // Create a new executable to fill - var executable = new Executable(); - - #region MS-DOS Stub - - // Parse the MS-DOS stub - var stub = MSDOS.ParseExecutable(data, offset); - if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0) - return null; - - // Set the MS-DOS stub - executable.Stub = stub; - - #endregion - - #region Signature - - offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr); - executable.Signature = new byte[4]; - for (int i = 0; i < executable.Signature.Length; i++) - { - executable.Signature[i] = data.ReadByte(ref offset); - } - if (executable.Signature[0] != 'P' || executable.Signature[1] != 'E' || executable.Signature[2] != '\0' || executable.Signature[3] != '\0') - return null; - - #endregion - - #region COFF File Header - - // Try to parse the COFF file header - var coffFileHeader = ParseCOFFFileHeader(data, ref offset); - if (coffFileHeader == null) - return null; - - // Set the COFF file header - executable.COFFFileHeader = coffFileHeader; - - #endregion - - #region Optional Header - - // Try to parse the optional header - var optionalHeader = ParseOptionalHeader(data, ref offset, coffFileHeader.SizeOfOptionalHeader); - if (optionalHeader == null) - return null; - - // Set the optional header - executable.OptionalHeader = optionalHeader; - - #endregion - - #region Section Table - - // Try to parse the section table - var sectionTable = ParseSectionTable(data, offset, coffFileHeader.NumberOfSections); - if (sectionTable == null) - return null; - - // Set the section table - executable.SectionTable = sectionTable; - - #endregion - - #region COFF Symbol Table and COFF String Table - - // TODO: Validate that this is correct with an "old" PE - if (coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the COFF symbol table doesn't exist - int coffSymbolTableAddress = initialOffset - + (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable); - if (coffSymbolTableAddress >= data.Length) - return executable; - - // Try to parse the COFF symbol table - var coffSymbolTable = ParseCOFFSymbolTable(data, coffSymbolTableAddress, coffFileHeader.NumberOfSymbols); - if (coffSymbolTable == null) - return null; - - // If the offset for the COFF string table doesn't exist - coffSymbolTableAddress = initialOffset - + (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable) - + (coffSymbolTable.Length * 18 /* sizeof(COFFSymbolTableEntry) */); - if (coffSymbolTableAddress >= data.Length) - return executable; - - // Set the COFF symbol table - executable.COFFSymbolTable = coffSymbolTable; - - // Try to parse the COFF string table - var coffStringTable = ParseCOFFStringTable(data, coffSymbolTableAddress); - if (coffStringTable == null) - return null; - - // Set the COFF string table - executable.COFFStringTable = coffStringTable; - } - - #endregion - - #region Attribute Certificate Table - - if (optionalHeader.CertificateTable != null && optionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the COFF symbol table doesn't exist - int certificateTableAddress = initialOffset - + (int)optionalHeader.CertificateTable.VirtualAddress; - if (certificateTableAddress >= data.Length) - return executable; - - // Try to parse the attribute certificate table - int endOffset = (int)(certificateTableAddress + optionalHeader.CertificateTable.Size); - var attributeCertificateTable = ParseAttributeCertificateTable(data, certificateTableAddress, endOffset); - if (attributeCertificateTable == null) - return null; - - // Set the attribute certificate table - executable.AttributeCertificateTable = attributeCertificateTable; - } - - #endregion - - #region Delay-Load Directory Table - - if (optionalHeader.DelayImportDescriptor != null && optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the delay-load directory table doesn't exist - int delayLoadDirectoryTableAddress = initialOffset - + (int)optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable); - if (delayLoadDirectoryTableAddress >= data.Length) - return executable; - - // Try to parse the delay-load directory table - var delayLoadDirectoryTable = ParseDelayLoadDirectoryTable(data, delayLoadDirectoryTableAddress); - if (delayLoadDirectoryTable == null) - return null; - - // Set the delay-load directory table - executable.DelayLoadDirectoryTable = delayLoadDirectoryTable; - } - - #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 - if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the debug table doesn't exist - int debugTableAddress = initialOffset - + (int)optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable); - if (debugTableAddress >= data.Length) - return executable; - - // Try to parse the debug table - int endOffset = (int)(debugTableAddress + optionalHeader.Debug.Size); - var debugTable = ParseDebugTable(data, debugTableAddress, endOffset, executable.SectionTable); - if (debugTable == null) - return null; - - // Set the debug table - executable.DebugTable = debugTable; - } - - #endregion - - #region Export Table - - // Should also be in a '.edata' section - if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the export table doesn't exist - int exportTableAddress = initialOffset - + (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable); - if (exportTableAddress >= data.Length) - return executable; - - // Try to parse the export table - var exportTable = ParseExportTable(data, exportTableAddress, executable.SectionTable); - if (exportTable == null) - return null; - - // Set the export table - executable.ExportTable = exportTable; - } - - #endregion - - #region Import Table - - // Should also be in a '.idata' section - if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the import table doesn't exist - int importTableAddress = initialOffset - + (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable); - if (importTableAddress >= data.Length) - return executable; - - // Try to parse the import table - var importTable = ParseImportTable(data, importTableAddress, optionalHeader.Magic, executable.SectionTable); - if (importTable == null) - return null; - - // Set the import table - executable.ImportTable = importTable; - } - - #endregion - - #region Resource Directory Table - - // Should also be in a '.rsrc' section - if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0) - { - // If the offset for the resource directory table doesn't exist - int resourceTableAddress = initialOffset - + (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable); - if (resourceTableAddress >= data.Length) - return executable; - - // Try to parse the resource directory table - var resourceDirectoryTable = ParseResourceDirectoryTable(data, resourceTableAddress, resourceTableAddress, executable.SectionTable); - if (resourceDirectoryTable == null) - return null; - - // Set the resource directory table - executable.ResourceDirectoryTable = resourceDirectoryTable; - } - - #endregion - - // TODO: Finish implementing PE parsing - return executable; - } - - /// - /// Parse a byte array into a Portable Executable COFF file header - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled COFF file header on success, null on error - private static COFFFileHeader ParseCOFFFileHeader(byte[] data, ref int offset) - { - // TODO: Use marshalling here instead of building - var fileHeader = new COFFFileHeader(); - - fileHeader.Machine = (MachineType)data.ReadUInt16(ref offset); - fileHeader.NumberOfSections = data.ReadUInt16(ref offset); - fileHeader.TimeDateStamp = data.ReadUInt32(ref offset); - fileHeader.PointerToSymbolTable = data.ReadUInt32(ref offset); - fileHeader.NumberOfSymbols = data.ReadUInt32(ref offset); - fileHeader.SizeOfOptionalHeader = data.ReadUInt16(ref offset); - fileHeader.Characteristics = (Characteristics)data.ReadUInt16(ref offset); - - return fileHeader; - } - - /// - /// Parse a byte array into an optional header - /// - /// Byte array to parse - /// Offset into the byte array - /// Size of the optional header - /// Filled optional header on success, null on error - private static OptionalHeader ParseOptionalHeader(byte[] data, ref int offset, int optionalSize) - { - int initialOffset = offset; - - // TODO: Use marshalling here instead of building - var optionalHeader = new OptionalHeader(); - - #region Standard Fields - - optionalHeader.Magic = (OptionalHeaderMagicNumber)data.ReadUInt16(ref offset); - optionalHeader.MajorLinkerVersion = data.ReadByte(ref offset); - optionalHeader.MinorLinkerVersion = data.ReadByte(ref offset); - optionalHeader.SizeOfCode = data.ReadUInt32(ref offset); - optionalHeader.SizeOfInitializedData = data.ReadUInt32(ref offset); - optionalHeader.SizeOfUninitializedData = data.ReadUInt32(ref offset); - optionalHeader.AddressOfEntryPoint = data.ReadUInt32(ref offset); - optionalHeader.BaseOfCode = data.ReadUInt32(ref offset); - - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.BaseOfData = data.ReadUInt32(ref offset); - - #endregion - - #region Windows-Specific Fields - - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.ImageBase_PE32 = data.ReadUInt32(ref offset); - else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus) - optionalHeader.ImageBase_PE32Plus = data.ReadUInt64(ref offset); - optionalHeader.SectionAlignment = data.ReadUInt32(ref offset); - optionalHeader.FileAlignment = data.ReadUInt32(ref offset); - optionalHeader.MajorOperatingSystemVersion = data.ReadUInt16(ref offset); - optionalHeader.MinorOperatingSystemVersion = data.ReadUInt16(ref offset); - optionalHeader.MajorImageVersion = data.ReadUInt16(ref offset); - optionalHeader.MinorImageVersion = data.ReadUInt16(ref offset); - optionalHeader.MajorSubsystemVersion = data.ReadUInt16(ref offset); - optionalHeader.MinorSubsystemVersion = data.ReadUInt16(ref offset); - optionalHeader.Win32VersionValue = data.ReadUInt32(ref offset); - optionalHeader.SizeOfImage = data.ReadUInt32(ref offset); - optionalHeader.SizeOfHeaders = data.ReadUInt32(ref offset); - optionalHeader.CheckSum = data.ReadUInt32(ref offset); - optionalHeader.Subsystem = (WindowsSubsystem)data.ReadUInt16(ref offset); - optionalHeader.DllCharacteristics = (DllCharacteristics)data.ReadUInt16(ref offset); - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.SizeOfStackReserve_PE32 = data.ReadUInt32(ref offset); - else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus) - optionalHeader.SizeOfStackReserve_PE32Plus = data.ReadUInt64(ref offset); - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.SizeOfStackCommit_PE32 = data.ReadUInt32(ref offset); - else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus) - optionalHeader.SizeOfStackCommit_PE32Plus = data.ReadUInt64(ref offset); - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.SizeOfHeapReserve_PE32 = data.ReadUInt32(ref offset); - else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus) - optionalHeader.SizeOfHeapReserve_PE32Plus = data.ReadUInt64(ref offset); - if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32) - optionalHeader.SizeOfHeapCommit_PE32 = data.ReadUInt32(ref offset); - else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus) - optionalHeader.SizeOfHeapCommit_PE32Plus = data.ReadUInt64(ref offset); - optionalHeader.LoaderFlags = data.ReadUInt32(ref offset); - optionalHeader.NumberOfRvaAndSizes = data.ReadUInt32(ref offset); - - #endregion - - #region Data Directories - - if (optionalHeader.NumberOfRvaAndSizes >= 1 && offset - initialOffset < optionalSize) - { - optionalHeader.ExportTable = new DataDirectory(); - optionalHeader.ExportTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ExportTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 2 && offset - initialOffset < optionalSize) - { - optionalHeader.ImportTable = new DataDirectory(); - optionalHeader.ImportTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ImportTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 3 && offset - initialOffset < optionalSize) - { - optionalHeader.ResourceTable = new DataDirectory(); - optionalHeader.ResourceTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ResourceTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 4 && offset - initialOffset < optionalSize) - { - optionalHeader.ExceptionTable = new DataDirectory(); - optionalHeader.ExceptionTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ExceptionTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 5 && offset - initialOffset < optionalSize) - { - optionalHeader.CertificateTable = new DataDirectory(); - optionalHeader.CertificateTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.CertificateTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 6 && offset - initialOffset < optionalSize) - { - optionalHeader.BaseRelocationTable = new DataDirectory(); - optionalHeader.BaseRelocationTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.BaseRelocationTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 7 && offset - initialOffset < optionalSize) - { - optionalHeader.Debug = new DataDirectory(); - optionalHeader.Debug.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.Debug.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 8 && offset - initialOffset < optionalSize) - { - optionalHeader.Architecture = data.ReadUInt64(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 9 && offset - initialOffset < optionalSize) - { - optionalHeader.GlobalPtr = new DataDirectory(); - optionalHeader.GlobalPtr.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.GlobalPtr.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 10 && offset - initialOffset < optionalSize) - { - optionalHeader.ThreadLocalStorageTable = new DataDirectory(); - optionalHeader.ThreadLocalStorageTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ThreadLocalStorageTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 11 && offset - initialOffset < optionalSize) - { - optionalHeader.LoadConfigTable = new DataDirectory(); - optionalHeader.LoadConfigTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.LoadConfigTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 12 && offset - initialOffset < optionalSize) - { - optionalHeader.BoundImport = new DataDirectory(); - optionalHeader.BoundImport.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.BoundImport.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 13 && offset - initialOffset < optionalSize) - { - optionalHeader.ImportAddressTable = new DataDirectory(); - optionalHeader.ImportAddressTable.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.ImportAddressTable.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 14 && offset - initialOffset < optionalSize) - { - optionalHeader.DelayImportDescriptor = new DataDirectory(); - optionalHeader.DelayImportDescriptor.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.DelayImportDescriptor.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 15 && offset - initialOffset < optionalSize) - { - optionalHeader.CLRRuntimeHeader = new DataDirectory(); - optionalHeader.CLRRuntimeHeader.VirtualAddress = data.ReadUInt32(ref offset); - optionalHeader.CLRRuntimeHeader.Size = data.ReadUInt32(ref offset); - } - if (optionalHeader.NumberOfRvaAndSizes >= 16 && offset - initialOffset < optionalSize) - { - optionalHeader.Reserved = data.ReadUInt64(ref offset); - } - - #endregion - - return optionalHeader; - } - - /// - /// Parse a byte array into a section table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of section table entries to read - /// Filled section table on success, null on error - private static SectionHeader[] ParseSectionTable(byte[] data, int offset, int count) - { - // TODO: Use marshalling here instead of building - var sectionTable = new SectionHeader[count]; - - for (int i = 0; i < count; i++) - { - var entry = new SectionHeader(); - entry.Name = data.ReadBytes(ref offset, 8); - entry.VirtualSize = data.ReadUInt32(ref offset); - entry.VirtualAddress = data.ReadUInt32(ref offset); - entry.SizeOfRawData = data.ReadUInt32(ref offset); - entry.PointerToRawData = data.ReadUInt32(ref offset); - entry.PointerToRelocations = data.ReadUInt32(ref offset); - entry.PointerToLinenumbers = data.ReadUInt32(ref offset); - entry.NumberOfRelocations = data.ReadUInt16(ref offset); - entry.NumberOfLinenumbers = data.ReadUInt16(ref offset); - entry.Characteristics = (SectionFlags)data.ReadUInt32(ref offset); - entry.COFFRelocations = new COFFRelocation[entry.NumberOfRelocations]; - for (int j = 0; i < entry.NumberOfRelocations; j++) - { - // TODO: Seek to correct location and read data - } - entry.COFFLineNumbers = new COFFLineNumber[entry.NumberOfRelocations]; - for (int j = 0; i < entry.NumberOfRelocations; j++) - { - // TODO: Seek to correct location and read data - } - sectionTable[i] = entry; - } - - return sectionTable; - } - - /// - /// Parse a byte array into a COFF symbol table - /// - /// Byte array to parse - /// Offset into the byte array - /// Number of COFF symbol table entries to read - /// Filled COFF symbol table on success, null on error - private static COFFSymbolTableEntry[] ParseCOFFSymbolTable(byte[] data, int offset, uint count) - { - // TODO: Use marshalling here instead of building - var coffSymbolTable = new COFFSymbolTableEntry[count]; - - int auxSymbolsRemaining = 0; - int currentSymbolType = 0; - - for (int i = 0; i < count; i++) - { - // Standard COFF Symbol Table Entry - if (currentSymbolType == 0) - { - var entry = new COFFSymbolTableEntry(); - entry.ShortName = data.ReadBytes(ref offset, 8); - entry.Zeroes = BitConverter.ToUInt32(entry.ShortName, 0); - if (entry.Zeroes == 0) - { - entry.Offset = BitConverter.ToUInt32(entry.ShortName, 4); - entry.ShortName = null; - } - entry.Value = data.ReadUInt32(ref offset); - entry.SectionNumber = data.ReadUInt16(ref offset); - entry.SymbolType = (SymbolType)data.ReadUInt16(ref offset); - entry.StorageClass = (StorageClass)data.ReadByte(ref offset); - entry.NumberOfAuxSymbols = data.ReadByte(ref offset); - coffSymbolTable[i] = entry; - - auxSymbolsRemaining = entry.NumberOfAuxSymbols; - if (auxSymbolsRemaining == 0) - continue; - - if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL - && entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC - && entry.SectionNumber > 0) - { - currentSymbolType = 1; - } - else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION - && entry.ShortName != null - && ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf - || (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef - { - currentSymbolType = 2; - } - else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL - && entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED - && entry.Value == 0) - { - currentSymbolType = 3; - } - else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE) - { - // TODO: Symbol name should be ".file" - currentSymbolType = 4; - } - else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC) - { - // TODO: Should have the name of a section (like ".text") - currentSymbolType = 5; - } - else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN) - { - currentSymbolType = 6; - } - } - - // Auxiliary Format 1: Function Definitions - else if (currentSymbolType == 1) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat1TagIndex = data.ReadUInt32(ref offset); - entry.AuxFormat1TotalSize = data.ReadUInt32(ref offset); - entry.AuxFormat1PointerToLinenumber = data.ReadUInt32(ref offset); - entry.AuxFormat1PointerToNextFunction = data.ReadUInt32(ref offset); - entry.AuxFormat1Unused = data.ReadUInt16(ref offset); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // Auxiliary Format 2: .bf and .ef Symbols - else if (currentSymbolType == 2) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat2Unused1 = data.ReadUInt32(ref offset); - entry.AuxFormat2Linenumber = data.ReadUInt16(ref offset); - entry.AuxFormat2Unused2 = data.ReadBytes(ref offset, 6); - entry.AuxFormat2PointerToNextFunction = data.ReadUInt32(ref offset); - entry.AuxFormat2Unused3 = data.ReadUInt16(ref offset); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // Auxiliary Format 3: Weak Externals - else if (currentSymbolType == 3) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat3TagIndex = data.ReadUInt32(ref offset); - entry.AuxFormat3Characteristics = data.ReadUInt32(ref offset); - entry.AuxFormat3Unused = data.ReadBytes(ref offset, 10); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // Auxiliary Format 4: Files - else if (currentSymbolType == 4) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat4FileName = data.ReadBytes(ref offset, 18); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // Auxiliary Format 5: Section Definitions - else if (currentSymbolType == 5) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat5Length = data.ReadUInt32(ref offset); - entry.AuxFormat5NumberOfRelocations = data.ReadUInt16(ref offset); - entry.AuxFormat5NumberOfLinenumbers = data.ReadUInt16(ref offset); - entry.AuxFormat5CheckSum = data.ReadUInt32(ref offset); - entry.AuxFormat5Number = data.ReadUInt16(ref offset); - entry.AuxFormat5Selection = data.ReadByte(ref offset); - entry.AuxFormat5Unused = data.ReadBytes(ref offset, 3); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // Auxiliary Format 6: CLR Token Definition - else if (currentSymbolType == 6) - { - var entry = new COFFSymbolTableEntry(); - entry.AuxFormat6AuxType = data.ReadByte(ref offset); - entry.AuxFormat6Reserved1 = data.ReadByte(ref offset); - entry.AuxFormat6SymbolTableIndex = data.ReadUInt32(ref offset); - entry.AuxFormat6Reserved2 = data.ReadBytes(ref offset, 12); - coffSymbolTable[i] = entry; - auxSymbolsRemaining--; - } - - // If we hit the last aux symbol, go back to normal format - if (auxSymbolsRemaining == 0) - currentSymbolType = 0; - } - - return coffSymbolTable; - } - - /// - /// Parse a Stream into a COFF string table - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled COFF string table on success, null on error - private static COFFStringTable ParseCOFFStringTable(byte[] data, int offset) - { - // TODO: Use marshalling here instead of building - var coffStringTable = new COFFStringTable(); - - coffStringTable.TotalSize = data.ReadUInt32(ref offset); - - if (coffStringTable.TotalSize <= 4) - return coffStringTable; - - var strings = new List(); - - uint totalSize = coffStringTable.TotalSize; - while (totalSize > 0 && offset < data.Length) - { - int initialPosition = offset; - string str = data.ReadString(ref offset); - strings.Add(str); - totalSize -= (uint)(offset - initialPosition); - } - - coffStringTable.Strings = strings.ToArray(); - - return coffStringTable; - } - - /// - /// Parse a byte array into an attribute certificate table - /// - /// Byte array to parse - /// Offset into the byte array - /// First address not part of the attribute certificate table - /// Filled attribute certificate on success, null on error - private static AttributeCertificateTableEntry[] ParseAttributeCertificateTable(byte[] data, int offset, int endOffset) - { - var attributeCertificateTable = new List(); - - while (offset < endOffset && offset != data.Length) - { - var entry = new AttributeCertificateTableEntry(); - - entry.Length = data.ReadUInt32(ref offset); - entry.Revision = (WindowsCertificateRevision)data.ReadUInt16(ref offset); - entry.CertificateType = (WindowsCertificateType)data.ReadUInt16(ref offset); - - int certificateDataLength = (int)(entry.Length - 8); - if (certificateDataLength > 0) - entry.Certificate = data.ReadBytes(ref offset, certificateDataLength); - - attributeCertificateTable.Add(entry); - - // Align to the 8-byte boundary - while ((offset % 8) != 0 && offset < endOffset - 1 && offset != data.Length) - _ = data.ReadByte(ref offset); - } - - return attributeCertificateTable.ToArray(); - } - - /// - /// Parse a byte array into a delay-load directory table - /// - /// Byte array to parse - /// Offset into the byte array - /// Filled delay-load directory table on success, null on error - private static DelayLoadDirectoryTable ParseDelayLoadDirectoryTable(byte[] data, int offset) - { - // TODO: Use marshalling here instead of building - var delayLoadDirectoryTable = new DelayLoadDirectoryTable(); - - delayLoadDirectoryTable.Attributes = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.Name = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.ModuleHandle = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.DelayImportAddressTable = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.DelayImportNameTable = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.BoundDelayImportTable = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.UnloadDelayImportTable = data.ReadUInt32(ref offset); - delayLoadDirectoryTable.TimeStamp = data.ReadUInt32(ref offset); - - return delayLoadDirectoryTable; - } - - /// - /// 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 && offset < data.Length) - { - 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 - /// First address not part of the debug table - /// Section table to use for virtual address translation - /// Filled debug table on success, null on error - private static DebugTable ParseDebugTable(byte[] data, int offset, int endOffset, SectionHeader[] sections) - { - // TODO: Use marshalling here instead of building - var debugTable = new DebugTable(); - - var debugDirectoryTable = new List(); - - while (offset < endOffset) - { - var debugDirectoryEntry = new DebugDirectoryEntry(); - - debugDirectoryEntry.Characteristics = data.ReadUInt32(ref offset); - debugDirectoryEntry.TimeDateStamp = data.ReadUInt32(ref offset); - debugDirectoryEntry.MajorVersion = data.ReadUInt16(ref offset); - debugDirectoryEntry.MinorVersion = data.ReadUInt16(ref offset); - debugDirectoryEntry.DebugType = (DebugType)data.ReadUInt32(ref offset); - debugDirectoryEntry.SizeOfData = data.ReadUInt32(ref offset); - debugDirectoryEntry.AddressOfRawData = data.ReadUInt32(ref offset); - debugDirectoryEntry.PointerToRawData = data.ReadUInt32(ref offset); - - debugDirectoryTable.Add(debugDirectoryEntry); - } - - debugTable.DebugDirectoryTable = debugDirectoryTable.ToArray(); - - // TODO: Should we read the debug data in? Most of it is unformatted or undocumented - // TODO: Implement .debug$F (Object Only) / IMAGE_DEBUG_TYPE_FPO - - return debugTable; - } - - /// - /// Parse a byte array into a export table - /// - /// Byte array to parse - /// Offset into the byte array - /// Section table to use for virtual address translation - /// Filled export table on success, null on error - private static ExportTable ParseExportTable(byte[] data, int offset, SectionHeader[] sections) - { - // TODO: Use marshalling here instead of building - var exportTable = new ExportTable(); - - var exportDirectoryTable = new ExportDirectoryTable(); - - exportDirectoryTable.ExportFlags = data.ReadUInt32(ref offset); - exportDirectoryTable.TimeDateStamp = data.ReadUInt32(ref offset); - exportDirectoryTable.MajorVersion = data.ReadUInt16(ref offset); - exportDirectoryTable.MinorVersion = data.ReadUInt16(ref offset); - exportDirectoryTable.NameRVA = data.ReadUInt32(ref offset); - exportDirectoryTable.OrdinalBase = data.ReadUInt32(ref offset); - exportDirectoryTable.AddressTableEntries = data.ReadUInt32(ref offset); - exportDirectoryTable.NumberOfNamePointers = data.ReadUInt32(ref offset); - exportDirectoryTable.ExportAddressTableRVA = data.ReadUInt32(ref offset); - exportDirectoryTable.NamePointerRVA = data.ReadUInt32(ref offset); - exportDirectoryTable.OrdinalTableRVA = data.ReadUInt32(ref offset); - - exportTable.ExportDirectoryTable = exportDirectoryTable; - - // Name - if (exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections) != 0) - { - offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections); - string name = data.ReadString(ref offset, Encoding.ASCII); - exportDirectoryTable.Name = name; - } - - // Address table - if (exportDirectoryTable.AddressTableEntries != 0 && exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections) != 0) - { - offset = (int)exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections); - var exportAddressTable = new ExportAddressTableEntry[exportDirectoryTable.AddressTableEntries]; - - for (int i = 0; i < exportDirectoryTable.AddressTableEntries; i++) - { - var addressTableEntry = new ExportAddressTableEntry(); - - // TODO: Use the optional header address and length to determine if export or forwarder - addressTableEntry.ExportRVA = data.ReadUInt32(ref offset); - addressTableEntry.ForwarderRVA = addressTableEntry.ExportRVA; - - exportAddressTable[i] = addressTableEntry; - } - - exportTable.ExportAddressTable = exportAddressTable; - } - - // Name pointer table - if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections) != 0) - { - offset = (int)exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections); - var namePointerTable = new ExportNamePointerTable(); - - namePointerTable.Pointers = new uint[exportDirectoryTable.NumberOfNamePointers]; - for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++) - { - uint pointer = data.ReadUInt32(ref offset); - namePointerTable.Pointers[i] = pointer; - } - - exportTable.NamePointerTable = namePointerTable; - } - - // Ordinal table - if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections) != 0) - { - offset = (int)exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections); - var exportOrdinalTable = new ExportOrdinalTable(); - - exportOrdinalTable.Indexes = new ushort[exportDirectoryTable.NumberOfNamePointers]; - for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++) - { - ushort pointer = data.ReadUInt16(ref offset); - exportOrdinalTable.Indexes[i] = pointer; - } - - exportTable.OrdinalTable = exportOrdinalTable; - } - - // Name table - if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections) != 0) - { - offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections); - var exportNameTable = new ExportNameTable(); - - exportNameTable.Strings = new string[exportDirectoryTable.NumberOfNamePointers]; - for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++) - { - string str = data.ReadString(ref offset, Encoding.ASCII); - exportNameTable.Strings[i] = str; - } - - exportTable.ExportNameTable = exportNameTable; - } - - return exportTable; - } - - /// - /// Parse a byte array into a import table - /// - /// Byte array to parse - /// Offset into the byte array - /// Optional header magic number indicating PE32 or PE32+ - /// Section table to use for virtual address translation - /// Filled import table on success, null on error - private static ImportTable ParseImportTable(byte[] data, int offset, OptionalHeaderMagicNumber magic, SectionHeader[] sections) - { - // TODO: Use marshalling here instead of building - var importTable = new ImportTable(); - - // Import directory table - var importDirectoryTable = new List(); - - // Loop until the last item (all nulls) are found - while (true) - { - var importDirectoryTableEntry = new ImportDirectoryTableEntry(); - - importDirectoryTableEntry.ImportLookupTableRVA = data.ReadUInt32(ref offset); - importDirectoryTableEntry.TimeDateStamp = data.ReadUInt32(ref offset); - importDirectoryTableEntry.ForwarderChain = data.ReadUInt32(ref offset); - importDirectoryTableEntry.NameRVA = data.ReadUInt32(ref offset); - importDirectoryTableEntry.ImportAddressTableRVA = data.ReadUInt32(ref offset); - - importDirectoryTable.Add(importDirectoryTableEntry); - - // All zero values means the last entry - if (importDirectoryTableEntry.ImportLookupTableRVA == 0 - && importDirectoryTableEntry.TimeDateStamp == 0 - && importDirectoryTableEntry.ForwarderChain == 0 - && importDirectoryTableEntry.NameRVA == 0 - && importDirectoryTableEntry.ImportAddressTableRVA == 0) - break; - } - - importTable.ImportDirectoryTable = importDirectoryTable.ToArray(); - - // Names - for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++) - { - var importDirectoryTableEntry = importTable.ImportDirectoryTable[i]; - if (importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections) == 0) - continue; - - int nameAddress = (int)importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections); - string name = data.ReadString(ref nameAddress, Encoding.ASCII); - importDirectoryTableEntry.Name = name; - } - - // Lookup tables - var importLookupTables = new Dictionary(); - - for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++) - { - var importDirectoryTableEntry = importTable.ImportDirectoryTable[i]; - if (importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections) == 0) - continue; - - int tableAddress = (int)importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections); - var entryLookupTable = new List(); - - while (true) - { - var entryLookupTableEntry = new ImportLookupTableEntry(); - - if (magic == OptionalHeaderMagicNumber.PE32) - { - uint entryValue = data.ReadUInt32(ref tableAddress); - entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0; - if (entryLookupTableEntry.OrdinalNameFlag) - entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000); - else - entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000); - } - else if (magic == OptionalHeaderMagicNumber.PE32Plus) - { - ulong entryValue = data.ReadUInt64(ref tableAddress); - entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0; - if (entryLookupTableEntry.OrdinalNameFlag) - entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000); - else - entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000); - } - - entryLookupTable.Add(entryLookupTableEntry); - - // All zero values means the last entry - if (entryLookupTableEntry.OrdinalNameFlag == false - && entryLookupTableEntry.OrdinalNumber == 0 - && entryLookupTableEntry.HintNameTableRVA == 0) - break; - } - - importLookupTables[i] = entryLookupTable.ToArray(); - } - - importTable.ImportLookupTables = importLookupTables; - - // Address tables - var importAddressTables = new Dictionary(); - - for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++) - { - var importDirectoryTableEntry = importTable.ImportDirectoryTable[i]; - if (importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections) == 0) - continue; - - int tableAddress = (int)importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections); - var addressLookupTable = new List(); - - while (true) - { - var addressLookupTableEntry = new ImportAddressTableEntry(); - - if (magic == OptionalHeaderMagicNumber.PE32) - { - uint entryValue = data.ReadUInt32(ref tableAddress); - addressLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0; - if (addressLookupTableEntry.OrdinalNameFlag) - addressLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000); - else - addressLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000); - } - else if (magic == OptionalHeaderMagicNumber.PE32Plus) - { - ulong entryValue = data.ReadUInt64(ref tableAddress); - addressLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0; - if (addressLookupTableEntry.OrdinalNameFlag) - addressLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000); - else - addressLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000); - } - - addressLookupTable.Add(addressLookupTableEntry); - - // All zero values means the last entry - if (addressLookupTableEntry.OrdinalNameFlag == false - && addressLookupTableEntry.OrdinalNumber == 0 - && addressLookupTableEntry.HintNameTableRVA == 0) - break; - } - - importAddressTables[i] = addressLookupTable.ToArray(); - } - - importTable.ImportAddressTables = importAddressTables; - - // Hint/Name table - var importHintNameTable = new List(); - - if (importTable.ImportLookupTables != null && importTable.ImportLookupTables.Count > 0) - { - // Get the addresses of the hint/name table entries - List hintNameTableEntryAddresses = new List(); - - // If we have import lookup tables - if (importTable.ImportLookupTables != null && importLookupTables.Count > 0) - { - var addresses = importTable.ImportLookupTables - .SelectMany(kvp => kvp.Value) - .Select(ilte => (int)ilte.HintNameTableRVA.ConvertVirtualAddress(sections)); - hintNameTableEntryAddresses.AddRange(addresses); - } - - // If we have import address tables - if (importTable.ImportAddressTables != null && importTable.ImportAddressTables.Count > 0) - { - var addresses = importTable.ImportAddressTables - .SelectMany(kvp => kvp.Value) - .Select(iate => (int)iate.HintNameTableRVA.ConvertVirtualAddress(sections)); - hintNameTableEntryAddresses.AddRange(addresses); - } - - // Sanitize the addresses - hintNameTableEntryAddresses = hintNameTableEntryAddresses.Where(addr => addr != 0) - .Distinct() - .OrderBy(a => a) - .ToList(); - - // If we have any addresses, add them to the table - if (hintNameTableEntryAddresses.Any()) - { - for (int i = 0; i < hintNameTableEntryAddresses.Count; i++) - { - int hintNameTableEntryAddress = hintNameTableEntryAddresses[i]; - var hintNameTableEntry = new HintNameTableEntry(); - - hintNameTableEntry.Hint = data.ReadUInt16(ref hintNameTableEntryAddress); - hintNameTableEntry.Name = data.ReadString(ref hintNameTableEntryAddress, Encoding.ASCII); - - importHintNameTable.Add(hintNameTableEntry); - } - } - } - - importTable.HintNameTable = importHintNameTable.ToArray(); - - return importTable; - } - - /// - /// Parse a byte array into a resource directory table - /// - /// Byte array to parse - /// Offset into the byte array - /// Initial offset to use in address comparisons - /// Section table to use for virtual address translation - /// Filled resource directory table on success, null on error - 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); - if (resourceDirectoryTable.Characteristics != 0) - return null; - - 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); - - // If we have no entries - int totalEntryCount = resourceDirectoryTable.NumberOfNameEntries + resourceDirectoryTable.NumberOfIDEntries; - if (totalEntryCount == 0) - return resourceDirectoryTable; - - // Perform top-level pass of data - resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount]; - for (int i = 0; i < totalEntryCount; i++) - { - var entry = new ResourceDirectoryEntry(); - 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, if needed - if (entry.NameOffset > 0) - { - int nameOffset = (int)(entry.NameOffset + (uint)initialOffset); - - var resourceDirectoryString = new ResourceDirectoryString(); - - resourceDirectoryString.Length = data.ReadUInt16(ref nameOffset); - if (resourceDirectoryString.Length > 0) - resourceDirectoryString.UnicodeString = data.ReadBytes(ref nameOffset, resourceDirectoryString.Length * 2); - - entry.Name = resourceDirectoryString; - } - - resourceDirectoryTable.Entries[i] = entry; - } - - // Loop through and process the entries - foreach (var entry in resourceDirectoryTable.Entries) - { - if (entry.DataEntryOffset > 0) - { - int dataEntryOffset = (int)(entry.DataEntryOffset + (uint)initialOffset); - - var resourceDataEntry = new ResourceDataEntry(); - resourceDataEntry.DataRVA = data.ReadUInt32(ref dataEntryOffset); - resourceDataEntry.Size = data.ReadUInt32(ref dataEntryOffset); - resourceDataEntry.Codepage = data.ReadUInt32(ref dataEntryOffset); - resourceDataEntry.Reserved = data.ReadUInt32(ref dataEntryOffset); - - // Read the data from the offset - dataEntryOffset = (int)resourceDataEntry.DataRVA.ConvertVirtualAddress(sections); - if (dataEntryOffset > 0 && resourceDataEntry.Size > 0) - resourceDataEntry.Data = data.ReadBytes(ref dataEntryOffset, (int)resourceDataEntry.Size); - - entry.DataEntry = resourceDataEntry; - } - else if (entry.SubdirectoryOffset > 0) - { - int subdirectoryOffset = (int)(entry.SubdirectoryOffset + (uint)initialOffset); - entry.Subdirectory = ParseResourceDirectoryTable(data, subdirectoryOffset, initialOffset, sections); - } - } - - return resourceDirectoryTable; + // Create a memory stream and parse that + MemoryStream dataStream = new MemoryStream(data); + dataStream.Position = offset; + return ParseExecutable(dataStream); } #endregion