From 750cecfdafe4eb98e4e03dc3ba8bdf804e49e808 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 10 Nov 2022 21:24:28 -0800 Subject: [PATCH] Add PE partial debug table parsing --- BurnOutSharp.Builder/PortableExecutable.cs | 124 ++++++++++++++++++ .../PortableExecutable/DebugTable.cs | 38 ++++++ .../PortableExecutable/Executable.cs | 8 +- 3 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 BurnOutSharp.Models/PortableExecutable/DebugTable.cs diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs index 9b0ccb69..6903fd0d 100644 --- a/BurnOutSharp.Builder/PortableExecutable.cs +++ b/BurnOutSharp.Builder/PortableExecutable.cs @@ -174,6 +174,29 @@ namespace BurnOutSharp.Builder #endregion + #region Debug Table + + // Should also be in the '.debug' section + if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress != 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.CertificateTable.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 the '.rsrc' section @@ -714,6 +737,45 @@ namespace BurnOutSharp.Builder return delayLoadDirectoryTable; } + /// + /// Parse a Stream 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 /// @@ -1298,6 +1360,30 @@ namespace BurnOutSharp.Builder #endregion + #region Debug Table + + // Should also be in the '.debug' section + if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress != 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 + data.Seek(debugTableAddress, SeekOrigin.Begin); + int endOffset = (int)(debugTableAddress + optionalHeader.CertificateTable.Size); + var debugTable = ParseDebugTable(data, endOffset, executable.SectionTable); + if (debugTable == null) + return null; + + // Set the debug table + executable.DebugTable = debugTable; + } + + #endregion + #region Export Table // Should also be in the '.edata' section @@ -1834,6 +1920,44 @@ namespace BurnOutSharp.Builder return delayLoadDirectoryTable; } + /// + /// Parse a Stream into a debug table + /// + /// Stream to parse + /// 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(Stream data, int endOffset, SectionHeader[] sections) + { + // TODO: Use marshalling here instead of building + var debugTable = new DebugTable(); + + var debugDirectoryTable = new List(); + + while (data.Position < endOffset) + { + var debugDirectoryEntry = new DebugDirectoryEntry(); + + debugDirectoryEntry.Characteristics = data.ReadUInt32(); + debugDirectoryEntry.TimeDateStamp = data.ReadUInt32(); + debugDirectoryEntry.MajorVersion = data.ReadUInt16(); + debugDirectoryEntry.MinorVersion = data.ReadUInt16(); + debugDirectoryEntry.DebugType = (DebugType)data.ReadUInt32(); + debugDirectoryEntry.SizeOfData = data.ReadUInt32(); + debugDirectoryEntry.AddressOfRawData = data.ReadUInt32(); + debugDirectoryEntry.PointerToRawData = data.ReadUInt32(); + + 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 Stream into a export table /// diff --git a/BurnOutSharp.Models/PortableExecutable/DebugTable.cs b/BurnOutSharp.Models/PortableExecutable/DebugTable.cs new file mode 100644 index 00000000..04468cee --- /dev/null +++ b/BurnOutSharp.Models/PortableExecutable/DebugTable.cs @@ -0,0 +1,38 @@ +namespace BurnOutSharp.Models.PortableExecutable +{ + /// + /// The .debug section is used in object files to contain compiler-generated debug + /// information and in image files to contain all of the debug information that is + /// generated. This section describes the packaging of debug information in object + /// and image files. + /// + /// The next section describes the format of the debug directory, which can be + /// anywhere in the image. Subsequent sections describe the "groups" in object + /// files that contain debug information. + /// + /// The default for the linker is that debug information is not mapped into the + /// address space of the image. A .debug section exists only when debug information + /// is mapped in the address space. + /// + /// + public class DebugTable + { + /// + /// Image files contain an optional debug directory that indicates what form + /// of debug information is present and where it is. This directory consists + /// of an array of debug directory entries whose location and size are + /// indicated in the image optional header. + /// + /// The debug directory can be in a discardable .debug section (if one exists), + /// or it can be included in any other section in the image file, or not be + /// in a section at all. + /// + /// Each debug directory entry identifies the location and size of a block of + /// debug information. The specified RVA can be zero if the debug information + /// is not covered by a section header (that is, it resides in the image + /// file and is not mapped into the run-time address space). If it is mapped, + /// the RVA is its address. + /// + public DebugDirectoryEntry[] DebugDirectoryTable; + } +} diff --git a/BurnOutSharp.Models/PortableExecutable/Executable.cs b/BurnOutSharp.Models/PortableExecutable/Executable.cs index c1e4ea54..7c9d199d 100644 --- a/BurnOutSharp.Models/PortableExecutable/Executable.cs +++ b/BurnOutSharp.Models/PortableExecutable/Executable.cs @@ -68,6 +68,11 @@ namespace BurnOutSharp.Models.PortableExecutable // the object file contains managed code. The format of the metadata is not // documented, but can be handed to the CLR interfaces for handling metadata. + /// + /// Debug table (.debug*) + /// + public DebugTable DebugTable { get; set; } + /// /// Export table (.edata) /// @@ -101,9 +106,6 @@ namespace BurnOutSharp.Models.PortableExecutable // - Delay Import Name Table // - Delay Bound Import Address Table // - Delay Unload Import Address Table - // - The .debug Section - // - [Debug Directory Entry] - // - .debug$F (Object Only) / IMAGE_DEBUG_TYPE_FPO // - The .drectve Section (Object Only) // - The .pdata Section [Multiple formats per entry] // - TLS Callback Functions