mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-13 21:31:04 +00:00
655 lines
31 KiB
C#
655 lines
31 KiB
C#
using System.IO;
|
|
using BurnOutSharp.Models.PortableExecutable;
|
|
|
|
namespace BurnOutSharp.Builder
|
|
{
|
|
// TODO: Make Stream Data rely on Byte Data
|
|
public static class PortableExecutable
|
|
{
|
|
#region Byte Data
|
|
|
|
/// <summary>
|
|
/// Parse a byte array into a Portable 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;
|
|
|
|
// 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
|
|
|
|
// TODO: Finish implementing PE parsing
|
|
return executable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a byte array into a Portable Executable COFF file header
|
|
/// </summary>
|
|
/// <param name="data">Byte array to parse</param>
|
|
/// <param name="offset">Offset into the byte array</param>
|
|
/// <returns>Filled COFF file header on success, null on error</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a byte array into an optional header
|
|
/// </summary>
|
|
/// <param name="data">Byte array to parse</param>
|
|
/// <param name="offset">Offset into the byte array</param>
|
|
/// <param name="optionalSize">Size of the optional header</param>
|
|
/// <returns>Filled optional header on success, null on error</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a byte array into a section table
|
|
/// </summary>
|
|
/// <param name="data">Byte array to parse</param>
|
|
/// <param name="offset">Offset into the byte array</param>
|
|
/// <param name="count">Number of section table entries to read</param>
|
|
/// <returns>Filled section table on success, null on error</returns>
|
|
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;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Stream Data
|
|
|
|
/// <summary>
|
|
/// Parse a Stream into a Portable 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)
|
|
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 Signature
|
|
|
|
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
|
|
executable.Signature = new byte[4];
|
|
for (int i = 0; i < executable.Signature.Length; i++)
|
|
{
|
|
executable.Signature[i] = data.ReadByteValue();
|
|
}
|
|
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);
|
|
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, 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, coffFileHeader.NumberOfSections);
|
|
if (sectionTable == null)
|
|
return null;
|
|
|
|
// Set the section table
|
|
executable.SectionTable = sectionTable;
|
|
|
|
#endregion
|
|
|
|
// TODO: Finish implementing PE parsing
|
|
return executable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a Stream into a Portable Executable COFF file header
|
|
/// </summary>
|
|
/// <param name="data">Stream to parse</param>
|
|
/// <returns>Filled executable header on success, null on error</returns>
|
|
private static COFFFileHeader ParseCOFFFileHeader(Stream data)
|
|
{
|
|
// TODO: Use marshalling here instead of building
|
|
var fileHeader = new COFFFileHeader();
|
|
|
|
fileHeader.Machine = (MachineType)data.ReadUInt16();
|
|
fileHeader.NumberOfSections = data.ReadUInt16();
|
|
fileHeader.TimeDateStamp = data.ReadUInt32();
|
|
fileHeader.PointerToSymbolTable = data.ReadUInt32();
|
|
fileHeader.NumberOfSymbols = data.ReadUInt32();
|
|
fileHeader.SizeOfOptionalHeader = data.ReadUInt16();
|
|
fileHeader.Characteristics = (Characteristics)data.ReadUInt16();
|
|
|
|
return fileHeader;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a Stream into an optional header
|
|
/// </summary>
|
|
/// <param name="data">Stream to parse</param>
|
|
/// <param name="optionalSize">Size of the optional header</param>
|
|
/// <returns>Filled optional header on success, null on error</returns>
|
|
private static OptionalHeader ParseOptionalHeader(Stream data, int optionalSize)
|
|
{
|
|
long initialOffset = data.Position;
|
|
|
|
// TODO: Use marshalling here instead of building
|
|
var optionalHeader = new OptionalHeader();
|
|
|
|
#region Standard Fields
|
|
|
|
optionalHeader.Magic = (OptionalHeaderMagicNumber)data.ReadUInt16();
|
|
optionalHeader.MajorLinkerVersion = data.ReadByteValue();
|
|
optionalHeader.MinorLinkerVersion = data.ReadByteValue();
|
|
optionalHeader.SizeOfCode = data.ReadUInt32();
|
|
optionalHeader.SizeOfInitializedData = data.ReadUInt32();
|
|
optionalHeader.SizeOfUninitializedData = data.ReadUInt32();
|
|
optionalHeader.AddressOfEntryPoint = data.ReadUInt32();
|
|
optionalHeader.BaseOfCode = data.ReadUInt32();
|
|
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.BaseOfData = data.ReadUInt32();
|
|
|
|
#endregion
|
|
|
|
#region Windows-Specific Fields
|
|
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.ImageBase_PE32 = data.ReadUInt32();
|
|
else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
|
|
optionalHeader.ImageBase_PE32Plus = data.ReadUInt64();
|
|
optionalHeader.SectionAlignment = data.ReadUInt32();
|
|
optionalHeader.FileAlignment = data.ReadUInt32();
|
|
optionalHeader.MajorOperatingSystemVersion = data.ReadUInt16();
|
|
optionalHeader.MinorOperatingSystemVersion = data.ReadUInt16();
|
|
optionalHeader.MajorImageVersion = data.ReadUInt16();
|
|
optionalHeader.MinorImageVersion = data.ReadUInt16();
|
|
optionalHeader.MajorSubsystemVersion = data.ReadUInt16();
|
|
optionalHeader.MinorSubsystemVersion = data.ReadUInt16();
|
|
optionalHeader.Win32VersionValue = data.ReadUInt32();
|
|
optionalHeader.SizeOfImage = data.ReadUInt32();
|
|
optionalHeader.SizeOfHeaders = data.ReadUInt32();
|
|
optionalHeader.CheckSum = data.ReadUInt32();
|
|
optionalHeader.Subsystem = (WindowsSubsystem)data.ReadUInt16();
|
|
optionalHeader.DllCharacteristics = (DllCharacteristics)data.ReadUInt16();
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.SizeOfStackReserve_PE32 = data.ReadUInt32();
|
|
else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
|
|
optionalHeader.SizeOfStackReserve_PE32Plus = data.ReadUInt64();
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.SizeOfStackCommit_PE32 = data.ReadUInt32();
|
|
else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
|
|
optionalHeader.SizeOfStackCommit_PE32Plus = data.ReadUInt64();
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.SizeOfHeapReserve_PE32 = data.ReadUInt32();
|
|
else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
|
|
optionalHeader.SizeOfHeapReserve_PE32Plus = data.ReadUInt64();
|
|
if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
|
|
optionalHeader.SizeOfHeapCommit_PE32 = data.ReadUInt32();
|
|
else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
|
|
optionalHeader.SizeOfHeapCommit_PE32Plus = data.ReadUInt64();
|
|
optionalHeader.LoaderFlags = data.ReadUInt32();
|
|
optionalHeader.NumberOfRvaAndSizes = data.ReadUInt32();
|
|
|
|
#endregion
|
|
|
|
#region Data Directories
|
|
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 1 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ExportTable = new DataDirectory();
|
|
optionalHeader.ExportTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ExportTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 2 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ImportTable = new DataDirectory();
|
|
optionalHeader.ImportTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ImportTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 3 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ResourceTable = new DataDirectory();
|
|
optionalHeader.ResourceTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ResourceTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 4 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ExceptionTable = new DataDirectory();
|
|
optionalHeader.ExceptionTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ExceptionTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 5 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.CertificateTable = new DataDirectory();
|
|
optionalHeader.CertificateTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.CertificateTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 6 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.BaseRelocationTable = new DataDirectory();
|
|
optionalHeader.BaseRelocationTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.BaseRelocationTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 7 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.Debug = new DataDirectory();
|
|
optionalHeader.Debug.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.Debug.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 8 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.Architecture = data.ReadUInt64();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 9 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.GlobalPtr = new DataDirectory();
|
|
optionalHeader.GlobalPtr.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.GlobalPtr.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 10 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ThreadLocalStorageTable = new DataDirectory();
|
|
optionalHeader.ThreadLocalStorageTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ThreadLocalStorageTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 11 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.LoadConfigTable = new DataDirectory();
|
|
optionalHeader.LoadConfigTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.LoadConfigTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 12 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.BoundImport = new DataDirectory();
|
|
optionalHeader.BoundImport.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.BoundImport.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 13 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.ImportAddressTable = new DataDirectory();
|
|
optionalHeader.ImportAddressTable.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.ImportAddressTable.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 14 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.DelayImportDescriptor = new DataDirectory();
|
|
optionalHeader.DelayImportDescriptor.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.DelayImportDescriptor.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 15 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.CLRRuntimeHeader = new DataDirectory();
|
|
optionalHeader.CLRRuntimeHeader.VirtualAddress = data.ReadUInt32();
|
|
optionalHeader.CLRRuntimeHeader.Size = data.ReadUInt32();
|
|
}
|
|
if (optionalHeader.NumberOfRvaAndSizes >= 16 && data.Position - initialOffset < optionalSize)
|
|
{
|
|
optionalHeader.Reserved = data.ReadUInt64();
|
|
}
|
|
|
|
#endregion
|
|
|
|
return optionalHeader;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a Stream into a section table
|
|
/// </summary>
|
|
/// <param name="data">Stream to parse</param>
|
|
/// <param name="count">Number of section table entries to read</param>
|
|
/// <returns>Filled section table on success, null on error</returns>
|
|
private static SectionHeader[] ParseSectionTable(Stream data, 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(8);
|
|
entry.VirtualSize = data.ReadUInt32();
|
|
entry.VirtualAddress = data.ReadUInt32();
|
|
entry.SizeOfRawData = data.ReadUInt32();
|
|
entry.PointerToRawData = data.ReadUInt32();
|
|
entry.PointerToRelocations = data.ReadUInt32();
|
|
entry.PointerToLinenumbers = data.ReadUInt32();
|
|
entry.NumberOfRelocations = data.ReadUInt16();
|
|
entry.NumberOfLinenumbers = data.ReadUInt16();
|
|
entry.Characteristics = (SectionFlags)data.ReadUInt32();
|
|
entry.COFFRelocations = new COFFRelocation[entry.NumberOfRelocations];
|
|
for (int j = 0; j < entry.NumberOfRelocations; j++)
|
|
{
|
|
// TODO: Seek to correct location and read data
|
|
}
|
|
entry.COFFLineNumbers = new COFFLineNumber[entry.NumberOfLinenumbers];
|
|
for (int j = 0; j < entry.NumberOfLinenumbers; j++)
|
|
{
|
|
// TODO: Seek to correct location and read data
|
|
}
|
|
sectionTable[i] = entry;
|
|
}
|
|
|
|
return sectionTable;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |