Files
BinaryObjectScanner/BinaryObjectScanner.Builders/LinearExecutable.cs
Matt Nadareski 473cbc5694 BOS.* -> BOS.*
2023-03-07 16:59:14 -05:00

943 lines
38 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Text;
using BinaryObjectScanner.Models.LinearExecutable;
using BinaryObjectScanner.Utilities;
using static BinaryObjectScanner.Models.LinearExecutable.Constants;
namespace BinaryObjectScanner.Builders
{
public static class LinearExecutable
{
#region Byte Data
/// <summary>
/// Parse a byte array into a Linear Executable
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
return ParseExecutable(dataStream);
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Linear Executable
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(Stream data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Information Block
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var informationBlock = ParseInformationBlock(data);
if (informationBlock == null)
return null;
// Set the executable header
executable.InformationBlock = informationBlock;
#endregion
#region Object Table
// Get the object table offset
long offset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object table
data.Seek(offset, SeekOrigin.Begin);
// Create the object table
executable.ObjectTable = new ObjectTableEntry[informationBlock.ObjectTableCount];
// Try to parse the object table
for (int i = 0; i < executable.ObjectTable.Length; i++)
{
var entry = ParseObjectTableEntry(data);
if (entry == null)
return null;
executable.ObjectTable[i] = entry;
}
}
#endregion
#region Object Page Map
// Get the object page map offset
offset = informationBlock.ObjectPageMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// Create the object page map
executable.ObjectPageMap = new ObjectPageMapEntry[informationBlock.ObjectTableCount];
// Try to parse the object page map
for (int i = 0; i < executable.ObjectPageMap.Length; i++)
{
var entry = ParseObjectPageMapEntry(data);
if (entry == null)
return null;
executable.ObjectPageMap[i] = entry;
}
}
#endregion
#region Object Iterate Data Map
offset = informationBlock.ObjectIterateDataMapOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the object page map
data.Seek(offset, SeekOrigin.Begin);
// TODO: Implement when model found
// No model has been found in the documentation about what
// each of the entries looks like for this map.
}
#endregion
#region Resource Table
// Get the resource table offset
offset = informationBlock.ResourceTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resource table
data.Seek(offset, SeekOrigin.Begin);
// Create the resource table
executable.ResourceTable = new ResourceTableEntry[informationBlock.ResourceTableCount];
// Try to parse the resource table
for (int i = 0; i < executable.ResourceTable.Length; i++)
{
var entry = ParseResourceTableEntry(data);
if (entry == null)
return null;
executable.ResourceTable[i] = entry;
}
}
#endregion
#region Resident Names Table
// Get the resident names table offset
offset = informationBlock.ResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the resident names table
var residentNamesTable = new List<ResidentNamesTableEntry>();
// Try to parse the resident names table
while (true)
{
var entry = ParseResidentNamesTableEntry(data);
residentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the resident names table
executable.ResidentNamesTable = residentNamesTable.ToArray();
}
#endregion
#region Entry Table
// Get the entry table offset
offset = informationBlock.EntryTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the entry table
data.Seek(offset, SeekOrigin.Begin);
// Create the entry table
var entryTable = new List<EntryTableBundle>();
// Try to parse the entry table
while (true)
{
var bundle = ParseEntryTableBundle(data);
entryTable.Add(bundle);
// If we have a 0-length entry
if (bundle.Entries == 0)
break;
}
// Assign the entry table
executable.EntryTable = entryTable.ToArray();
}
#endregion
#region Module Format Directives Table
// Get the module format directives table offset
offset = informationBlock.ModuleDirectivesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the module format directives table
data.Seek(offset, SeekOrigin.Begin);
// Create the module format directives table
executable.ModuleFormatDirectivesTable = new ModuleFormatDirectivesTableEntry[informationBlock.ModuleDirectivesCount];
// Try to parse the module format directives table
for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++)
{
var entry = ParseModuleFormatDirectivesTableEntry(data);
if (entry == null)
return null;
executable.ModuleFormatDirectivesTable[i] = entry;
}
}
#endregion
#region Verify Record Directive Table
// TODO: Figure out where the offset to this table is stored
// The documentation suggests it's either part of or immediately following
// the Module Format Directives Table
#endregion
#region Fix-up Page Table
// Get the fix-up page table offset
offset = informationBlock.FixupPageTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up page table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up page table
executable.FixupPageTable = new FixupPageTableEntry[executable.ObjectPageMap.Length + 1];
// Try to parse the fix-up page table
for (int i = 0; i < executable.FixupPageTable.Length; i++)
{
var entry = ParseFixupPageTableEntry(data);
if (entry == null)
return null;
executable.FixupPageTable[i] = entry;
}
}
#endregion
#region Fix-up Record Table
// Get the fix-up record table offset
offset = informationBlock.FixupRecordTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the fix-up record table
data.Seek(offset, SeekOrigin.Begin);
// Create the fix-up record table
executable.FixupRecordTable = new FixupRecordTableEntry[executable.ObjectPageMap.Length + 1];
// Try to parse the fix-up record table
for (int i = 0; i < executable.FixupRecordTable.Length; i++)
{
var entry = ParseFixupRecordTableEntry(data);
if (entry == null)
return null;
executable.FixupRecordTable[i] = entry;
}
}
#endregion
#region Imported Module Name Table
// Get the imported module name table offset
offset = informationBlock.ImportedModulesNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module name table
data.Seek(offset, SeekOrigin.Begin);
// Create the imported module name table
executable.ImportModuleNameTable = new ImportModuleNameTableEntry[informationBlock.ImportedModulesCount];
// Try to parse the imported module name table
for (int i = 0; i < executable.ImportModuleNameTable.Length; i++)
{
var entry = ParseImportModuleNameTableEntry(data);
if (entry == null)
return null;
executable.ImportModuleNameTable[i] = entry;
}
}
#endregion
#region Imported Module Procedure Name Table
// Get the imported module procedure name table offset
offset = informationBlock.ImportProcedureNameTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the imported module procedure name table
data.Seek(offset, SeekOrigin.Begin);
// Get the size of the imported module procedure name table
long tableSize = informationBlock.FixupPageTableOffset
+ informationBlock.FixupSectionSize
- informationBlock.ImportProcedureNameTableOffset;
// Create the imported module procedure name table
var importModuleProcedureNameTable = new List<ImportModuleProcedureNameTableEntry>();
// Try to parse the imported module procedure name table
while (data.Position < offset + tableSize)
{
var entry = ParseImportModuleProcedureNameTableEntry(data);
if (entry == null)
return null;
importModuleProcedureNameTable.Add(entry);
}
// Assign the resident names table
executable.ImportModuleProcedureNameTable = importModuleProcedureNameTable.ToArray();
}
#endregion
#region Per-Page Checksum Table
// Get the per-page checksum table offset
offset = informationBlock.PerPageChecksumTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the per-page checksum name table
data.Seek(offset, SeekOrigin.Begin);
// Create the per-page checksum name table
executable.PerPageChecksumTable = new PerPageChecksumTableEntry[informationBlock.ModuleNumberPages];
// Try to parse the per-page checksum name table
for (int i = 0; i < executable.PerPageChecksumTable.Length; i++)
{
var entry = ParsePerPageChecksumTableEntry(data);
if (entry == null)
return null;
executable.PerPageChecksumTable[i] = entry;
}
}
#endregion
#region Non-Resident Names Table
// Get the non-resident names table offset
offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the non-resident names table
data.Seek(offset, SeekOrigin.Begin);
// Create the non-resident names table
var nonResidentNamesTable = new List<NonResidentNamesTableEntry>();
// Try to parse the non-resident names table
while (true)
{
var entry = ParseNonResidentNameTableEntry(data);
nonResidentNamesTable.Add(entry);
// If we have a 0-length entry
if (entry.Length == 0)
break;
}
// Assign the non-resident names table
executable.NonResidentNamesTable = nonResidentNamesTable.ToArray();
}
#endregion
#region Debug Information
// Get the debug information offset
offset = informationBlock.DebugInformationOffset + stub.Header.NewExeHeaderAddr;
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
{
// Seek to the debug information
data.Seek(offset, SeekOrigin.Begin);
// Try to parse the debug information
var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength);
if (debugInformation == null)
return null;
// Set the debug information
executable.DebugInformation = debugInformation;
}
#endregion
return executable;
}
/// <summary>
/// Parse a Stream into an information block
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled information block on success, null on error</returns>
private static InformationBlock ParseInformationBlock(Stream data)
{
// TODO: Use marshalling here instead of building
var informationBlock = new InformationBlock();
byte[] magic = data.ReadBytes(2);
informationBlock.Signature = Encoding.ASCII.GetString(magic);
if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString)
return null;
informationBlock.ByteOrder = (ByteOrder)data.ReadByteValue();
informationBlock.WordOrder = (WordOrder)data.ReadByteValue();
informationBlock.ExecutableFormatLevel = data.ReadUInt32();
informationBlock.CPUType = (CPUType)data.ReadUInt16();
informationBlock.ModuleOS = (OperatingSystem)data.ReadUInt16();
informationBlock.ModuleVersion = data.ReadUInt32();
informationBlock.ModuleTypeFlags = (ModuleFlags)data.ReadUInt32();
informationBlock.ModuleNumberPages = data.ReadUInt32();
informationBlock.InitialObjectCS = data.ReadUInt32();
informationBlock.InitialEIP = data.ReadUInt32();
informationBlock.InitialObjectSS = data.ReadUInt32();
informationBlock.InitialESP = data.ReadUInt32();
informationBlock.MemoryPageSize = data.ReadUInt32();
informationBlock.BytesOnLastPage = data.ReadUInt32();
informationBlock.FixupSectionSize = data.ReadUInt32();
informationBlock.FixupSectionChecksum = data.ReadUInt32();
informationBlock.LoaderSectionSize = data.ReadUInt32();
informationBlock.LoaderSectionChecksum = data.ReadUInt32();
informationBlock.ObjectTableOffset = data.ReadUInt32();
informationBlock.ObjectTableCount = data.ReadUInt32();
informationBlock.ObjectPageMapOffset = data.ReadUInt32();
informationBlock.ObjectIterateDataMapOffset = data.ReadUInt32();
informationBlock.ResourceTableOffset = data.ReadUInt32();
informationBlock.ResourceTableCount = data.ReadUInt32();
informationBlock.ResidentNamesTableOffset = data.ReadUInt32();
informationBlock.EntryTableOffset = data.ReadUInt32();
informationBlock.ModuleDirectivesTableOffset = data.ReadUInt32();
informationBlock.ModuleDirectivesCount = data.ReadUInt32();
informationBlock.FixupPageTableOffset = data.ReadUInt32();
informationBlock.FixupRecordTableOffset = data.ReadUInt32();
informationBlock.ImportedModulesNameTableOffset = data.ReadUInt32();
informationBlock.ImportedModulesCount = data.ReadUInt32();
informationBlock.ImportProcedureNameTableOffset = data.ReadUInt32();
informationBlock.PerPageChecksumTableOffset = data.ReadUInt32();
informationBlock.DataPagesOffset = data.ReadUInt32();
informationBlock.PreloadPageCount = data.ReadUInt32();
informationBlock.NonResidentNamesTableOffset = data.ReadUInt32();
informationBlock.NonResidentNamesTableLength = data.ReadUInt32();
informationBlock.NonResidentNamesTableChecksum = data.ReadUInt32();
informationBlock.AutomaticDataObject = data.ReadUInt32();
informationBlock.DebugInformationOffset = data.ReadUInt32();
informationBlock.DebugInformationLength = data.ReadUInt32();
informationBlock.PreloadInstancePagesNumber = data.ReadUInt32();
informationBlock.DemandInstancePagesNumber = data.ReadUInt32();
informationBlock.ExtraHeapAllocation = data.ReadUInt32();
return informationBlock;
}
/// <summary>
/// Parse a Stream into an object table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled object table entry on success, null on error</returns>
private static ObjectTableEntry ParseObjectTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ObjectTableEntry();
entry.VirtualSegmentSize = data.ReadUInt32();
entry.RelocationBaseAddress = data.ReadUInt32();
entry.ObjectFlags = (ObjectFlags)data.ReadUInt16();
entry.PageTableIndex = data.ReadUInt32();
entry.PageTableEntries = data.ReadUInt32();
entry.Reserved = data.ReadUInt32();
return entry;
}
/// <summary>
/// Parse a Stream into an object page map entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled object page map entry on success, null on error</returns>
private static ObjectPageMapEntry ParseObjectPageMapEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ObjectPageMapEntry();
entry.PageDataOffset = data.ReadUInt32();
entry.DataSize = data.ReadUInt16();
entry.Flags = (ObjectPageFlags)data.ReadUInt16();
return entry;
}
/// <summary>
/// Parse a Stream into a resource table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled resource table entry on success, null on error</returns>
private static ResourceTableEntry ParseResourceTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ResourceTableEntry();
entry.TypeID = (ResourceTableEntryType)data.ReadUInt32();
entry.NameID = data.ReadUInt16();
entry.ResourceSize = data.ReadUInt32();
entry.ObjectNumber = data.ReadUInt16();
entry.Offset = data.ReadUInt32();
return entry;
}
/// <summary>
/// Parse a Stream into a resident names table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled resident names table entry on success, null on error</returns>
private static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ResidentNamesTableEntry();
entry.Length = data.ReadByteValue();
if (entry.Length > 0)
{
byte[] name = data.ReadBytes(entry.Length);
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
}
entry.OrdinalNumber = data.ReadUInt16();
return entry;
}
/// <summary>
/// Parse a Stream into an entry table bundle
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled entry table bundle on success, null on error</returns>
private static EntryTableBundle ParseEntryTableBundle(Stream data)
{
// TODO: Use marshalling here instead of building
var bundle = new EntryTableBundle();
bundle.Entries = data.ReadByteValue();
if (bundle.Entries == 0)
return bundle;
bundle.BundleType = (BundleType)data.ReadByteValue();
bundle.TableEntries = new EntryTableEntry[bundle.Entries];
for (int i = 0; i < bundle.Entries; i++)
{
var entry = new EntryTableEntry();
switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent)
{
case BundleType.UnusedEntry:
// Empty entry with no information
break;
case BundleType.SixteenBitEntry:
entry.SixteenBitObjectNumber = data.ReadUInt16();
entry.SixteenBitEntryFlags = (EntryFlags)data.ReadByteValue();
entry.SixteenBitOffset = data.ReadUInt16();
break;
case BundleType.TwoEightySixCallGateEntry:
entry.TwoEightySixObjectNumber = data.ReadUInt16();
entry.TwoEightySixEntryFlags = (EntryFlags)data.ReadByteValue();
entry.TwoEightySixOffset = data.ReadUInt16();
entry.TwoEightySixCallgate = data.ReadUInt16();
break;
case BundleType.ThirtyTwoBitEntry:
entry.ThirtyTwoBitObjectNumber = data.ReadUInt16();
entry.ThirtyTwoBitEntryFlags = (EntryFlags)data.ReadByteValue();
entry.ThirtyTwoBitOffset = data.ReadUInt32();
break;
case BundleType.ForwarderEntry:
entry.ForwarderReserved = data.ReadUInt16();
entry.ForwarderFlags = (ForwarderFlags)data.ReadByteValue();
entry.ForwarderModuleOrdinalNumber = data.ReadUInt16();
entry.ProcedureNameOffset = data.ReadUInt32();
entry.ImportOrdinalNumber = data.ReadUInt32();
break;
default:
return null;
}
bundle.TableEntries[i] = entry;
}
return bundle;
}
/// <summary>
/// Parse a Stream into a module format directives table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled module format directives table entry on success, null on error</returns>
private static ModuleFormatDirectivesTableEntry ParseModuleFormatDirectivesTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ModuleFormatDirectivesTableEntry();
entry.DirectiveNumber = (DirectiveNumber)data.ReadUInt16();
entry.DirectiveDataLength = data.ReadUInt16();
entry.DirectiveDataOffset = data.ReadUInt32();
return entry;
}
/// <summary>
/// Parse a Stream into a verify record directive table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled verify record directive table entry on success, null on error</returns>
private static VerifyRecordDirectiveTableEntry ParseVerifyRecordDirectiveTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new VerifyRecordDirectiveTableEntry();
entry.EntryCount = data.ReadUInt16();
entry.OrdinalIndex = data.ReadUInt16();
entry.Version = data.ReadUInt16();
entry.ObjectEntriesCount = data.ReadUInt16();
entry.ObjectNumberInModule = data.ReadUInt16();
entry.ObjectLoadBaseAddress = data.ReadUInt16();
entry.ObjectVirtualAddressSize = data.ReadUInt16();
return entry;
}
/// <summary>
/// Parse a Stream into a fix-up page table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled fix-up page table entry on success, null on error</returns>
private static FixupPageTableEntry ParseFixupPageTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new FixupPageTableEntry();
entry.Offset = data.ReadUInt32();
return entry;
}
/// <summary>
/// Parse a Stream into a fix-up record table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled fix-up record table entry on success, null on error</returns>
private static FixupRecordTableEntry ParseFixupRecordTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new FixupRecordTableEntry();
entry.SourceType = (FixupRecordSourceType)data.ReadByteValue();
entry.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue();
// Source list flag
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
entry.SourceOffsetListCount = data.ReadByteValue();
else
entry.SourceOffset = data.ReadUInt16();
// OBJECT / TRGOFF
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference))
{
// 16-bit Object Number/Module Ordinal Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
entry.TargetObjectNumberWORD = data.ReadUInt16();
else
entry.TargetObjectNumberByte = data.ReadByteValue();
// 16-bit Selector fixup
if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup))
{
// 32-bit Target Offset Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
entry.TargetOffsetDWORD = data.ReadUInt32();
else
entry.TargetOffsetWORD = data.ReadUInt16();
}
}
// MOD ORD# / IMPORT ORD / ADDITIVE
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal))
{
// 16-bit Object Number/Module Ordinal Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
else
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
// 8-bit Ordinal Flag & 32-bit Target Offset Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag))
entry.ImportedOrdinalNumberByte = data.ReadByteValue();
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
entry.ImportedOrdinalNumberDWORD = data.ReadUInt32();
else
entry.ImportedOrdinalNumberWORD = data.ReadUInt16();
// Additive Fixup Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
{
// 32-bit Additive Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
else
entry.AdditiveFixupValueWORD = data.ReadUInt16();
}
}
// MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName))
{
// 16-bit Object Number/Module Ordinal Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
else
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
// 32-bit Target Offset Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
entry.OffsetImportProcedureNameTableDWORD = data.ReadUInt32();
else
entry.OffsetImportProcedureNameTableWORD = data.ReadUInt16();
// Additive Fixup Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
{
// 32-bit Additive Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
else
entry.AdditiveFixupValueWORD = data.ReadUInt16();
}
}
// ORD # / ADDITIVE
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable))
{
// 16-bit Object Number/Module Ordinal Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
else
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
// Additive Fixup Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
{
// 32-bit Additive Flag
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
else
entry.AdditiveFixupValueWORD = data.ReadUInt16();
}
}
// No other top-level flags recognized
else
{
return null;
}
#region SCROFFn
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
{
entry.SourceOffsetList = new ushort[entry.SourceOffsetListCount];
for (int i = 0; i < entry.SourceOffsetList.Length; i++)
{
entry.SourceOffsetList[i] = data.ReadUInt16();
}
}
#endregion
return entry;
}
/// <summary>
/// Parse a Stream into a import module name table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled import module name table entry on success, null on error</returns>
private static ImportModuleNameTableEntry ParseImportModuleNameTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ImportModuleNameTableEntry();
entry.Length = data.ReadByteValue();
if (entry.Length > 0)
{
byte[] name = data.ReadBytes(entry.Length);
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
}
return entry;
}
/// <summary>
/// Parse a Stream into a import module name table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled import module name table entry on success, null on error</returns>
private static ImportModuleProcedureNameTableEntry ParseImportModuleProcedureNameTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new ImportModuleProcedureNameTableEntry();
entry.Length = data.ReadByteValue();
if (entry.Length > 0)
{
byte[] name = data.ReadBytes(entry.Length);
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
}
return entry;
}
/// <summary>
/// Parse a Stream into a per-page checksum table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled per-page checksum table entry on success, null on error</returns>
private static PerPageChecksumTableEntry ParsePerPageChecksumTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new PerPageChecksumTableEntry();
entry.Checksum = data.ReadUInt32();
return entry;
}
/// <summary>
/// Parse a Stream into a non-resident names table entry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled non-resident names table entry on success, null on error</returns>
private static NonResidentNamesTableEntry ParseNonResidentNameTableEntry(Stream data)
{
// TODO: Use marshalling here instead of building
var entry = new NonResidentNamesTableEntry();
entry.Length = data.ReadByteValue();
if (entry.Length > 0)
{
byte[] name = data.ReadBytes(entry.Length);
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
}
entry.OrdinalNumber = data.ReadUInt16();
return entry;
}
/// <summary>
/// Parse a Stream into a debug information
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="size">Total size of the debug information</param>
/// <returns>Filled debug information on success, null on error</returns>
private static DebugInformation ParseDebugInformation(Stream data, long size)
{
// TODO: Use marshalling here instead of building
var debugInformation = new DebugInformation();
byte[] signature = data.ReadBytes(3);
debugInformation.Signature = Encoding.ASCII.GetString(signature);
if (debugInformation.Signature != DebugInformationSignatureString)
return null;
debugInformation.FormatType = (DebugFormatType)data.ReadByteValue();
debugInformation.DebuggerData = data.ReadBytes((int)(size - 4));
return debugInformation;
}
#endregion
}
}