diff --git a/BinaryObjectScanner.Printing/BinaryObjectScanner.Printing.csproj b/BinaryObjectScanner.Printing/BinaryObjectScanner.Printing.csproj
index 7373f86d..327828d7 100644
--- a/BinaryObjectScanner.Printing/BinaryObjectScanner.Printing.csproj
+++ b/BinaryObjectScanner.Printing/BinaryObjectScanner.Printing.csproj
@@ -24,6 +24,8 @@
+
+
diff --git a/BinaryObjectScanner.Printing/PortableExecutable.cs b/BinaryObjectScanner.Printing/PortableExecutable.cs
new file mode 100644
index 00000000..15e6ca4f
--- /dev/null
+++ b/BinaryObjectScanner.Printing/PortableExecutable.cs
@@ -0,0 +1,2038 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using SabreTools.ASN1;
+using SabreTools.IO;
+using SabreTools.Models.PortableExecutable;
+using static SabreTools.Serialization.Extensions;
+
+namespace BinaryObjectScanner.Printing
+{
+ public static class PortableExecutable
+ {
+ public static void Print(StringBuilder builder, Executable executable)
+ {
+ builder.AppendLine("Portable Executable Information:");
+ builder.AppendLine("-------------------------");
+ builder.AppendLine();
+
+ // Stub
+ Print(builder, executable.Stub?.Header);
+
+ // Header
+ Print(builder, executable.Signature, executable.COFFFileHeader);
+ Print(builder, executable.OptionalHeader, executable.SectionTable);
+
+ // Tables
+ Print(builder, executable.SectionTable);
+ Print(builder, executable.COFFSymbolTable);
+ Print(builder, executable.COFFStringTable);
+ Print(builder, executable.AttributeCertificateTable);
+ Print(builder, executable.DelayLoadDirectoryTable);
+
+ // Named Sections
+ Print(builder, executable.BaseRelocationTable, executable.SectionTable);
+ Print(builder, executable.DebugTable);
+ Print(builder, executable.ExportTable);
+ Print(builder, executable.ImportTable, executable.SectionTable);
+ Print(builder, executable.ResourceDirectoryTable);
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader header)
+#else
+ private static void Print(StringBuilder builder, SabreTools.Models.MSDOS.ExecutableHeader? header)
+#endif
+ {
+ builder.AppendLine(" MS-DOS Stub Header Information:");
+ builder.AppendLine(" -------------------------");
+ if (header == null)
+ {
+ builder.AppendLine(" No MS-DOS stub header");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(header.Magic, " Magic number");
+ builder.AppendLine(header.LastPageBytes, " Last page bytes");
+ builder.AppendLine(header.Pages, " Pages");
+ builder.AppendLine(header.RelocationItems, " Relocation items");
+ builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size");
+ builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs");
+ builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs");
+ builder.AppendLine(header.InitialSSValue, " Initial SS value");
+ builder.AppendLine(header.InitialSPValue, " Initial SP value");
+ builder.AppendLine(header.Checksum, " Checksum");
+ builder.AppendLine(header.InitialIPValue, " Initial IP value");
+ builder.AppendLine(header.InitialCSValue, " Initial CS value");
+ builder.AppendLine(header.RelocationTableAddr, " Relocation table address");
+ builder.AppendLine(header.OverlayNumber, " Overlay number");
+ builder.AppendLine();
+
+ builder.AppendLine(" MS-DOS Stub Extended Header Information:");
+ builder.AppendLine(" -------------------------");
+ builder.AppendLine(header.Reserved1, " Reserved words");
+ builder.AppendLine(header.OEMIdentifier, " OEM identifier");
+ builder.AppendLine(header.OEMInformation, " OEM information");
+ builder.AppendLine(header.Reserved2, " Reserved words");
+ builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address");
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, string signature, COFFFileHeader header)
+#else
+ private static void Print(StringBuilder builder, string? signature, COFFFileHeader? header)
+#endif
+ {
+ builder.AppendLine(" COFF File Header Information:");
+ builder.AppendLine(" -------------------------");
+ if (header == null)
+ {
+ builder.AppendLine(" No COFF file header");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(signature, " Signature");
+ builder.AppendLine($" Machine: {header.Machine} (0x{header.Machine:X})");
+ builder.AppendLine(header.NumberOfSections, " Number of sections");
+ builder.AppendLine(header.TimeDateStamp, " Time/Date stamp");
+ builder.AppendLine(header.PointerToSymbolTable, " Pointer to symbol table");
+ builder.AppendLine(header.NumberOfSymbols, " Number of symbols");
+ builder.AppendLine(header.SizeOfOptionalHeader, " Size of optional header");
+ builder.AppendLine($" Characteristics: {header.Characteristics} (0x{header.Characteristics:X})");
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, OptionalHeader header, SectionHeader[] table)
+#else
+ private static void Print(StringBuilder builder, OptionalHeader? header, SectionHeader?[]? table)
+#endif
+ {
+ builder.AppendLine(" Optional Header Information:");
+ builder.AppendLine(" -------------------------");
+ if (header == null)
+ {
+ builder.AppendLine(" No optional header");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine($" Magic: {header.Magic} (0x{header.Magic:X})");
+ builder.AppendLine(header.MajorLinkerVersion, " Major linker version");
+ builder.AppendLine(header.MinorLinkerVersion, " Minor linker version");
+ builder.AppendLine(header.SizeOfCode, " Size of code section");
+ builder.AppendLine(header.SizeOfInitializedData, " Size of initialized data");
+ builder.AppendLine(header.SizeOfUninitializedData, " Size of uninitialized data");
+ builder.AppendLine(header.AddressOfEntryPoint, " Address of entry point");
+ builder.AppendLine(header.BaseOfCode, " Base of code");
+ if (header.Magic == OptionalHeaderMagicNumber.PE32)
+ builder.AppendLine(header.BaseOfData, " Base of data");
+
+ if (header.Magic == OptionalHeaderMagicNumber.PE32)
+ builder.AppendLine(header.ImageBase_PE32, " Image base");
+ else
+ builder.AppendLine(header.ImageBase_PE32Plus, " Image base");
+ builder.AppendLine(header.SectionAlignment, " Section alignment");
+ builder.AppendLine(header.FileAlignment, " File alignment");
+ builder.AppendLine(header.MajorOperatingSystemVersion, " Major operating system version");
+ builder.AppendLine(header.MinorOperatingSystemVersion, " Minor operating system version");
+ builder.AppendLine(header.MajorImageVersion, " Major image version");
+ builder.AppendLine(header.MinorImageVersion, " Minor image version");
+ builder.AppendLine(header.MajorSubsystemVersion, " Major subsystem version");
+ builder.AppendLine(header.MinorSubsystemVersion, " Minor subsystem version");
+ builder.AppendLine(header.Win32VersionValue, " Win32 version value");
+ builder.AppendLine(header.SizeOfImage, " Size of image");
+ builder.AppendLine(header.SizeOfHeaders, " Size of headers");
+ builder.AppendLine(header.CheckSum, " Checksum");
+ builder.AppendLine($" Subsystem: {header.Subsystem} (0x{header.Subsystem:X})");
+ builder.AppendLine($" DLL characteristics: {header.DllCharacteristics} (0x{header.DllCharacteristics:X})");
+ if (header.Magic == OptionalHeaderMagicNumber.PE32)
+ {
+ builder.AppendLine(header.SizeOfStackReserve_PE32, " Size of stack reserve");
+ builder.AppendLine(header.SizeOfStackCommit_PE32, " Size of stack commit");
+ builder.AppendLine(header.SizeOfHeapReserve_PE32, " Size of heap reserve");
+ builder.AppendLine(header.SizeOfHeapCommit_PE32, " Size of heap commit");
+ }
+ else
+ {
+ builder.AppendLine(header.SizeOfStackReserve_PE32Plus, " Size of stack reserve");
+ builder.AppendLine(header.SizeOfStackCommit_PE32Plus, " Size of stack commit");
+ builder.AppendLine(header.SizeOfHeapReserve_PE32Plus, " Size of heap reserve");
+ builder.AppendLine(header.SizeOfHeapCommit_PE32Plus, " Size of heap commit");
+ }
+ builder.AppendLine(header.LoaderFlags, " Loader flags");
+ builder.AppendLine(header.NumberOfRvaAndSizes, " Number of data-directory entries");
+
+ if (header.ExportTable != null)
+ {
+ builder.AppendLine(" Export Table (1)");
+ builder.AppendLine(header.ExportTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ExportTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ExportTable.Size, " Size");
+ }
+ if (header.ImportTable != null)
+ {
+ builder.AppendLine(" Import Table (2)");
+ builder.AppendLine(header.ImportTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ImportTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ImportTable.Size, " Size");
+ }
+ if (header.ResourceTable != null)
+ {
+ builder.AppendLine(" Resource Table (3)");
+ builder.AppendLine(header.ResourceTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ResourceTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ResourceTable.Size, " Size");
+ }
+ if (header.ExceptionTable != null)
+ {
+ builder.AppendLine(" Exception Table (4)");
+ builder.AppendLine(header.ExceptionTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ExceptionTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ExceptionTable.Size, " Size");
+ }
+ if (header.CertificateTable != null)
+ {
+ builder.AppendLine(" Certificate Table (5)");
+ builder.AppendLine(header.CertificateTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.CertificateTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.CertificateTable.Size, " Size");
+ }
+ if (header.BaseRelocationTable != null)
+ {
+ builder.AppendLine(" Base Relocation Table (6)");
+ builder.AppendLine(header.BaseRelocationTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.BaseRelocationTable.Size, " Size");
+ }
+ if (header.Debug != null)
+ {
+ builder.AppendLine(" Debug Table (7)");
+ builder.AppendLine(header.Debug.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.Debug.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.Debug.Size, " Size");
+ }
+ if (header.NumberOfRvaAndSizes >= 8)
+ {
+ builder.AppendLine(" Architecture Table (8)");
+ builder.AppendLine(" Virtual address: 0 (0x00000000)");
+ builder.AppendLine(" Physical address: 0 (0x00000000)");
+ builder.AppendLine(" Size: 0 (0x00000000)");
+ }
+ if (header.GlobalPtr != null)
+ {
+ builder.AppendLine(" Global Pointer Register (9)");
+ builder.AppendLine(header.GlobalPtr.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.GlobalPtr.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.GlobalPtr.Size, " Size");
+ }
+ if (header.ThreadLocalStorageTable != null)
+ {
+ builder.AppendLine(" Thread Local Storage (TLS) Table (10)");
+ builder.AppendLine(header.ThreadLocalStorageTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ThreadLocalStorageTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ThreadLocalStorageTable.Size, " Size");
+ }
+ if (header.LoadConfigTable != null)
+ {
+ builder.AppendLine(" Load Config Table (11)");
+ builder.AppendLine(header.LoadConfigTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.LoadConfigTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.LoadConfigTable.Size, " Size");
+ }
+ if (header.BoundImport != null)
+ {
+ builder.AppendLine(" Bound Import Table (12)");
+ builder.AppendLine(header.BoundImport.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.BoundImport.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.BoundImport.Size, " Size");
+ }
+ if (header.ImportAddressTable != null)
+ {
+ builder.AppendLine(" Import Address Table (13)");
+ builder.AppendLine(header.ImportAddressTable.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.ImportAddressTable.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.ImportAddressTable.Size, " Size");
+ }
+ if (header.DelayImportDescriptor != null)
+ {
+ builder.AppendLine(" Delay Import Descriptior (14)");
+ builder.AppendLine(header.DelayImportDescriptor.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.DelayImportDescriptor.Size, " Size");
+ }
+ if (header.CLRRuntimeHeader != null)
+ {
+ builder.AppendLine(" CLR Runtime Header (15)");
+ builder.AppendLine(header.CLRRuntimeHeader.VirtualAddress, " Virtual address");
+ builder.AppendLine(header.CLRRuntimeHeader.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(header.CLRRuntimeHeader.Size, " Size");
+ }
+ if (header.NumberOfRvaAndSizes >= 16)
+ {
+ builder.AppendLine(" Reserved (16)");
+ builder.AppendLine(" Virtual address: 0 (0x00000000)");
+ builder.AppendLine(" Physical address: 0 (0x00000000)");
+ builder.AppendLine(" Size: 0 (0x00000000)");
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, SectionHeader[] table)
+#else
+ private static void Print(StringBuilder builder, SectionHeader?[]? table)
+#endif
+ {
+ builder.AppendLine(" Section Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table == null || table.Length == 0)
+ {
+ builder.AppendLine(" No section table items");
+ builder.AppendLine();
+ return;
+ }
+
+#if NET48
+ for (int i = 0; i < table.Length; i++)
+#else
+ for (int i = 0; i < table!.Length; i++)
+#endif
+ {
+ var entry = table[i];
+ builder.AppendLine($" Section Table Entry {i}");
+ if (entry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(entry.Name, " Name");
+ builder.AppendLine(entry.VirtualSize, " Virtual size");
+ builder.AppendLine(entry.VirtualAddress, " Virtual address");
+ builder.AppendLine(entry.VirtualAddress.ConvertVirtualAddress(table ?? Array.Empty()), " Physical address");
+ builder.AppendLine(entry.SizeOfRawData, " Size of raw data");
+ builder.AppendLine(entry.PointerToRawData, " Pointer to raw data");
+ builder.AppendLine(entry.PointerToRelocations, " Pointer to relocations");
+ builder.AppendLine(entry.PointerToLinenumbers, " Pointer to linenumbers");
+ builder.AppendLine(entry.NumberOfRelocations, " Number of relocations");
+ builder.AppendLine(entry.NumberOfLinenumbers, " Number of linenumbers");
+ builder.AppendLine($" Characteristics: {entry.Characteristics} (0x{entry.Characteristics:X})");
+ // TODO: Add COFFRelocations
+ // TODO: Add COFFLineNumbers
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, COFFSymbolTableEntry[] symbolTable)
+#else
+ private static void Print(StringBuilder builder, COFFSymbolTableEntry?[]? symbolTable)
+#endif
+ {
+ builder.AppendLine(" COFF Symbol Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (symbolTable == null || symbolTable.Length == 0)
+ {
+ builder.AppendLine(" No COFF symbol table items");
+ builder.AppendLine();
+ return;
+ }
+
+ int auxSymbolsRemaining = 0;
+ int currentSymbolType = 0;
+
+ for (int i = 0; i < symbolTable.Length; i++)
+ {
+ var entry = symbolTable[i];
+ builder.AppendLine($" COFF Symbol Table Entry {i} (Subtype {currentSymbolType})");
+ if (entry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ if (currentSymbolType == 0)
+ {
+ if (entry.ShortName != null)
+ {
+ builder.AppendLine(entry.ShortName, " Short name");
+ }
+ else
+ {
+ builder.AppendLine(entry.Zeroes, " Zeroes");
+ builder.AppendLine(entry.Offset, " Offset");
+ }
+ builder.AppendLine(entry.Value, " Value");
+ builder.AppendLine(entry.SectionNumber, " Section number");
+ builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})");
+ builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})");
+ builder.AppendLine(entry.NumberOfAuxSymbols, " Number of aux symbols");
+
+ 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;
+ }
+ }
+ else if (currentSymbolType == 1)
+ {
+ builder.AppendLine(entry.AuxFormat1TagIndex, " Tag index");
+ builder.AppendLine(entry.AuxFormat1TotalSize, " Total size");
+ builder.AppendLine(entry.AuxFormat1PointerToLinenumber, " Pointer to linenumber");
+ builder.AppendLine(entry.AuxFormat1PointerToNextFunction, " Pointer to next function");
+ builder.AppendLine(entry.AuxFormat1Unused, " Unused");
+ auxSymbolsRemaining--;
+ }
+ else if (currentSymbolType == 2)
+ {
+ builder.AppendLine(entry.AuxFormat2Unused1, " Unused");
+ builder.AppendLine(entry.AuxFormat2Linenumber, " Linenumber");
+ builder.AppendLine(entry.AuxFormat2Unused2, " Unused");
+ builder.AppendLine(entry.AuxFormat2PointerToNextFunction, " Pointer to next function");
+ builder.AppendLine(entry.AuxFormat2Unused3, " Unused");
+ auxSymbolsRemaining--;
+ }
+ else if (currentSymbolType == 3)
+ {
+ builder.AppendLine(entry.AuxFormat3TagIndex, " Tag index");
+ builder.AppendLine(entry.AuxFormat3Characteristics, " Characteristics");
+ builder.AppendLine(entry.AuxFormat3Unused, " Unused");
+ auxSymbolsRemaining--;
+ }
+ else if (currentSymbolType == 4)
+ {
+ builder.AppendLine(entry.AuxFormat4FileName, " File name");
+ auxSymbolsRemaining--;
+ }
+ else if (currentSymbolType == 5)
+ {
+ builder.AppendLine(entry.AuxFormat5Length, " Length");
+ builder.AppendLine(entry.AuxFormat5NumberOfRelocations, " Number of relocations");
+ builder.AppendLine(entry.AuxFormat5NumberOfLinenumbers, " Number of linenumbers");
+ builder.AppendLine(entry.AuxFormat5CheckSum, " Checksum");
+ builder.AppendLine(entry.AuxFormat5Number, " Number");
+ builder.AppendLine(entry.AuxFormat5Selection, " Selection");
+ builder.AppendLine(entry.AuxFormat5Unused, " Unused");
+ auxSymbolsRemaining--;
+ }
+ else if (currentSymbolType == 6)
+ {
+ builder.AppendLine(entry.AuxFormat6AuxType, " Aux type");
+ builder.AppendLine(entry.AuxFormat6Reserved1, " Reserved");
+ builder.AppendLine(entry.AuxFormat6SymbolTableIndex, " Symbol table index");
+ builder.AppendLine(entry.AuxFormat6Reserved2, " Reserved");
+ auxSymbolsRemaining--;
+ }
+
+ // If we hit the last aux symbol, go back to normal format
+ if (auxSymbolsRemaining == 0)
+ currentSymbolType = 0;
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, COFFStringTable stringTable)
+#else
+ private static void Print(StringBuilder builder, COFFStringTable? stringTable)
+#endif
+ {
+ builder.AppendLine(" COFF String Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (stringTable?.Strings == null || stringTable.Strings.Length == 0)
+ {
+ builder.AppendLine(" No COFF string table items");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(stringTable.TotalSize, " Total size");
+ for (int i = 0; i < stringTable.Strings.Length; i++)
+ {
+#if NET48
+ string entry = stringTable.Strings[i];
+#else
+ string? entry = stringTable.Strings[i];
+#endif
+ builder.AppendLine($" COFF String Table Entry {i})");
+ builder.AppendLine(entry, " Value");
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, AttributeCertificateTableEntry[] entries)
+#else
+ private static void Print(StringBuilder builder, AttributeCertificateTableEntry?[]? entries)
+#endif
+ {
+ builder.AppendLine(" Attribute Certificate Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (entries == null || entries.Length == 0)
+ {
+ builder.AppendLine(" No attribute certificate table items");
+ builder.AppendLine();
+ return;
+ }
+
+ for (int i = 0; i < entries.Length; i++)
+ {
+ var entry = entries[i];
+ builder.AppendLine($" Attribute Certificate Table Entry {i}");
+ if (entry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(entry.Length, " Length");
+ builder.AppendLine($" Revision: {entry.Revision} (0x{entry.Revision:X})");
+ builder.AppendLine($" Certificate type: {entry.CertificateType} (0x{entry.CertificateType:X})");
+ builder.AppendLine();
+ if (entry.CertificateType == WindowsCertificateType.WIN_CERT_TYPE_PKCS_SIGNED_DATA)
+ {
+ builder.AppendLine(" Certificate Data [Formatted]");
+ builder.AppendLine(" -------------------------");
+ if (entry.Certificate == null)
+ {
+ builder.AppendLine(" INVALID DATA FOUND");
+ }
+ else
+ {
+ var topLevelValues = AbstractSyntaxNotationOne.Parse(entry.Certificate, 0);
+ if (topLevelValues == null)
+ {
+ builder.AppendLine(" INVALID DATA FOUND");
+ builder.AppendLine(entry.Certificate, " Raw data");
+ }
+ else
+ {
+ foreach (TypeLengthValue tlv in topLevelValues)
+ {
+ string tlvString = tlv.Format(paddingLevel: 4);
+ builder.AppendLine(tlvString);
+ }
+ }
+ }
+ }
+ else
+ {
+ builder.AppendLine(" Certificate Data [Binary]");
+ builder.AppendLine(" -------------------------");
+ try
+ {
+ builder.AppendLine(entry.Certificate, " Raw data");
+ }
+ catch
+ {
+ builder.AppendLine(" [DATA TOO LARGE TO FORMAT]");
+ }
+ }
+
+ builder.AppendLine();
+ }
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, DelayLoadDirectoryTable table)
+#else
+ private static void Print(StringBuilder builder, DelayLoadDirectoryTable? table)
+#endif
+ {
+ builder.AppendLine(" Delay-Load Directory Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table == null)
+ {
+ builder.AppendLine(" No delay-load directory table items");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(table.Attributes, " Attributes");
+ builder.AppendLine(table.Name, " Name RVA");
+ builder.AppendLine(table.ModuleHandle, " Module handle");
+ builder.AppendLine(table.DelayImportAddressTable, " Delay import address table RVA");
+ builder.AppendLine(table.DelayImportNameTable, " Delay import name table RVA");
+ builder.AppendLine(table.BoundDelayImportTable, " Bound delay import table RVA");
+ builder.AppendLine(table.UnloadDelayImportTable, " Unload delay import table RVA");
+ builder.AppendLine(table.TimeStamp, " Timestamp");
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, BaseRelocationBlock[] entries, SectionHeader[] table)
+#else
+ private static void Print(StringBuilder builder, BaseRelocationBlock?[]? entries, SectionHeader?[]? table)
+#endif
+ {
+ builder.AppendLine(" Base Relocation Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (entries == null || entries.Length == 0)
+ {
+ builder.AppendLine(" No base relocation table items");
+ builder.AppendLine();
+ return;
+ }
+
+ for (int i = 0; i < entries.Length; i++)
+ {
+ var baseRelocationTableEntry = entries[i];
+ builder.AppendLine($" Base Relocation Table Entry {i}");
+ if (baseRelocationTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(baseRelocationTableEntry.PageRVA, " Page RVA");
+ builder.AppendLine(baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(table ?? Array.Empty()), " Page physical address");
+ builder.AppendLine(baseRelocationTableEntry.BlockSize, " Block size");
+
+ builder.AppendLine($" Base Relocation Table {i} Type and Offset Information:");
+ builder.AppendLine(" -------------------------");
+ if (baseRelocationTableEntry.TypeOffsetFieldEntries == null || baseRelocationTableEntry.TypeOffsetFieldEntries.Length == 0)
+ {
+ builder.AppendLine(" No base relocation table type and offset entries");
+ continue;
+ }
+
+ for (int j = 0; j < baseRelocationTableEntry.TypeOffsetFieldEntries.Length; j++)
+ {
+ var typeOffsetFieldEntry = baseRelocationTableEntry.TypeOffsetFieldEntries[j];
+ builder.AppendLine($" Type and Offset Entry {j}");
+#if NET6_0_OR_GREATER
+ if (typeOffsetFieldEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+#endif
+ builder.AppendLine($" Type: {typeOffsetFieldEntry.BaseRelocationType} (0x{typeOffsetFieldEntry.BaseRelocationType:X})");
+ builder.AppendLine(typeOffsetFieldEntry.Offset, " Offset");
+ }
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, DebugTable table)
+#else
+ private static void Print(StringBuilder builder, DebugTable? table)
+#endif
+ {
+ builder.AppendLine(" Debug Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table?.DebugDirectoryTable == null || table.DebugDirectoryTable.Length == 0)
+ {
+ builder.AppendLine(" No debug table items");
+ builder.AppendLine();
+ return;
+ }
+
+ // TODO: If more sections added, model this after the Export Table
+ for (int i = 0; i < table.DebugDirectoryTable.Length; i++)
+ {
+ var entry = table.DebugDirectoryTable[i];
+ builder.AppendLine($" Debug Directory Table Entry {i}");
+ if (entry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(entry.Characteristics, " Characteristics");
+ builder.AppendLine(entry.TimeDateStamp, " Time/Date stamp");
+ builder.AppendLine(entry.MajorVersion, " Major version");
+ builder.AppendLine(entry.MinorVersion, " Minor version");
+ builder.AppendLine($" Debug type: {entry.DebugType} (0x{entry.DebugType:X})");
+ builder.AppendLine(entry.SizeOfData, " Size of data");
+ builder.AppendLine(entry.AddressOfRawData, " Address of raw data");
+ builder.AppendLine(entry.PointerToRawData, " Pointer to raw data");
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, ExportTable table)
+#else
+ private static void Print(StringBuilder builder, ExportTable? table)
+#endif
+ {
+ builder.AppendLine(" Export Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table == null)
+ {
+ builder.AppendLine(" No export table");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine(" Export Directory Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ExportDirectoryTable == null)
+ {
+ builder.AppendLine(" No export directory table");
+ }
+ else
+ {
+ builder.AppendLine(table.ExportDirectoryTable.ExportFlags, " Export flags");
+ builder.AppendLine(table.ExportDirectoryTable.TimeDateStamp, " Time/Date stamp");
+ builder.AppendLine(table.ExportDirectoryTable.MajorVersion, " Major version");
+ builder.AppendLine(table.ExportDirectoryTable.MinorVersion, " Minor version");
+ builder.AppendLine(table.ExportDirectoryTable.NameRVA, " Name RVA");
+ builder.AppendLine(table.ExportDirectoryTable.Name, " Name");
+ builder.AppendLine(table.ExportDirectoryTable.OrdinalBase, " Ordinal base");
+ builder.AppendLine(table.ExportDirectoryTable.AddressTableEntries, " Address table entries");
+ builder.AppendLine(table.ExportDirectoryTable.NumberOfNamePointers, " Number of name pointers");
+ builder.AppendLine(table.ExportDirectoryTable.ExportAddressTableRVA, " Export address table RVA");
+ builder.AppendLine(table.ExportDirectoryTable.NamePointerRVA, " Name pointer table RVA");
+ builder.AppendLine(table.ExportDirectoryTable.OrdinalTableRVA, " Ordinal table RVA");
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Export Address Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ExportAddressTable == null || table.ExportAddressTable.Length == 0)
+ {
+ builder.AppendLine(" No export address table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.ExportAddressTable.Length; i++)
+ {
+ var exportAddressTableEntry = table.ExportAddressTable[i];
+ builder.AppendLine($" Export Address Table Entry {i}");
+ if (exportAddressTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(exportAddressTableEntry.ExportRVA, " Export RVA / Forwarder RVA");
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Name Pointer Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.NamePointerTable?.Pointers == null || table.NamePointerTable.Pointers.Length == 0)
+ {
+ builder.AppendLine(" No name pointer table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.NamePointerTable.Pointers.Length; i++)
+ {
+ var namePointerTableEntry = table.NamePointerTable.Pointers[i];
+ builder.AppendLine($" Name Pointer Table Entry {i}");
+ builder.AppendLine(namePointerTableEntry, " Pointer");
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Ordinal Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.OrdinalTable?.Indexes == null || table.OrdinalTable.Indexes.Length == 0)
+ {
+ builder.AppendLine(" No ordinal table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.OrdinalTable.Indexes.Length; i++)
+ {
+ var ordinalTableEntry = table.OrdinalTable.Indexes[i];
+ builder.AppendLine($" Ordinal Table Entry {i}");
+ builder.AppendLine(ordinalTableEntry, " Index");
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Export Name Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ExportNameTable?.Strings == null || table.ExportNameTable.Strings.Length == 0)
+ {
+ builder.AppendLine(" No export name table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.ExportNameTable.Strings.Length; i++)
+ {
+ var exportNameTableEntry = table.ExportNameTable.Strings[i];
+ builder.AppendLine($" Export Name Table Entry {i}");
+ builder.AppendLine(exportNameTableEntry, " String");
+ }
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, ImportTable table, SectionHeader[] sectionTable)
+#else
+ private static void Print(StringBuilder builder, ImportTable? table, SectionHeader?[]? sectionTable)
+#endif
+ {
+ builder.AppendLine(" Import Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table == null)
+ {
+ builder.AppendLine(" No import table");
+ builder.AppendLine();
+ return;
+ }
+
+ builder.AppendLine();
+ builder.AppendLine(" Import Directory Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ImportDirectoryTable == null || table.ImportDirectoryTable.Length == 0)
+ {
+ builder.AppendLine(" No import directory table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.ImportDirectoryTable.Length; i++)
+ {
+ var importDirectoryTableEntry = table.ImportDirectoryTable[i];
+ builder.AppendLine($" Import Directory Table Entry {i}");
+ if (importDirectoryTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(importDirectoryTableEntry.ImportLookupTableRVA, " Import lookup table RVA");
+ builder.AppendLine(importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Import lookup table Physical Address");
+ builder.AppendLine(importDirectoryTableEntry.TimeDateStamp, " Time/Date stamp");
+ builder.AppendLine(importDirectoryTableEntry.ForwarderChain, " Forwarder chain");
+ builder.AppendLine(importDirectoryTableEntry.NameRVA, " Name RVA");
+ builder.AppendLine(importDirectoryTableEntry.Name, " Name");
+ builder.AppendLine(importDirectoryTableEntry.ImportAddressTableRVA, " Import address table RVA");
+ builder.AppendLine(importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Import address table Physical Address");
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Import Lookup Tables Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ImportLookupTables == null || table.ImportLookupTables.Count == 0)
+ {
+ builder.AppendLine(" No import lookup tables");
+ }
+ else
+ {
+ foreach (var kvp in table.ImportLookupTables)
+ {
+ int index = kvp.Key;
+ var importLookupTable = kvp.Value;
+
+ builder.AppendLine();
+ builder.AppendLine($" Import Lookup Table {index} Information:");
+ builder.AppendLine(" -------------------------");
+ if (importLookupTable == null || importLookupTable.Length == 0)
+ {
+ builder.AppendLine(" No import lookup table items");
+ continue;
+ }
+
+ for (int i = 0; i < importLookupTable.Length; i++)
+ {
+ var importLookupTableEntry = importLookupTable[i];
+ builder.AppendLine($" Import Lookup Table {index} Entry {i}");
+ if (importLookupTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(importLookupTableEntry.OrdinalNameFlag, " Ordinal/Name flag");
+ if (importLookupTableEntry.OrdinalNameFlag)
+ {
+ builder.AppendLine(importLookupTableEntry.OrdinalNumber, " Ordinal number");
+ }
+ else
+ {
+ builder.AppendLine(importLookupTableEntry.HintNameTableRVA, " Hint/Name table RVA");
+ builder.AppendLine(importLookupTableEntry.HintNameTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Hint/Name table Physical Address");
+ }
+ }
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Import Address Tables Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.ImportAddressTables == null || table.ImportAddressTables.Count == 0)
+ {
+ builder.AppendLine(" No import address tables");
+ }
+ else
+ {
+ foreach (var kvp in table.ImportAddressTables)
+ {
+ int index = kvp.Key;
+ var importAddressTable = kvp.Value;
+
+ builder.AppendLine();
+ builder.AppendLine($" Import Address Table {index} Information:");
+ builder.AppendLine(" -------------------------");
+ if (importAddressTable == null || importAddressTable.Length == 0)
+ {
+ builder.AppendLine(" No import address table items");
+ continue;
+ }
+
+ for (int i = 0; i < importAddressTable.Length; i++)
+ {
+ var importAddressTableEntry = importAddressTable[i];
+ builder.AppendLine($" Import Address Table {index} Entry {i}");
+ if (importAddressTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(importAddressTableEntry.OrdinalNameFlag, " Ordinal/Name flag");
+ if (importAddressTableEntry.OrdinalNameFlag)
+ {
+ builder.AppendLine(importAddressTableEntry.OrdinalNumber, " Ordinal number");
+ }
+ else
+ {
+ builder.AppendLine(importAddressTableEntry.HintNameTableRVA, " Hint/Name table RVA");
+ builder.AppendLine(importAddressTableEntry.HintNameTableRVA.ConvertVirtualAddress(sectionTable ?? Array.Empty()), " Hint/Name table Physical Address");
+ }
+ }
+ }
+ }
+ builder.AppendLine();
+
+ builder.AppendLine(" Hint/Name Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table.HintNameTable == null || table.HintNameTable.Length == 0)
+ {
+ builder.AppendLine(" No hint/name table items");
+ }
+ else
+ {
+ for (int i = 0; i < table.HintNameTable.Length; i++)
+ {
+ var hintNameTableEntry = table.HintNameTable[i];
+ builder.AppendLine($" Hint/Name Table Entry {i}");
+ if (hintNameTableEntry == null)
+ {
+ builder.AppendLine(" [NULL]");
+ continue;
+ }
+
+ builder.AppendLine(hintNameTableEntry.Hint, " Hint");
+ builder.AppendLine(hintNameTableEntry.Name, " Name");
+ }
+ }
+ builder.AppendLine();
+ }
+
+#if NET48
+ private static void Print(StringBuilder builder, ResourceDirectoryTable table)
+#else
+ private static void Print(StringBuilder builder, ResourceDirectoryTable? table)
+#endif
+ {
+ builder.AppendLine(" Resource Directory Table Information:");
+ builder.AppendLine(" -------------------------");
+ if (table == null)
+ {
+ builder.AppendLine(" No resource directory table items");
+ builder.AppendLine();
+ return;
+ }
+
+ Print(table, level: 0, types: new List