using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using SabreTools.ASN1; using SabreTools.IO; using static SabreTools.Serialization.Extensions; namespace BinaryObjectScanner.Wrappers { public class PortableExecutable : WrapperBase { #region Descriptive Properties /// public override string DescriptionString => "Portable Executable (PE)"; #endregion #region Pass-Through Properties #region MS-DOS Stub #region Standard Fields /// #if NET48 public string Stub_Magic => _model.Stub.Header.Magic; #else public string? Stub_Magic => _model.Stub?.Header?.Magic; #endif /// #if NET48 public ushort Stub_LastPageBytes => _model.Stub.Header.LastPageBytes; #else public ushort? Stub_LastPageBytes => _model.Stub?.Header?.LastPageBytes; #endif /// #if NET48 public ushort Stub_Pages => _model.Stub.Header.Pages; #else public ushort? Stub_Pages => _model.Stub?.Header?.Pages; #endif /// #if NET48 public ushort Stub_RelocationItems => _model.Stub.Header.RelocationItems; #else public ushort? Stub_RelocationItems => _model.Stub?.Header?.RelocationItems; #endif /// #if NET48 public ushort Stub_HeaderParagraphSize => _model.Stub.Header.HeaderParagraphSize; #else public ushort? Stub_HeaderParagraphSize => _model.Stub?.Header?.HeaderParagraphSize; #endif /// #if NET48 public ushort Stub_MinimumExtraParagraphs => _model.Stub.Header.MinimumExtraParagraphs; #else public ushort? Stub_MinimumExtraParagraphs => _model.Stub?.Header?.MinimumExtraParagraphs; #endif /// #if NET48 public ushort Stub_MaximumExtraParagraphs => _model.Stub.Header.MaximumExtraParagraphs; #else public ushort? Stub_MaximumExtraParagraphs => _model.Stub?.Header?.MaximumExtraParagraphs; #endif /// #if NET48 public ushort Stub_InitialSSValue => _model.Stub.Header.InitialSSValue; #else public ushort? Stub_InitialSSValue => _model.Stub?.Header?.InitialSSValue; #endif /// #if NET48 public ushort Stub_InitialSPValue => _model.Stub.Header.InitialSPValue; #else public ushort? Stub_InitialSPValue => _model.Stub?.Header?.InitialSPValue; #endif /// #if NET48 public ushort Stub_Checksum => _model.Stub.Header.Checksum; #else public ushort? Stub_Checksum => _model.Stub?.Header?.Checksum; #endif /// #if NET48 public ushort Stub_InitialIPValue => _model.Stub.Header.InitialIPValue; #else public ushort? Stub_InitialIPValue => _model.Stub?.Header?.InitialIPValue; #endif /// #if NET48 public ushort Stub_InitialCSValue => _model.Stub.Header.InitialCSValue; #else public ushort? Stub_InitialCSValue => _model.Stub?.Header?.InitialCSValue; #endif /// #if NET48 public ushort Stub_RelocationTableAddr => _model.Stub.Header.RelocationTableAddr; #else public ushort? Stub_RelocationTableAddr => _model.Stub?.Header?.RelocationTableAddr; #endif /// #if NET48 public ushort Stub_OverlayNumber => _model.Stub.Header.OverlayNumber; #else public ushort? Stub_OverlayNumber => _model.Stub?.Header?.OverlayNumber; #endif #endregion #region PE Extensions /// #if NET48 public ushort[] Stub_Reserved1 => _model.Stub.Header.Reserved1; #else public ushort[]? Stub_Reserved1 => _model.Stub?.Header?.Reserved1; #endif /// #if NET48 public ushort Stub_OEMIdentifier => _model.Stub.Header.OEMIdentifier; #else public ushort? Stub_OEMIdentifier => _model.Stub?.Header?.OEMIdentifier; #endif /// #if NET48 public ushort Stub_OEMInformation => _model.Stub.Header.OEMInformation; #else public ushort? Stub_OEMInformation => _model.Stub?.Header?.OEMInformation; #endif /// #if NET48 public ushort[] Stub_Reserved2 => _model.Stub.Header.Reserved2; #else public ushort[]? Stub_Reserved2 => _model.Stub?.Header?.Reserved2; #endif /// #if NET48 public uint Stub_NewExeHeaderAddr => _model.Stub.Header.NewExeHeaderAddr; #else public uint? Stub_NewExeHeaderAddr => _model.Stub?.Header?.NewExeHeaderAddr; #endif #endregion #endregion /// #if NET48 public string Signature => _model.Signature; #else public string? Signature => _model.Signature; #endif #region COFF File Header /// #if NET48 public SabreTools.Models.PortableExecutable.MachineType Machine => _model.COFFFileHeader.Machine; #else public SabreTools.Models.PortableExecutable.MachineType? Machine => _model.COFFFileHeader?.Machine; #endif /// #if NET48 public ushort NumberOfSections => _model.COFFFileHeader.NumberOfSections; #else public ushort? NumberOfSections => _model.COFFFileHeader?.NumberOfSections; #endif /// #if NET48 public uint TimeDateStamp => _model.COFFFileHeader.TimeDateStamp; #else public uint? TimeDateStamp => _model.COFFFileHeader?.TimeDateStamp; #endif /// #if NET48 public uint PointerToSymbolTable => _model.COFFFileHeader.PointerToSymbolTable; #else public uint? PointerToSymbolTable => _model.COFFFileHeader?.PointerToSymbolTable; #endif /// #if NET48 public uint NumberOfSymbols => _model.COFFFileHeader.NumberOfSymbols; #else public uint? NumberOfSymbols => _model.COFFFileHeader?.NumberOfSymbols; #endif /// #if NET48 public uint SizeOfOptionalHeader => _model.COFFFileHeader.SizeOfOptionalHeader; #else public uint? SizeOfOptionalHeader => _model.COFFFileHeader?.SizeOfOptionalHeader; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.Characteristics Characteristics => _model.COFFFileHeader.Characteristics; #else public SabreTools.Models.PortableExecutable.Characteristics? Characteristics => _model.COFFFileHeader?.Characteristics; #endif #endregion #region Optional Header #region Standard Fields /// #if NET48 public SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber OH_Magic => _model.OptionalHeader.Magic; #else public SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber? OH_Magic => _model.OptionalHeader?.Magic; #endif /// #if NET48 public byte OH_MajorLinkerVersion => _model.OptionalHeader.MajorLinkerVersion; #else public byte? OH_MajorLinkerVersion => _model.OptionalHeader?.MajorLinkerVersion; #endif /// #if NET48 public byte OH_MinorLinkerVersion => _model.OptionalHeader.MinorLinkerVersion; #else public byte? OH_MinorLinkerVersion => _model.OptionalHeader?.MinorLinkerVersion; #endif /// #if NET48 public uint OH_SizeOfCode => _model.OptionalHeader.SizeOfCode; #else public uint? OH_SizeOfCode => _model.OptionalHeader?.SizeOfCode; #endif /// #if NET48 public uint OH_SizeOfInitializedData => _model.OptionalHeader.SizeOfInitializedData; #else public uint? OH_SizeOfInitializedData => _model.OptionalHeader?.SizeOfInitializedData; #endif /// #if NET48 public uint OH_SizeOfUninitializedData => _model.OptionalHeader.SizeOfUninitializedData; #else public uint? OH_SizeOfUninitializedData => _model.OptionalHeader?.SizeOfUninitializedData; #endif /// #if NET48 public uint OH_AddressOfEntryPoint => _model.OptionalHeader.AddressOfEntryPoint; #else public uint? OH_AddressOfEntryPoint => _model.OptionalHeader?.AddressOfEntryPoint; #endif /// #if NET48 public uint OH_BaseOfCode => _model.OptionalHeader.BaseOfCode; #else public uint? OH_BaseOfCode => _model.OptionalHeader?.BaseOfCode; #endif /// public uint? OH_BaseOfData => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? (uint?)_model.OptionalHeader.BaseOfData : null; #endregion #region Windows-Specific Fields /// public ulong OH_ImageBase => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? _model.OptionalHeader.ImageBase_PE32 : _model.OptionalHeader?.ImageBase_PE32Plus ?? 0; /// #if NET48 public uint OH_SectionAlignment => _model.OptionalHeader.SectionAlignment; #else public uint? OH_SectionAlignment => _model.OptionalHeader?.SectionAlignment; #endif /// #if NET48 public uint OH_FileAlignment => _model.OptionalHeader.FileAlignment; #else public uint? OH_FileAlignment => _model.OptionalHeader?.FileAlignment; #endif /// #if NET48 public ushort OH_MajorOperatingSystemVersion => _model.OptionalHeader.MajorOperatingSystemVersion; #else public ushort? OH_MajorOperatingSystemVersion => _model.OptionalHeader?.MajorOperatingSystemVersion; #endif /// #if NET48 public ushort OH_MinorOperatingSystemVersion => _model.OptionalHeader.MinorOperatingSystemVersion; #else public ushort? OH_MinorOperatingSystemVersion => _model.OptionalHeader?.MinorOperatingSystemVersion; #endif /// #if NET48 public ushort OH_MajorImageVersion => _model.OptionalHeader.MajorImageVersion; #else public ushort? OH_MajorImageVersion => _model.OptionalHeader?.MajorImageVersion; #endif /// #if NET48 public ushort OH_MinorImageVersion => _model.OptionalHeader.MinorImageVersion; #else public ushort? OH_MinorImageVersion => _model.OptionalHeader?.MinorImageVersion; #endif /// #if NET48 public ushort OH_MajorSubsystemVersion => _model.OptionalHeader.MajorSubsystemVersion; #else public ushort? OH_MajorSubsystemVersion => _model.OptionalHeader?.MajorSubsystemVersion; #endif /// #if NET48 public ushort OH_MinorSubsystemVersion => _model.OptionalHeader.MinorSubsystemVersion; #else public ushort? OH_MinorSubsystemVersion => _model.OptionalHeader?.MinorSubsystemVersion; #endif /// #if NET48 public uint OH_Win32VersionValue => _model.OptionalHeader.Win32VersionValue; #else public uint? OH_Win32VersionValue => _model.OptionalHeader?.Win32VersionValue; #endif /// #if NET48 public uint OH_SizeOfImage => _model.OptionalHeader.SizeOfImage; #else public uint? OH_SizeOfImage => _model.OptionalHeader?.SizeOfImage; #endif /// #if NET48 public uint OH_SizeOfHeaders => _model.OptionalHeader.SizeOfHeaders; #else public uint? OH_SizeOfHeaders => _model.OptionalHeader?.SizeOfHeaders; #endif /// #if NET48 public uint OH_CheckSum => _model.OptionalHeader.CheckSum; #else public uint? OH_CheckSum => _model.OptionalHeader?.CheckSum; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.WindowsSubsystem OH_Subsystem => _model.OptionalHeader.Subsystem; #else public SabreTools.Models.PortableExecutable.WindowsSubsystem? OH_Subsystem => _model.OptionalHeader?.Subsystem; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DllCharacteristics OH_DllCharacteristics => _model.OptionalHeader.DllCharacteristics; #else public SabreTools.Models.PortableExecutable.DllCharacteristics? OH_DllCharacteristics => _model.OptionalHeader?.DllCharacteristics; #endif /// public ulong OH_SizeOfStackReserve => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? _model.OptionalHeader.SizeOfStackReserve_PE32 : _model.OptionalHeader?.SizeOfStackReserve_PE32Plus ?? 0; /// public ulong OH_SizeOfStackCommit => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? _model.OptionalHeader.SizeOfStackCommit_PE32 : _model.OptionalHeader?.SizeOfStackCommit_PE32Plus ?? 0; /// public ulong OH_SizeOfHeapReserve => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? _model.OptionalHeader.SizeOfHeapReserve_PE32 : _model.OptionalHeader?.SizeOfHeapReserve_PE32Plus ?? 0; /// public ulong OH_SizeOfHeapCommit => _model.OptionalHeader?.Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32 ? _model.OptionalHeader.SizeOfHeapCommit_PE32 : _model.OptionalHeader?.SizeOfHeapCommit_PE32Plus ?? 0; /// #if NET48 public uint OH_LoaderFlags => _model.OptionalHeader.LoaderFlags; #else public uint? OH_LoaderFlags => _model.OptionalHeader?.LoaderFlags; #endif /// #if NET48 public uint OH_NumberOfRvaAndSizes => _model.OptionalHeader.NumberOfRvaAndSizes; #else public uint? OH_NumberOfRvaAndSizes => _model.OptionalHeader?.NumberOfRvaAndSizes; #endif #endregion #region Data Directories /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ExportTable => _model.OptionalHeader.ExportTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ExportTable => _model.OptionalHeader?.ExportTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ImportTable => _model.OptionalHeader.ImportTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ImportTable => _model.OptionalHeader?.ImportTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ResourceTable => _model.OptionalHeader.ResourceTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ResourceTable => _model.OptionalHeader?.ResourceTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ExceptionTable => _model.OptionalHeader.ExceptionTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ExceptionTable => _model.OptionalHeader?.ExceptionTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_CertificateTable => _model.OptionalHeader.CertificateTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_CertificateTable => _model.OptionalHeader?.CertificateTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_BaseRelocationTable => _model.OptionalHeader.BaseRelocationTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_BaseRelocationTable => _model.OptionalHeader?.BaseRelocationTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_Debug => _model.OptionalHeader.Debug; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_Debug => _model.OptionalHeader?.Debug; #endif /// #if NET48 public ulong OH_Architecture => _model.OptionalHeader.Architecture; #else public ulong? OH_Architecture => _model.OptionalHeader?.Architecture; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_GlobalPtr => _model.OptionalHeader.GlobalPtr; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_GlobalPtr => _model.OptionalHeader?.GlobalPtr; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ThreadLocalStorageTable => _model.OptionalHeader.ThreadLocalStorageTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ThreadLocalStorageTable => _model.OptionalHeader?.ThreadLocalStorageTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_LoadConfigTable => _model.OptionalHeader.LoadConfigTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_LoadConfigTable => _model.OptionalHeader?.LoadConfigTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_BoundImport => _model.OptionalHeader.BoundImport; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_BoundImport => _model.OptionalHeader?.BoundImport; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_ImportAddressTable => _model.OptionalHeader.ImportAddressTable; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_ImportAddressTable => _model.OptionalHeader?.ImportAddressTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_DelayImportDescriptor => _model.OptionalHeader.DelayImportDescriptor; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_DelayImportDescriptor => _model.OptionalHeader?.DelayImportDescriptor; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DataDirectory OH_CLRRuntimeHeader => _model.OptionalHeader.CLRRuntimeHeader; #else public SabreTools.Models.PortableExecutable.DataDirectory? OH_CLRRuntimeHeader => _model.OptionalHeader?.CLRRuntimeHeader; #endif /// #if NET48 public ulong OH_Reserved => _model.OptionalHeader.Reserved; #else public ulong? OH_Reserved => _model.OptionalHeader?.Reserved; #endif #endregion #endregion #region Tables /// #if NET48 public SabreTools.Models.PortableExecutable.SectionHeader[] SectionTable => _model.SectionTable; #else public SabreTools.Models.PortableExecutable.SectionHeader?[]? SectionTable => _model.SectionTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.COFFSymbolTableEntry[] COFFSymbolTable => _model.COFFSymbolTable; #else public SabreTools.Models.PortableExecutable.COFFSymbolTableEntry?[]? COFFSymbolTable => _model.COFFSymbolTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.COFFStringTable COFFStringTable => _model.COFFStringTable; #else public SabreTools.Models.PortableExecutable.COFFStringTable? COFFStringTable => _model.COFFStringTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.AttributeCertificateTableEntry[] AttributeCertificateTable => _model.AttributeCertificateTable; #else public SabreTools.Models.PortableExecutable.AttributeCertificateTableEntry?[]? AttributeCertificateTable => _model.AttributeCertificateTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DelayLoadDirectoryTable DelayLoadDirectoryTable => _model.DelayLoadDirectoryTable; #else public SabreTools.Models.PortableExecutable.DelayLoadDirectoryTable? DelayLoadDirectoryTable => _model.DelayLoadDirectoryTable; #endif #endregion #region Sections /// #if NET48 public SabreTools.Models.PortableExecutable.BaseRelocationBlock[] BaseRelocationTable => _model.BaseRelocationTable; #else public SabreTools.Models.PortableExecutable.BaseRelocationBlock?[]? BaseRelocationTable => _model.BaseRelocationTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.DebugTable DebugTable => _model.DebugTable; #else public SabreTools.Models.PortableExecutable.DebugTable? DebugTable => _model.DebugTable; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.ExportTable ExportTable => _model.ExportTable; #else public SabreTools.Models.PortableExecutable.ExportTable? ExportTable => _model.ExportTable; #endif /// #if NET48 public string[] ExportNameTable => _model.ExportTable?.ExportNameTable?.Strings; #else public string[]? ExportNameTable => _model.ExportTable?.ExportNameTable?.Strings; #endif /// #if NET48 public SabreTools.Models.PortableExecutable.ImportTable ImportTable => _model.ImportTable; #else public SabreTools.Models.PortableExecutable.ImportTable? ImportTable => _model.ImportTable; #endif /// #if NET48 public string[] ImportHintNameTable => _model.ImportTable?.HintNameTable != null #else public string?[]? ImportHintNameTable => _model.ImportTable?.HintNameTable != null #endif ? _model.ImportTable.HintNameTable.Select(entry => entry?.Name).ToArray() : null; /// #if NET48 public SabreTools.Models.PortableExecutable.ResourceDirectoryTable ResourceDirectoryTable => _model.ResourceDirectoryTable; #else public SabreTools.Models.PortableExecutable.ResourceDirectoryTable? ResourceDirectoryTable => _model.ResourceDirectoryTable; #endif #endregion #endregion #region Extension Properties /// /// Header padding data, if it exists /// #if NET48 public byte[] HeaderPaddingData #else public byte[]? HeaderPaddingData #endif { get { lock (_sourceDataLock) { // If we already have cached data, just use that immediately if (_headerPaddingData != null) return _headerPaddingData; // TODO: Don't scan the known header data as well // If the section table is missing if (SectionTable == null) return null; // Populate the raw header padding data based on the source #if NET48 uint headerStartAddress = Stub_NewExeHeaderAddr; #else uint headerStartAddress = Stub_NewExeHeaderAddr ?? 0; #endif uint firstSectionAddress = SectionTable .Select(s => s?.PointerToRawData ?? 0) .Where(s => s != 0) .OrderBy(s => s) .First(); int headerLength = (int)(firstSectionAddress - headerStartAddress); _headerPaddingData = ReadFromDataSource((int)headerStartAddress, headerLength); // Cache and return the header padding data, even if null return _headerPaddingData; } } } /// /// Header padding strings, if they exist /// #if NET48 public List HeaderPaddingStrings #else public List? HeaderPaddingStrings #endif { get { lock (_sourceDataLock) { // If we already have cached data, just use that immediately if (_headerPaddingStrings != null) return _headerPaddingStrings; // TODO: Don't scan the known header data as well // If the section table is missing if (SectionTable == null) return null; // Populate the raw header padding data based on the source #if NET48 uint headerStartAddress = Stub_NewExeHeaderAddr; #else uint headerStartAddress = Stub_NewExeHeaderAddr ?? 0; #endif uint firstSectionAddress = SectionTable .Select(s => s?.PointerToRawData ?? 0) .Where(s => s != 0) .OrderBy(s => s) .First(); int headerLength = (int)(firstSectionAddress - headerStartAddress); _headerPaddingStrings = ReadStringsFromDataSource((int)headerStartAddress, headerLength, charLimit: 3); // Cache and return the header padding data, even if null return _headerPaddingStrings; } } } /// /// Entry point data, if it exists /// #if NET48 public byte[] EntryPointData #else public byte[]? EntryPointData #endif { get { lock (_sourceDataLock) { // If the section table is missing if (SectionTable == null) return null; #if NET6_0_OR_GREATER // If the address is missing if (OH_AddressOfEntryPoint == null) return null; #endif // If we have no entry point #if NET48 int entryPointAddress = (int)OH_AddressOfEntryPoint.ConvertVirtualAddress(SectionTable); #else int entryPointAddress = (int)OH_AddressOfEntryPoint.Value.ConvertVirtualAddress(SectionTable); #endif if (entryPointAddress == 0) return null; // If the entry point matches with the start of a section, use that int entryPointSection = FindEntryPointSectionIndex(); if (entryPointSection >= 0 && OH_AddressOfEntryPoint == SectionTable[entryPointSection]?.VirtualAddress) return GetSectionData(entryPointSection); // If we already have cached data, just use that immediately if (_entryPointData != null) return _entryPointData; // Read the first 128 bytes of the entry point _entryPointData = ReadFromDataSource(entryPointAddress, length: 128); // Cache and return the entry point padding data, even if null return _entryPointData; } } } /// /// Address of the overlay, if it exists /// /// public int OverlayAddress { get { lock (_sourceDataLock) { // Use the cached data if possible if (_overlayAddress != null) return _overlayAddress.Value; // Get the end of the file, if possible int endOfFile = GetEndOfFile(); if (endOfFile == -1) return -1; // If the section table is missing if (SectionTable == null) return -1; // If we have certificate data, use that as the end if (OH_CertificateTable != null) { int certificateTableAddress = (int)OH_CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable); if (certificateTableAddress != 0 && certificateTableAddress < endOfFile) endOfFile = certificateTableAddress; } // Search through all sections and find the furthest a section goes int endOfSectionData = -1; foreach (var section in SectionTable) { // If we have an invalid section if (section == null) continue; // If we have an invalid section address int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable); if (sectionAddress == 0) continue; // If we have an invalid section size if (section.SizeOfRawData == 0 && section.VirtualSize == 0) continue; // Get the real section size int sectionSize; if (section.SizeOfRawData < section.VirtualSize) sectionSize = (int)section.VirtualSize; else sectionSize = (int)section.SizeOfRawData; // Compare and set the end of section data if (sectionAddress + sectionSize > endOfSectionData) endOfSectionData = sectionAddress + sectionSize; } // If we didn't find the end of section data if (endOfSectionData <= 0) endOfSectionData = -1; // Cache and return the position _overlayAddress = endOfSectionData; return _overlayAddress.Value; } } } /// /// Overlay data, if it exists /// /// #if NET48 public byte[] OverlayData #else public byte[]? OverlayData #endif { get { lock (_sourceDataLock) { // Use the cached data if possible if (_overlayData != null) return _overlayData; // Get the end of the file, if possible int endOfFile = GetEndOfFile(); if (endOfFile == -1) return null; // If the section table is missing if (SectionTable == null) return null; // If we have certificate data, use that as the end if (OH_CertificateTable != null) { int certificateTableAddress = (int)OH_CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable); if (certificateTableAddress != 0 && certificateTableAddress < endOfFile) endOfFile = certificateTableAddress; } // Search through all sections and find the furthest a section goes int endOfSectionData = -1; foreach (var section in SectionTable) { // If we have an invalid section if (section == null) continue; // If we have an invalid section address int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable); if (sectionAddress == 0) continue; // If we have an invalid section size if (section.SizeOfRawData == 0 && section.VirtualSize == 0) continue; // Get the real section size int sectionSize; if (section.SizeOfRawData < section.VirtualSize) sectionSize = (int)section.VirtualSize; else sectionSize = (int)section.SizeOfRawData; // Compare and set the end of section data if (sectionAddress + sectionSize > endOfSectionData) endOfSectionData = sectionAddress + sectionSize; } // If we didn't find the end of section data if (endOfSectionData <= 0) return null; // If we're at the end of the file, cache an empty byte array if (endOfSectionData >= endOfFile) { _overlayData = new byte[0]; return _overlayData; } // Otherwise, cache and return the data int overlayLength = endOfFile - endOfSectionData; _overlayData = ReadFromDataSource(endOfSectionData, overlayLength); return _overlayData; } } } /// /// Overlay strings, if they exist /// #if NET48 public List OverlayStrings #else public List? OverlayStrings #endif { get { lock (_sourceDataLock) { // Use the cached data if possible if (_overlayStrings != null) return _overlayStrings; // Get the end of the file, if possible int endOfFile = GetEndOfFile(); if (endOfFile == -1) return null; // If the section table is missing if (SectionTable == null) return null; // If we have certificate data, use that as the end if (OH_CertificateTable != null) { var certificateTable = OH_CertificateTable; int certificateTableAddress = (int)certificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable); if (certificateTableAddress != 0 && certificateTableAddress < endOfFile) endOfFile = certificateTableAddress; } // Search through all sections and find the furthest a section goes int endOfSectionData = -1; foreach (var section in SectionTable) { // If we have an invalid section if (section == null) continue; // If we have an invalid section address int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable); if (sectionAddress == 0) continue; // If we have an invalid section size if (section.SizeOfRawData == 0 && section.VirtualSize == 0) continue; // Get the real section size int sectionSize; if (section.SizeOfRawData < section.VirtualSize) sectionSize = (int)section.VirtualSize; else sectionSize = (int)section.SizeOfRawData; // Compare and set the end of section data if (sectionAddress + sectionSize > endOfSectionData) endOfSectionData = sectionAddress + sectionSize; } // If we didn't find the end of section data if (endOfSectionData <= 0) return null; // If we're at the end of the file, cache an empty list if (endOfSectionData >= endOfFile) { _overlayStrings = new List(); return _overlayStrings; } // Otherwise, cache and return the strings int overlayLength = endOfFile - endOfSectionData; _overlayStrings = ReadStringsFromDataSource(endOfSectionData, overlayLength, charLimit: 3); return _overlayStrings; } } } /// /// Sanitized section names /// #if NET48 public string[] SectionNames #else public string[]? SectionNames #endif { get { lock (_sourceDataLock) { // Use the cached data if possible if (_sectionNames != null) return _sectionNames; // If there are no sections if (SectionTable == null) return null; // Otherwise, build and return the cached array _sectionNames = new string[SectionTable.Length]; for (int i = 0; i < _sectionNames.Length; i++) { var section = SectionTable[i]; if (section == null) continue; // TODO: Handle long section names with leading `/` #if NET48 byte[] sectionNameBytes = section.Name; #else byte[]? sectionNameBytes = section.Name; #endif if (sectionNameBytes != null) { string sectionNameString = Encoding.UTF8.GetString(sectionNameBytes).TrimEnd('\0'); _sectionNames[i] = sectionNameString; } } return _sectionNames; } } } /// /// Stub executable data, if it exists /// #if NET48 public byte[] StubExecutableData #else public byte[]? StubExecutableData #endif { get { lock (_sourceDataLock) { // If we already have cached data, just use that immediately if (_stubExecutableData != null) return _stubExecutableData; #if NET6_0_OR_GREATER if (Stub_NewExeHeaderAddr == null) return null; #endif // Populate the raw stub executable data based on the source int endOfStubHeader = 0x40; int lengthOfStubExecutableData = (int)Stub_NewExeHeaderAddr - endOfStubHeader; _stubExecutableData = ReadFromDataSource(endOfStubHeader, lengthOfStubExecutableData); // Cache and return the stub executable data, even if null return _stubExecutableData; } } } /// /// Dictionary of debug data /// #if NET48 public Dictionary DebugData #else public Dictionary? DebugData #endif { get { lock (_sourceDataLock) { // Use the cached data if possible if (_debugData != null && _debugData.Count != 0) return _debugData; // If we have no resource table, just return if (DebugTable?.DebugDirectoryTable == null || DebugTable.DebugDirectoryTable.Length == 0) return null; // Otherwise, build and return the cached dictionary ParseDebugTable(); return _debugData; } } } /// /// Dictionary of resource data /// #if NET48 public Dictionary ResourceData #else public Dictionary? ResourceData #endif { get { lock (_sourceDataLock) { // Use the cached data if possible if (_resourceData != null && _resourceData.Count != 0) return _resourceData; // If we have no resource table, just return if (OH_ResourceTable == null || OH_ResourceTable.VirtualAddress == 0 || ResourceDirectoryTable == null) return null; // Otherwise, build and return the cached dictionary ParseResourceDirectoryTable(ResourceDirectoryTable, types: new List()); return _resourceData; } } } #region Version Information /// /// "Build GUID" /// #if NET48 public string BuildGuid => GetVersionInfoString("BuildGuid"); #else public string? BuildGuid => GetVersionInfoString("BuildGuid"); #endif /// /// "Build signature" /// #if NET48 public string BuildSignature => GetVersionInfoString("BuildSignature"); #else public string? BuildSignature => GetVersionInfoString("BuildSignature"); #endif /// /// Additional information that should be displayed for diagnostic purposes. /// #if NET48 public string Comments => GetVersionInfoString("Comments"); #else public string? Comments => GetVersionInfoString("Comments"); #endif /// /// Company that produced the file—for example, "Microsoft Corporation" or /// "Standard Microsystems Corporation, Inc." This string is required. /// #if NET48 public string CompanyName => GetVersionInfoString("CompanyName"); #else public string? CompanyName => GetVersionInfoString("CompanyName"); #endif /// /// "Debug version" /// #if NET48 public string DebugVersion => GetVersionInfoString("DebugVersion"); #else public string? DebugVersion => GetVersionInfoString("DebugVersion"); #endif /// /// File description to be presented to users. This string may be displayed in a /// list box when the user is choosing files to install—for example, "Keyboard /// Driver for AT-Style Keyboards". This string is required. /// #if NET48 public string FileDescription => GetVersionInfoString("FileDescription"); #else public string? FileDescription => GetVersionInfoString("FileDescription"); #endif /// /// Version number of the file—for example, "3.10" or "5.00.RC2". This string /// is required. /// #if NET48 public string FileVersion => GetVersionInfoString("FileVersion"); #else public string? FileVersion => GetVersionInfoString("FileVersion"); #endif /// /// Internal name of the file, if one exists—for example, a module name if the /// file is a dynamic-link library. If the file has no internal name, this /// string should be the original filename, without extension. This string is required. /// #if NET48 public string InternalName => GetVersionInfoString(key: "InternalName"); #else public string? InternalName => GetVersionInfoString(key: "InternalName"); #endif /// /// Copyright notices that apply to the file. This should include the full text of /// all notices, legal symbols, copyright dates, and so on. This string is optional. /// #if NET48 public string LegalCopyright => GetVersionInfoString(key: "LegalCopyright"); #else public string? LegalCopyright => GetVersionInfoString(key: "LegalCopyright"); #endif /// /// Trademarks and registered trademarks that apply to the file. This should include /// the full text of all notices, legal symbols, trademark numbers, and so on. This /// string is optional. /// #if NET48 public string LegalTrademarks => GetVersionInfoString(key: "LegalTrademarks"); #else public string? LegalTrademarks => GetVersionInfoString(key: "LegalTrademarks"); #endif /// /// Original name of the file, not including a path. This information enables an /// application to determine whether a file has been renamed by a user. The format of /// the name depends on the file system for which the file was created. This string /// is required. /// #if NET48 public string OriginalFilename => GetVersionInfoString(key: "OriginalFilename"); #else public string? OriginalFilename => GetVersionInfoString(key: "OriginalFilename"); #endif /// /// Information about a private version of the file—for example, "Built by TESTER1 on /// \TESTBED". This string should be present only if VS_FF_PRIVATEBUILD is specified in /// the fileflags parameter of the root block. /// #if NET48 public string PrivateBuild => GetVersionInfoString(key: "PrivateBuild"); #else public string? PrivateBuild => GetVersionInfoString(key: "PrivateBuild"); #endif /// /// "Product GUID" /// #if NET48 public string ProductGuid => GetVersionInfoString("ProductGuid"); #else public string? ProductGuid => GetVersionInfoString("ProductGuid"); #endif /// /// Name of the product with which the file is distributed. This string is required. /// #if NET48 public string ProductName => GetVersionInfoString(key: "ProductName"); #else public string? ProductName => GetVersionInfoString(key: "ProductName"); #endif /// /// Version of the product with which the file is distributed—for example, "3.10" or /// "5.00.RC2". This string is required. /// #if NET48 public string ProductVersion => GetVersionInfoString(key: "ProductVersion"); #else public string? ProductVersion => GetVersionInfoString(key: "ProductVersion"); #endif /// /// Text that specifies how this version of the file differs from the standard /// version—for example, "Private build for TESTER1 solving mouse problems on M250 and /// M250E computers". This string should be present only if VS_FF_SPECIALBUILD is /// specified in the fileflags parameter of the root block. /// #if NET48 public string SpecialBuild => GetVersionInfoString(key: "SpecialBuild") ?? GetVersionInfoString(key: "Special Build"); #else public string? SpecialBuild => GetVersionInfoString(key: "SpecialBuild") ?? GetVersionInfoString(key: "Special Build"); #endif /// /// "Trade name" /// #if NET48 public string TradeName => GetVersionInfoString(key: "TradeName"); #else public string? TradeName => GetVersionInfoString(key: "TradeName"); #endif /// /// Get the internal version as reported by the resources /// /// Version string, null on error /// The internal version is either the file version, product version, or assembly version, in that order #if NET48 public string GetInternalVersion() #else public string? GetInternalVersion() #endif { #if NET48 string version = this.FileVersion; #else string? version = this.FileVersion; #endif if (!string.IsNullOrWhiteSpace(version)) return version.Replace(", ", "."); version = this.ProductVersion; if (!string.IsNullOrWhiteSpace(version)) return version.Replace(", ", "."); version = this.AssemblyVersion; if (!string.IsNullOrWhiteSpace(version)) return version; return null; } #endregion #region Manifest Information /// /// Description as derived from the assembly manifest /// #if NET48 public string AssemblyDescription #else public string? AssemblyDescription #endif { get { var manifest = GetAssemblyManifest(); return manifest? .Description? .Value; } } /// /// Version as derived from the assembly manifest /// /// /// If there are multiple identities included in the manifest, /// this will only retrieve the value from the first that doesn't /// have a null or empty version. /// #if NET48 public string AssemblyVersion #else public string? AssemblyVersion #endif { get { var manifest = GetAssemblyManifest(); return manifest? .AssemblyIdentities? .FirstOrDefault(ai => !string.IsNullOrWhiteSpace(ai?.Version))? .Version; } } #endregion #endregion #region Instance Variables /// /// Header padding data, if it exists /// #if NET48 private byte[] _headerPaddingData = null; #else private byte[]? _headerPaddingData = null; #endif /// /// Header padding strings, if they exist /// #if NET48 private List _headerPaddingStrings = null; #else private List? _headerPaddingStrings = null; #endif /// /// Entry point data, if it exists and isn't aligned to a section /// #if NET48 private byte[] _entryPointData = null; #else private byte[]? _entryPointData = null; #endif /// /// Address of the overlay, if it exists /// private int? _overlayAddress = null; /// /// Overlay data, if it exists /// #if NET48 private byte[] _overlayData = null; #else private byte[]? _overlayData = null; #endif /// /// Overlay strings, if they exist /// #if NET48 private List _overlayStrings = null; #else private List? _overlayStrings = null; #endif /// /// Stub executable data, if it exists /// #if NET48 private byte[] _stubExecutableData = null; #else private byte[]? _stubExecutableData = null; #endif /// /// Sanitized section names /// #if NET48 private string[] _sectionNames = null; #else private string[]? _sectionNames = null; #endif /// /// Cached raw section data /// #if NET48 private byte[][] _sectionData = null; #else private byte[]?[]? _sectionData = null; #endif /// /// Cached found string data in sections /// #if NET48 private List[] _sectionStringData = null; #else private List?[]? _sectionStringData = null; #endif /// /// Cached raw table data /// #if NET48 private byte[][] _tableData = null; #else private byte[]?[]? _tableData = null; #endif /// /// Cached found string data in tables /// #if NET48 private List[] _tableStringData = null; #else private List?[]? _tableStringData = null; #endif /// /// Cached debug data /// private readonly Dictionary _debugData = new Dictionary(); /// /// Cached resource data /// #if NET48 private readonly Dictionary _resourceData = new Dictionary(); #else private readonly Dictionary _resourceData = new Dictionary(); #endif /// /// Cached version info data /// #if NET48 private SabreTools.Models.PortableExecutable.VersionInfo _versionInfo = null; #else private SabreTools.Models.PortableExecutable.VersionInfo? _versionInfo = null; #endif /// /// Cached assembly manifest data /// #if NET48 private SabreTools.Models.PortableExecutable.AssemblyManifest _assemblyManifest = null; #else private SabreTools.Models.PortableExecutable.AssemblyManifest? _assemblyManifest = null; #endif /// /// Lock object for reading from the source /// private readonly object _sourceDataLock = new object(); #endregion #region Constructors /// #if NET48 public PortableExecutable(SabreTools.Models.PortableExecutable.Executable model, byte[] data, int offset) #else public PortableExecutable(SabreTools.Models.PortableExecutable.Executable? model, byte[]? data, int offset) #endif : base(model, data, offset) { // All logic is handled by the base class } /// #if NET48 public PortableExecutable(SabreTools.Models.PortableExecutable.Executable model, Stream data) #else public PortableExecutable(SabreTools.Models.PortableExecutable.Executable? model, Stream? data) #endif : base(model, data) { // All logic is handled by the base class } /// /// Create a PE executable from a byte array and offset /// /// Byte array representing the executable /// Offset within the array to parse /// A PE executable wrapper on success, null on failure #if NET48 public static PortableExecutable Create(byte[] data, int offset) #else public static PortableExecutable? Create(byte[]? data, int offset) #endif { // 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 use that MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); return Create(dataStream); } /// /// Create a PE executable from a Stream /// /// Stream representing the executable /// A PE executable wrapper on success, null on failure #if NET48 public static PortableExecutable Create(Stream data) #else public static PortableExecutable? Create(Stream? data) #endif { // If the data is invalid if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) return null; var executable = new SabreTools.Serialization.Streams.PortableExecutable().Deserialize(data); if (executable == null) return null; try { return new PortableExecutable(executable, data); } catch { return null; } } #endregion #region Data // TODO: Cache all certificate objects /// /// Get the version info string associated with a key, if possible /// /// Case-insensitive key to find in the version info /// String representing the data, null on error /// /// This code does not take into account the locale and will find and return /// the first available value. This may not actually matter for version info, /// but it is worth mentioning. /// #if NET48 public string GetVersionInfoString(string key) #else public string? GetVersionInfoString(string key) #endif { // If we have an invalid key, we can't do anything if (string.IsNullOrEmpty(key)) return null; // Ensure that we have the resource data cached if (ResourceData == null) return null; // If we don't have string version info in this executable var stringTable = _versionInfo?.StringFileInfo?.Children; if (stringTable == null || !stringTable.Any()) return null; // Try to find a key that matches var match = stringTable .SelectMany(st => st?.Children ?? Array.Empty()) .FirstOrDefault(sd => sd != null && key.Equals(sd.Key, StringComparison.OrdinalIgnoreCase)); // Return either the match or null return match?.Value?.TrimEnd('\0'); } /// /// Get the assembly manifest, if possible /// /// Assembly manifest object, null on error #if NET48 private SabreTools.Models.PortableExecutable.AssemblyManifest GetAssemblyManifest() #else private SabreTools.Models.PortableExecutable.AssemblyManifest? GetAssemblyManifest() #endif { // Use the cached data if possible if (_assemblyManifest != null) return _assemblyManifest; // Ensure that we have the resource data cached if (ResourceData == null) return null; // Return the now-cached assembly manifest return _assemblyManifest; } #endregion #region Printing /// public override StringBuilder PrettyPrint() { StringBuilder builder = new StringBuilder(); builder.AppendLine("Portable Executable Information:"); builder.AppendLine("-------------------------"); builder.AppendLine(); // Stub PrintStubHeader(builder); PrintStubExtendedHeader(builder); // Header PrintCOFFFileHeader(builder); PrintOptionalHeader(builder); // Tables PrintSectionTable(builder); PrintCOFFSymbolTable(builder); PrintAttributeCertificateTable(builder); PrintDelayLoadDirectoryTable(builder); // Named Sections PrintBaseRelocationTable(builder); PrintDebugTable(builder); PrintExportTable(builder); PrintImportTable(builder); PrintResourceDirectoryTable(builder); return builder; } /// /// Print stub header information /// /// StringBuilder to append information to private void PrintStubHeader(StringBuilder builder) { builder.AppendLine(" MS-DOS Stub Header Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine($" Magic number: {Stub_Magic}"); builder.AppendLine($" Last page bytes: {Stub_LastPageBytes} (0x{Stub_LastPageBytes:X})"); builder.AppendLine($" Pages: {Stub_Pages} (0x{Stub_Pages:X})"); builder.AppendLine($" Relocation items: {Stub_RelocationItems} (0x{Stub_RelocationItems:X})"); builder.AppendLine($" Header paragraph size: {Stub_HeaderParagraphSize} (0x{Stub_HeaderParagraphSize:X})"); builder.AppendLine($" Minimum extra paragraphs: {Stub_MinimumExtraParagraphs} (0x{Stub_MinimumExtraParagraphs:X})"); builder.AppendLine($" Maximum extra paragraphs: {Stub_MaximumExtraParagraphs} (0x{Stub_MaximumExtraParagraphs:X})"); builder.AppendLine($" Initial SS value: {Stub_InitialSSValue} (0x{Stub_InitialSSValue:X})"); builder.AppendLine($" Initial SP value: {Stub_InitialSPValue} (0x{Stub_InitialSPValue:X})"); builder.AppendLine($" Checksum: {Stub_Checksum} (0x{Stub_Checksum:X})"); builder.AppendLine($" Initial IP value: {Stub_InitialIPValue} (0x{Stub_InitialIPValue:X})"); builder.AppendLine($" Initial CS value: {Stub_InitialCSValue} (0x{Stub_InitialCSValue:X})"); builder.AppendLine($" Relocation table address: {Stub_RelocationTableAddr} (0x{Stub_RelocationTableAddr:X})"); builder.AppendLine($" Overlay number: {Stub_OverlayNumber} (0x{Stub_OverlayNumber:X})"); builder.AppendLine(); } /// /// Print stub extended header information /// /// StringBuilder to append information to private void PrintStubExtendedHeader(StringBuilder builder) { builder.AppendLine(" MS-DOS Stub Extended Header Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine($" Reserved words: {(Stub_Reserved1 == null ? "[NULL]" : string.Join(", ", Stub_Reserved1))}"); builder.AppendLine($" OEM identifier: {Stub_OEMIdentifier} (0x{Stub_OEMIdentifier:X})"); builder.AppendLine($" OEM information: {Stub_OEMInformation} (0x{Stub_OEMInformation:X})"); builder.AppendLine($" Reserved words: {(Stub_Reserved2 == null ? "[NULL]" : string.Join(", ", Stub_Reserved2))}"); builder.AppendLine($" New EXE header address: {Stub_NewExeHeaderAddr} (0x{Stub_NewExeHeaderAddr:X})"); builder.AppendLine(); } /// /// Print COFF file header information /// /// StringBuilder to append information to private void PrintCOFFFileHeader(StringBuilder builder) { builder.AppendLine(" COFF File Header Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine($" Signature: {Signature}"); builder.AppendLine($" Machine: {Machine} (0x{Machine:X})"); builder.AppendLine($" Number of sections: {NumberOfSections} (0x{NumberOfSections:X})"); builder.AppendLine($" Time/Date stamp: {TimeDateStamp} (0x{TimeDateStamp:X})"); builder.AppendLine($" Pointer to symbol table: {PointerToSymbolTable} (0x{PointerToSymbolTable:X})"); builder.AppendLine($" Number of symbols: {NumberOfSymbols} (0x{NumberOfSymbols:X})"); builder.AppendLine($" Size of optional header: {SizeOfOptionalHeader} (0x{SizeOfOptionalHeader:X})"); builder.AppendLine($" Characteristics: {Characteristics} (0x{Characteristics:X})"); builder.AppendLine(); } /// /// Print optional header information /// /// StringBuilder to append information to private void PrintOptionalHeader(StringBuilder builder) { builder.AppendLine(" Optional Header Information:"); builder.AppendLine(" -------------------------"); if (SizeOfOptionalHeader == 0) { builder.AppendLine(" No optional header present"); } else { builder.AppendLine($" Magic: {OH_Magic} (0x{OH_Magic:X})"); builder.AppendLine($" Major linker version: {OH_MajorLinkerVersion} (0x{OH_MajorLinkerVersion:X})"); builder.AppendLine($" Minor linker version: {OH_MinorLinkerVersion} (0x{OH_MinorLinkerVersion:X})"); builder.AppendLine($" Size of code section: {OH_SizeOfCode} (0x{OH_SizeOfCode:X})"); builder.AppendLine($" Size of initialized data: {OH_SizeOfInitializedData} (0x{OH_SizeOfInitializedData:X})"); builder.AppendLine($" Size of uninitialized data: {OH_SizeOfUninitializedData} (0x{OH_SizeOfUninitializedData:X})"); builder.AppendLine($" Address of entry point: {OH_AddressOfEntryPoint} (0x{OH_AddressOfEntryPoint:X})"); builder.AppendLine($" Base of code: {OH_BaseOfCode} (0x{OH_BaseOfCode:X})"); if (OH_Magic == SabreTools.Models.PortableExecutable.OptionalHeaderMagicNumber.PE32) builder.AppendLine($" Base of data: {OH_BaseOfData} (0x{OH_BaseOfData:X})"); builder.AppendLine($" Image base: {OH_ImageBase} (0x{OH_ImageBase:X})"); builder.AppendLine($" Section alignment: {OH_SectionAlignment} (0x{OH_SectionAlignment:X})"); builder.AppendLine($" File alignment: {OH_FileAlignment} (0x{OH_FileAlignment:X})"); builder.AppendLine($" Major operating system version: {OH_MajorOperatingSystemVersion} (0x{OH_MajorOperatingSystemVersion:X})"); builder.AppendLine($" Minor operating system version: {OH_MinorOperatingSystemVersion} (0x{OH_MinorOperatingSystemVersion:X})"); builder.AppendLine($" Major image version: {OH_MajorImageVersion} (0x{OH_MajorImageVersion:X})"); builder.AppendLine($" Minor image version: {OH_MinorImageVersion} (0x{OH_MinorImageVersion:X})"); builder.AppendLine($" Major subsystem version: {OH_MajorSubsystemVersion} (0x{OH_MajorSubsystemVersion:X})"); builder.AppendLine($" Minor subsystem version: {OH_MinorSubsystemVersion} (0x{OH_MinorSubsystemVersion:X})"); builder.AppendLine($" Win32 version value: {OH_Win32VersionValue} (0x{OH_Win32VersionValue:X})"); builder.AppendLine($" Size of image: {OH_SizeOfImage} (0x{OH_SizeOfImage:X})"); builder.AppendLine($" Size of headers: {OH_SizeOfHeaders} (0x{OH_SizeOfHeaders:X})"); builder.AppendLine($" Checksum: {OH_CheckSum} (0x{OH_CheckSum:X})"); builder.AppendLine($" Subsystem: {OH_Subsystem} (0x{OH_Subsystem:X})"); builder.AppendLine($" DLL characteristics: {OH_DllCharacteristics} (0x{OH_DllCharacteristics:X})"); builder.AppendLine($" Size of stack reserve: {OH_SizeOfStackReserve} (0x{OH_SizeOfStackReserve:X})"); builder.AppendLine($" Size of stack commit: {OH_SizeOfStackCommit} (0x{OH_SizeOfStackCommit:X})"); builder.AppendLine($" Size of heap reserve: {OH_SizeOfHeapReserve} (0x{OH_SizeOfHeapReserve:X})"); builder.AppendLine($" Size of heap commit: {OH_SizeOfHeapCommit} (0x{OH_SizeOfHeapCommit:X})"); builder.AppendLine($" Loader flags: {OH_LoaderFlags} (0x{OH_LoaderFlags:X})"); builder.AppendLine($" Number of data-directory entries: {OH_NumberOfRvaAndSizes} (0x{OH_NumberOfRvaAndSizes:X})"); if (OH_ExportTable != null) { builder.AppendLine(" Export Table (1)"); builder.AppendLine($" Virtual address: {OH_ExportTable.VirtualAddress} (0x{OH_ExportTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ExportTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ExportTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ExportTable.Size} (0x{OH_ExportTable.Size:X})"); } if (OH_ImportTable != null) { builder.AppendLine(" Import Table (2)"); builder.AppendLine($" Virtual address: {OH_ImportTable.VirtualAddress} (0x{OH_ImportTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ImportTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ImportTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ImportTable.Size} (0x{OH_ImportTable.Size:X})"); } if (OH_ResourceTable != null) { builder.AppendLine(" Resource Table (3)"); builder.AppendLine($" Virtual address: {OH_ResourceTable.VirtualAddress} (0x{OH_ResourceTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ResourceTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ResourceTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ResourceTable.Size} (0x{OH_ResourceTable.Size:X})"); } if (OH_ExceptionTable != null) { builder.AppendLine(" Exception Table (4)"); builder.AppendLine($" Virtual address: {OH_ExceptionTable.VirtualAddress} (0x{OH_ExceptionTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ExceptionTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ExceptionTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ExceptionTable.Size} (0x{OH_ExceptionTable.Size:X})"); } if (OH_CertificateTable != null) { builder.AppendLine(" Certificate Table (5)"); builder.AppendLine($" Virtual address: {OH_CertificateTable.VirtualAddress} (0x{OH_CertificateTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_CertificateTable.Size} (0x{OH_CertificateTable.Size:X})"); } if (OH_BaseRelocationTable != null) { builder.AppendLine(" Base Relocation Table (6)"); builder.AppendLine($" Virtual address: {OH_BaseRelocationTable.VirtualAddress} (0x{OH_BaseRelocationTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_BaseRelocationTable.Size} (0x{OH_BaseRelocationTable.Size:X})"); } if (OH_Debug != null) { builder.AppendLine(" Debug Table (7)"); builder.AppendLine($" Virtual address: {OH_Debug.VirtualAddress} (0x{OH_Debug.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_Debug.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_Debug.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_Debug.Size} (0x{OH_Debug.Size:X})"); } if (OH_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 (OH_GlobalPtr != null) { builder.AppendLine(" Global Pointer Register (9)"); builder.AppendLine($" Virtual address: {OH_GlobalPtr.VirtualAddress} (0x{OH_GlobalPtr.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_GlobalPtr.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_GlobalPtr.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_GlobalPtr.Size} (0x{OH_GlobalPtr.Size:X})"); } if (OH_ThreadLocalStorageTable != null) { builder.AppendLine(" Thread Local Storage (TLS) Table (10)"); builder.AppendLine($" Virtual address: {OH_ThreadLocalStorageTable.VirtualAddress} (0x{OH_ThreadLocalStorageTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ThreadLocalStorageTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ThreadLocalStorageTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ThreadLocalStorageTable.Size} (0x{OH_ThreadLocalStorageTable.Size:X})"); } if (OH_LoadConfigTable != null) { builder.AppendLine(" Load Config Table (11)"); builder.AppendLine($" Virtual address: {OH_LoadConfigTable.VirtualAddress} (0x{OH_LoadConfigTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_LoadConfigTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_LoadConfigTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_LoadConfigTable.Size} (0x{OH_LoadConfigTable.Size:X})"); } if (OH_BoundImport != null) { builder.AppendLine(" Bound Import Table (12)"); builder.AppendLine($" Virtual address: {OH_BoundImport.VirtualAddress} (0x{OH_BoundImport.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_BoundImport.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_BoundImport.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_BoundImport.Size} (0x{OH_BoundImport.Size:X})"); } if (OH_ImportAddressTable != null) { builder.AppendLine(" Import Address Table (13)"); builder.AppendLine($" Virtual address: {OH_ImportAddressTable.VirtualAddress} (0x{OH_ImportAddressTable.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_ImportAddressTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_ImportAddressTable.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_ImportAddressTable.Size} (0x{OH_ImportAddressTable.Size:X})"); } if (OH_DelayImportDescriptor != null) { builder.AppendLine(" Delay Import Descriptior (14)"); builder.AppendLine($" Virtual address: {OH_DelayImportDescriptor.VirtualAddress} (0x{OH_DelayImportDescriptor.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_DelayImportDescriptor.Size} (0x{OH_DelayImportDescriptor.Size:X})"); } if (OH_CLRRuntimeHeader != null) { builder.AppendLine(" CLR Runtime Header (15)"); builder.AppendLine($" Virtual address: {OH_CLRRuntimeHeader.VirtualAddress} (0x{OH_CLRRuntimeHeader.VirtualAddress:X})"); builder.AppendLine($" Physical address: {OH_CLRRuntimeHeader.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{OH_CLRRuntimeHeader.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size: {OH_CLRRuntimeHeader.Size} (0x{OH_CLRRuntimeHeader.Size:X})"); } if (OH_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(); } /// /// Print section table information /// /// StringBuilder to append information to private void PrintSectionTable(StringBuilder builder) { builder.AppendLine(" Section Table Information:"); builder.AppendLine(" -------------------------"); if (NumberOfSections == 0 || SectionTable == null || SectionTable.Length == 0) { builder.AppendLine(" No section table items"); } else { #if NET48 for (int i = 0; i < SectionTable.Length; i++) #else for (int i = 0; i < SectionTable!.Length; i++) #endif { var entry = SectionTable[i]; builder.AppendLine($" Section Table Entry {i}"); if (entry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Name: {(entry.Name == null ? "[NULL]" : Encoding.UTF8.GetString(entry.Name).TrimEnd('\0'))}"); builder.AppendLine($" Virtual size: {entry.VirtualSize} (0x{entry.VirtualSize:X})"); builder.AppendLine($" Virtual address: {entry.VirtualAddress} (0x{entry.VirtualAddress:X})"); builder.AppendLine($" Physical address: {entry.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{entry.VirtualAddress.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Size of raw data: {entry.SizeOfRawData} (0x{entry.SizeOfRawData:X})"); builder.AppendLine($" Pointer to raw data: {entry.PointerToRawData} (0x{entry.PointerToRawData:X})"); builder.AppendLine($" Pointer to relocations: {entry.PointerToRelocations} (0x{entry.PointerToRelocations:X})"); builder.AppendLine($" Pointer to linenumbers: {entry.PointerToLinenumbers} (0x{entry.PointerToLinenumbers:X})"); builder.AppendLine($" Number of relocations: {entry.NumberOfRelocations} (0x{entry.NumberOfRelocations:X})"); builder.AppendLine($" Number of linenumbers: {entry.NumberOfLinenumbers} (0x{entry.NumberOfLinenumbers:X})"); builder.AppendLine($" Characteristics: {entry.Characteristics} (0x{entry.Characteristics:X})"); // TODO: Add COFFRelocations // TODO: Add COFFLineNumbers } } builder.AppendLine(); } /// /// Print COFF symbol table information /// /// StringBuilder to append information to private void PrintCOFFSymbolTable(StringBuilder builder) { builder.AppendLine(" COFF Symbol Table Information:"); builder.AppendLine(" -------------------------"); if (PointerToSymbolTable == 0 || NumberOfSymbols == 0 || COFFSymbolTable == null || COFFSymbolTable.Length == 0) { builder.AppendLine(" No COFF symbol table items"); } else { int auxSymbolsRemaining = 0; int currentSymbolType = 0; for (int i = 0; i < COFFSymbolTable.Length; i++) { var entry = COFFSymbolTable[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($" Short name: {(entry.ShortName == null ? "[NULL]" : Encoding.UTF8.GetString(entry.ShortName).TrimEnd('\0'))}"); } else { builder.AppendLine($" Zeroes: {entry.Zeroes} (0x{entry.Zeroes:X})"); builder.AppendLine($" Offset: {entry.Offset} (0x{entry.Offset:X})"); } builder.AppendLine($" Value: {entry.Value} (0x{entry.Value:X})"); builder.AppendLine($" Section number: {entry.SectionNumber} (0x{entry.SectionNumber:X})"); builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})"); builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})"); builder.AppendLine($" Number of aux symbols: {entry.NumberOfAuxSymbols} (0x{entry.NumberOfAuxSymbols:X})"); auxSymbolsRemaining = entry.NumberOfAuxSymbols; if (auxSymbolsRemaining == 0) continue; if (entry.StorageClass == SabreTools.Models.PortableExecutable.StorageClass.IMAGE_SYM_CLASS_EXTERNAL && entry.SymbolType == SabreTools.Models.PortableExecutable.SymbolType.IMAGE_SYM_TYPE_FUNC && entry.SectionNumber > 0) { currentSymbolType = 1; } else if (entry.StorageClass == SabreTools.Models.PortableExecutable.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 == SabreTools.Models.PortableExecutable.StorageClass.IMAGE_SYM_CLASS_EXTERNAL && entry.SectionNumber == (ushort)SabreTools.Models.PortableExecutable.SectionNumber.IMAGE_SYM_UNDEFINED && entry.Value == 0) { currentSymbolType = 3; } else if (entry.StorageClass == SabreTools.Models.PortableExecutable.StorageClass.IMAGE_SYM_CLASS_FILE) { // TODO: Symbol name should be ".file" currentSymbolType = 4; } else if (entry.StorageClass == SabreTools.Models.PortableExecutable.StorageClass.IMAGE_SYM_CLASS_STATIC) { // TODO: Should have the name of a section (like ".text") currentSymbolType = 5; } else if (entry.StorageClass == SabreTools.Models.PortableExecutable.StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN) { currentSymbolType = 6; } } else if (currentSymbolType == 1) { builder.AppendLine($" Tag index: {entry.AuxFormat1TagIndex} (0x{entry.AuxFormat1TagIndex:X})"); builder.AppendLine($" Total size: {entry.AuxFormat1TotalSize} (0x{entry.AuxFormat1TotalSize:X})"); builder.AppendLine($" Pointer to linenumber: {entry.AuxFormat1PointerToLinenumber} (0x{entry.AuxFormat1PointerToLinenumber:X})"); builder.AppendLine($" Pointer to next function: {entry.AuxFormat1PointerToNextFunction} (0x{entry.AuxFormat1PointerToNextFunction:X})"); builder.AppendLine($" Unused: {entry.AuxFormat1Unused} (0x{entry.AuxFormat1Unused:X})"); auxSymbolsRemaining--; } else if (currentSymbolType == 2) { builder.AppendLine($" Unused: {entry.AuxFormat2Unused1} (0x{entry.AuxFormat2Unused1:X})"); builder.AppendLine($" Linenumber: {entry.AuxFormat2Linenumber} (0x{entry.AuxFormat2Linenumber:X})"); builder.AppendLine($" Unused: {entry.AuxFormat2Unused2} (0x{entry.AuxFormat2Unused2:X})"); builder.AppendLine($" Pointer to next function: {entry.AuxFormat2PointerToNextFunction} (0x{entry.AuxFormat2PointerToNextFunction:X})"); builder.AppendLine($" Unused: {entry.AuxFormat2Unused3} (0x{entry.AuxFormat2Unused3:X})"); auxSymbolsRemaining--; } else if (currentSymbolType == 3) { builder.AppendLine($" Tag index: {entry.AuxFormat3TagIndex} (0x{entry.AuxFormat3TagIndex:X})"); builder.AppendLine($" Characteristics: {entry.AuxFormat3Characteristics} (0x{entry.AuxFormat3Characteristics:X})"); builder.AppendLine($" Unused: {(entry.AuxFormat3Unused == null ? "[NULL]" : BitConverter.ToString(entry.AuxFormat3Unused).Replace("-", string.Empty))}"); auxSymbolsRemaining--; } else if (currentSymbolType == 4) { builder.AppendLine($" File name: {(entry.AuxFormat4FileName == null ? "[NULL]" : Encoding.ASCII.GetString(entry.AuxFormat4FileName).TrimEnd('\0'))}"); auxSymbolsRemaining--; } else if (currentSymbolType == 5) { builder.AppendLine($" Length: {entry.AuxFormat5Length} (0x{entry.AuxFormat5Length:X})"); builder.AppendLine($" Number of relocations: {entry.AuxFormat5NumberOfRelocations} (0x{entry.AuxFormat5NumberOfRelocations:X})"); builder.AppendLine($" Number of linenumbers: {entry.AuxFormat5NumberOfLinenumbers} (0x{entry.AuxFormat5NumberOfLinenumbers:X})"); builder.AppendLine($" Checksum: {entry.AuxFormat5CheckSum} (0x{entry.AuxFormat5CheckSum:X})"); builder.AppendLine($" Number: {entry.AuxFormat5Number} (0x{entry.AuxFormat5Number:X})"); builder.AppendLine($" Selection: {entry.AuxFormat5Selection} (0x{entry.AuxFormat5Selection:X})"); builder.AppendLine($" Unused: {(entry.AuxFormat5Unused == null ? "[NULL]" : BitConverter.ToString(entry.AuxFormat5Unused).Replace("-", string.Empty))}"); auxSymbolsRemaining--; } else if (currentSymbolType == 6) { builder.AppendLine($" Aux type: {entry.AuxFormat6AuxType} (0x{entry.AuxFormat6AuxType:X})"); builder.AppendLine($" Reserved: {entry.AuxFormat6Reserved1} (0x{entry.AuxFormat6Reserved1:X})"); builder.AppendLine($" Symbol table index: {entry.AuxFormat6SymbolTableIndex} (0x{entry.AuxFormat6SymbolTableIndex:X})"); builder.AppendLine($" Reserved: {(entry.AuxFormat6Reserved2 == null ? "[NULL]" : BitConverter.ToString(entry.AuxFormat6Reserved2).Replace("-", string.Empty))}"); auxSymbolsRemaining--; } // If we hit the last aux symbol, go back to normal format if (auxSymbolsRemaining == 0) currentSymbolType = 0; } builder.AppendLine(); builder.AppendLine(" COFF String Table Information:"); builder.AppendLine(" -------------------------"); if (COFFStringTable == null || COFFStringTable.Strings == null || COFFStringTable.Strings.Length == 0) { builder.AppendLine(" No COFF string table items"); } else { builder.AppendLine($" Total size: {COFFStringTable.TotalSize} (0x{COFFStringTable.TotalSize:X})"); for (int i = 0; i < COFFStringTable.Strings.Length; i++) { string entry = COFFStringTable.Strings[i]; builder.AppendLine($" COFF String Table Entry {i})"); builder.AppendLine($" Value: {entry}"); } } } builder.AppendLine(); } /// /// Print attribute certificate table information /// /// StringBuilder to append information to private void PrintAttributeCertificateTable(StringBuilder builder) { builder.AppendLine(" Attribute Certificate Table Information:"); builder.AppendLine(" -------------------------"); if (OH_CertificateTable == null || OH_CertificateTable.VirtualAddress == 0 || AttributeCertificateTable == null || AttributeCertificateTable.Length == 0) { builder.AppendLine(" No attribute certificate table items"); } else { for (int i = 0; i < AttributeCertificateTable.Length; i++) { var entry = AttributeCertificateTable[i]; builder.AppendLine($" Attribute Certificate Table Entry {i}"); if (entry == null) { builder.AppendLine($" [NULL]"); continue; } builder.AppendLine($" Length: {entry.Length} (0x{entry.Length:X})"); builder.AppendLine($" Revision: {entry.Revision} (0x{entry.Revision:X})"); builder.AppendLine($" Certificate type: {entry.CertificateType} (0x{entry.CertificateType:X})"); builder.AppendLine(); if (entry.CertificateType == SabreTools.Models.PortableExecutable.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 == null ? "[NULL]" : BitConverter.ToString(entry.Certificate).Replace("-", string.Empty))}"); } 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 == null ? "[NULL]" : BitConverter.ToString(entry.Certificate).Replace("-", string.Empty))}"); } catch { builder.AppendLine($" [DATA TOO LARGE TO FORMAT]"); } } builder.AppendLine(); } } builder.AppendLine(); } /// /// Print delay-load directory table information /// /// StringBuilder to append information to private void PrintDelayLoadDirectoryTable(StringBuilder builder) { builder.AppendLine(" Delay-Load Directory Table Information:"); builder.AppendLine(" -------------------------"); if (OH_DelayImportDescriptor == null || OH_DelayImportDescriptor.VirtualAddress == 0 || DelayLoadDirectoryTable == null) { builder.AppendLine(" No delay-load directory table items"); } else { builder.AppendLine($" Attributes: {DelayLoadDirectoryTable.Attributes} (0x{DelayLoadDirectoryTable.Attributes:X})"); builder.AppendLine($" Name RVA: {DelayLoadDirectoryTable.Name} (0x{DelayLoadDirectoryTable.Name:X})"); builder.AppendLine($" Module handle: {DelayLoadDirectoryTable.ModuleHandle} (0x{DelayLoadDirectoryTable.ModuleHandle:X})"); builder.AppendLine($" Delay import address table RVA: {DelayLoadDirectoryTable.DelayImportAddressTable} (0x{DelayLoadDirectoryTable.DelayImportAddressTable:X})"); builder.AppendLine($" Delay import name table RVA: {DelayLoadDirectoryTable.DelayImportNameTable} (0x{DelayLoadDirectoryTable.DelayImportNameTable:X})"); builder.AppendLine($" Bound delay import table RVA: {DelayLoadDirectoryTable.BoundDelayImportTable} (0x{DelayLoadDirectoryTable.BoundDelayImportTable:X})"); builder.AppendLine($" Unload delay import table RVA: {DelayLoadDirectoryTable.UnloadDelayImportTable} (0x{DelayLoadDirectoryTable.UnloadDelayImportTable:X})"); builder.AppendLine($" Timestamp: {DelayLoadDirectoryTable.TimeStamp} (0x{DelayLoadDirectoryTable.TimeStamp:X})"); } builder.AppendLine(); } /// /// Print base relocation table information /// /// StringBuilder to append information to private void PrintBaseRelocationTable(StringBuilder builder) { builder.AppendLine(" Base Relocation Table Information:"); builder.AppendLine(" -------------------------"); if (OH_BaseRelocationTable == null || OH_BaseRelocationTable.VirtualAddress == 0 || BaseRelocationTable == null) { builder.AppendLine(" No base relocation table items"); } else { for (int i = 0; i < BaseRelocationTable.Length; i++) { var baseRelocationTableEntry = BaseRelocationTable[i]; builder.AppendLine($" Base Relocation Table Entry {i}"); if (baseRelocationTableEntry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Page RVA: {baseRelocationTableEntry.PageRVA} (0x{baseRelocationTableEntry.PageRVA:X})"); builder.AppendLine($" Page physical address: {baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Block size: {baseRelocationTableEntry.BlockSize} (0x{baseRelocationTableEntry.BlockSize:X})"); 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"); } else { 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($" Offset: {typeOffsetFieldEntry.Offset} (0x{typeOffsetFieldEntry.Offset:X})"); } } } } builder.AppendLine(); } /// /// Print debug table information /// /// StringBuilder to append information to private void PrintDebugTable(StringBuilder builder) { builder.AppendLine(" Debug Table Information:"); builder.AppendLine(" -------------------------"); if (OH_Debug == null || OH_Debug.VirtualAddress == 0 || DebugTable?.DebugDirectoryTable == null) { builder.AppendLine(" No debug table items"); } else { // TODO: If more sections added, model this after the Export Table for (int i = 0; i < DebugTable.DebugDirectoryTable.Length; i++) { var debugDirectoryEntry = DebugTable.DebugDirectoryTable[i]; builder.AppendLine($" Debug Directory Table Entry {i}"); if (debugDirectoryEntry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Characteristics: {debugDirectoryEntry.Characteristics} (0x{debugDirectoryEntry.Characteristics:X})"); builder.AppendLine($" Time/Date stamp: {debugDirectoryEntry.TimeDateStamp} (0x{debugDirectoryEntry.TimeDateStamp:X})"); builder.AppendLine($" Major version: {debugDirectoryEntry.MajorVersion} (0x{debugDirectoryEntry.MajorVersion:X})"); builder.AppendLine($" Minor version: {debugDirectoryEntry.MinorVersion} (0x{debugDirectoryEntry.MinorVersion:X})"); builder.AppendLine($" Debug type: {debugDirectoryEntry.DebugType} (0x{debugDirectoryEntry.DebugType:X})"); builder.AppendLine($" Size of data: {debugDirectoryEntry.SizeOfData} (0x{debugDirectoryEntry.SizeOfData:X})"); builder.AppendLine($" Address of raw data: {debugDirectoryEntry.AddressOfRawData} (0x{debugDirectoryEntry.AddressOfRawData:X})"); builder.AppendLine($" Pointer to raw data: {debugDirectoryEntry.PointerToRawData} (0x{debugDirectoryEntry.PointerToRawData:X})"); } } builder.AppendLine(); } /// /// Print export table information /// /// StringBuilder to append information to private void PrintExportTable(StringBuilder builder) { builder.AppendLine(" Export Table Information:"); builder.AppendLine(" -------------------------"); if (OH_ExportTable == null || OH_ExportTable.VirtualAddress == 0 || ExportTable?.ExportDirectoryTable == null) { builder.AppendLine(" No export table items"); } else { builder.AppendLine(); builder.AppendLine(" Export Directory Table Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine($" Export flags: {ExportTable.ExportDirectoryTable.ExportFlags} (0x{ExportTable.ExportDirectoryTable.ExportFlags:X})"); builder.AppendLine($" Time/Date stamp: {ExportTable.ExportDirectoryTable.TimeDateStamp} (0x{ExportTable.ExportDirectoryTable.TimeDateStamp:X})"); builder.AppendLine($" Major version: {ExportTable.ExportDirectoryTable.MajorVersion} (0x{ExportTable.ExportDirectoryTable.MajorVersion:X})"); builder.AppendLine($" Minor version: {ExportTable.ExportDirectoryTable.MinorVersion} (0x{ExportTable.ExportDirectoryTable.MinorVersion:X})"); builder.AppendLine($" Name RVA: {ExportTable.ExportDirectoryTable.NameRVA} (0x{ExportTable.ExportDirectoryTable.NameRVA:X})"); builder.AppendLine($" Name: {ExportTable.ExportDirectoryTable.Name}"); builder.AppendLine($" Ordinal base: {ExportTable.ExportDirectoryTable.OrdinalBase} (0x{ExportTable.ExportDirectoryTable.OrdinalBase:X})"); builder.AppendLine($" Address table entries: {ExportTable.ExportDirectoryTable.AddressTableEntries} (0x{ExportTable.ExportDirectoryTable.AddressTableEntries:X})"); builder.AppendLine($" Number of name pointers: {ExportTable.ExportDirectoryTable.NumberOfNamePointers} (0x{ExportTable.ExportDirectoryTable.NumberOfNamePointers:X})"); builder.AppendLine($" Export address table RVA: {ExportTable.ExportDirectoryTable.ExportAddressTableRVA} (0x{ExportTable.ExportDirectoryTable.ExportAddressTableRVA:X})"); builder.AppendLine($" Name pointer table RVA: {ExportTable.ExportDirectoryTable.NamePointerRVA} (0x{ExportTable.ExportDirectoryTable.NamePointerRVA:X})"); builder.AppendLine($" Ordinal table RVA: {ExportTable.ExportDirectoryTable.OrdinalTableRVA} (0x{ExportTable.ExportDirectoryTable.OrdinalTableRVA:X})"); builder.AppendLine(); builder.AppendLine(" Export Address Table Information:"); builder.AppendLine(" -------------------------"); if (ExportTable.ExportAddressTable == null || ExportTable.ExportAddressTable.Length == 0) { builder.AppendLine(" No export address table items"); } else { for (int i = 0; i < ExportTable.ExportAddressTable.Length; i++) { var exportAddressTableEntry = ExportTable.ExportAddressTable[i]; builder.AppendLine($" Export Address Table Entry {i}"); if (exportAddressTableEntry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Export RVA / Forwarder RVA: {exportAddressTableEntry.ExportRVA} (0x{exportAddressTableEntry.ExportRVA:X})"); } } builder.AppendLine(); builder.AppendLine(" Name Pointer Table Information:"); builder.AppendLine(" -------------------------"); if (ExportTable.NamePointerTable?.Pointers == null || ExportTable.NamePointerTable.Pointers.Length == 0) { builder.AppendLine(" No name pointer table items"); } else { for (int i = 0; i < ExportTable.NamePointerTable.Pointers.Length; i++) { var namePointerTableEntry = ExportTable.NamePointerTable.Pointers[i]; builder.AppendLine($" Name Pointer Table Entry {i}"); builder.AppendLine($" Pointer: {namePointerTableEntry} (0x{namePointerTableEntry:X})"); } } builder.AppendLine(); builder.AppendLine(" Ordinal Table Information:"); builder.AppendLine(" -------------------------"); if (ExportTable.OrdinalTable?.Indexes == null || ExportTable.OrdinalTable.Indexes.Length == 0) { builder.AppendLine(" No ordinal table items"); } else { for (int i = 0; i < ExportTable.OrdinalTable.Indexes.Length; i++) { var ordinalTableEntry = ExportTable.OrdinalTable.Indexes[i]; builder.AppendLine($" Ordinal Table Entry {i}"); builder.AppendLine($" Index: {ordinalTableEntry} (0x{ordinalTableEntry:X})"); } } builder.AppendLine(); builder.AppendLine(" Export Name Table Information:"); builder.AppendLine(" -------------------------"); if (ExportTable.ExportNameTable?.Strings == null || ExportTable.ExportNameTable.Strings.Length == 0) { builder.AppendLine(" No export name table items"); } else { for (int i = 0; i < ExportTable.ExportNameTable.Strings.Length; i++) { var exportNameTableEntry = ExportTable.ExportNameTable.Strings[i]; builder.AppendLine($" Export Name Table Entry {i}"); builder.AppendLine($" String: {exportNameTableEntry}"); } } } builder.AppendLine(); } /// /// Print import table information /// /// StringBuilder to append information to private void PrintImportTable(StringBuilder builder) { builder.AppendLine(" Import Table Information:"); builder.AppendLine(" -------------------------"); if (OH_ImportTable == null || OH_ImportTable.VirtualAddress == 0 || ImportTable == null) { builder.AppendLine(" No import table items"); } else { builder.AppendLine(); builder.AppendLine(" Import Directory Table Information:"); builder.AppendLine(" -------------------------"); if (ImportTable.ImportDirectoryTable == null || ImportTable.ImportDirectoryTable.Length == 0) { builder.AppendLine(" No import directory table items"); } else { for (int i = 0; i < ImportTable.ImportDirectoryTable.Length; i++) { var importDirectoryTableEntry = ImportTable.ImportDirectoryTable[i]; builder.AppendLine($" Import Directory Table Entry {i}"); if (importDirectoryTableEntry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Import lookup table RVA: {importDirectoryTableEntry.ImportLookupTableRVA} (0x{importDirectoryTableEntry.ImportLookupTableRVA:X})"); builder.AppendLine($" Import lookup table Physical Address: {importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); builder.AppendLine($" Time/Date stamp: {importDirectoryTableEntry.TimeDateStamp} (0x{importDirectoryTableEntry.TimeDateStamp:X})"); builder.AppendLine($" Forwarder chain: {importDirectoryTableEntry.ForwarderChain} (0x{importDirectoryTableEntry.ForwarderChain:X})"); builder.AppendLine($" Name RVA: {importDirectoryTableEntry.NameRVA} (0x{importDirectoryTableEntry.NameRVA:X})"); builder.AppendLine($" Name: {importDirectoryTableEntry.Name}"); builder.AppendLine($" Import address table RVA: {importDirectoryTableEntry.ImportAddressTableRVA} (0x{importDirectoryTableEntry.ImportAddressTableRVA:X})"); builder.AppendLine($" Import address table Physical Address: {importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); } } builder.AppendLine(); builder.AppendLine(" Import Lookup Tables Information:"); builder.AppendLine(" -------------------------"); if (ImportTable.ImportLookupTables == null || ImportTable.ImportLookupTables.Count == 0) { builder.AppendLine(" No import lookup tables"); } else { foreach (var kvp in ImportTable.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"); } else { 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($" Ordinal/Name flag: {importLookupTableEntry.OrdinalNameFlag} (0x{importLookupTableEntry.OrdinalNameFlag:X})"); if (importLookupTableEntry.OrdinalNameFlag) { builder.AppendLine($" Ordinal number: {importLookupTableEntry.OrdinalNumber} (0x{importLookupTableEntry.OrdinalNumber:X})"); } else { builder.AppendLine($" Hint/Name table RVA: {importLookupTableEntry.HintNameTableRVA} (0x{importLookupTableEntry.HintNameTableRVA:X})"); builder.AppendLine($" Hint/Name table Physical Address: {importLookupTableEntry.HintNameTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{importLookupTableEntry.HintNameTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); } } } } } builder.AppendLine(); builder.AppendLine(" Import Address Tables Information:"); builder.AppendLine(" -------------------------"); if (ImportTable.ImportAddressTables == null || ImportTable.ImportAddressTables.Count == 0) { builder.AppendLine(" No import address tables"); } else { foreach (var kvp in ImportTable.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"); } else { 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($" Ordinal/Name flag: {importAddressTableEntry.OrdinalNameFlag} (0x{importAddressTableEntry.OrdinalNameFlag:X})"); if (importAddressTableEntry.OrdinalNameFlag) { builder.AppendLine($" Ordinal number: {importAddressTableEntry.OrdinalNumber} (0x{importAddressTableEntry.OrdinalNumber:X})"); } else { builder.AppendLine($" Hint/Name table RVA: {importAddressTableEntry.HintNameTableRVA} (0x{importAddressTableEntry.HintNameTableRVA:X})"); builder.AppendLine($" Hint/Name table Physical Address: {importAddressTableEntry.HintNameTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty())} (0x{importAddressTableEntry.HintNameTableRVA.ConvertVirtualAddress(SectionTable ?? Array.Empty()):X})"); } } } } } builder.AppendLine(); builder.AppendLine(" Hint/Name Table Information:"); builder.AppendLine(" -------------------------"); if (ImportTable.HintNameTable == null || ImportTable.HintNameTable.Length == 0) { builder.AppendLine(" No hint/name table items"); } else { for (int i = 0; i < ImportTable.HintNameTable.Length; i++) { var hintNameTableEntry = ImportTable.HintNameTable[i]; builder.AppendLine($" Hint/Name Table Entry {i}"); if (hintNameTableEntry == null) { builder.AppendLine(" [NULL]"); continue; } builder.AppendLine($" Hint: {hintNameTableEntry.Hint} (0x{hintNameTableEntry.Hint:X})"); builder.AppendLine($" Name: {hintNameTableEntry.Name}"); } } } builder.AppendLine(); } /// /// Print resource directory table information /// /// StringBuilder to append information to private void PrintResourceDirectoryTable(StringBuilder builder) { builder.AppendLine(" Resource Directory Table Information:"); builder.AppendLine(" -------------------------"); if (OH_ResourceTable == null || OH_ResourceTable.VirtualAddress == 0 || ResourceDirectoryTable == null) { builder.AppendLine(" No resource directory table items"); } else { PrintResourceDirectoryTable(ResourceDirectoryTable, level: 0, types: new List(), builder); } builder.AppendLine(); } /// /// Pretty print the resource directory table information /// /// StringBuilder to append information to private static void PrintResourceDirectoryTable(SabreTools.Models.PortableExecutable.ResourceDirectoryTable table, int level, List types, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Table level: {level}"); builder.AppendLine($"{padding}Characteristics: {table.Characteristics} (0x{table.Characteristics:X})"); builder.AppendLine($"{padding}Time/Date stamp: {table.TimeDateStamp} (0x{table.TimeDateStamp:X})"); builder.AppendLine($"{padding}Major version: {table.MajorVersion} (0x{table.MajorVersion:X})"); builder.AppendLine($"{padding}Minor version: {table.MinorVersion} (0x{table.MinorVersion:X})"); builder.AppendLine($"{padding}Number of name entries: {table.NumberOfNameEntries} (0x{table.NumberOfNameEntries:X})"); builder.AppendLine($"{padding}Number of ID entries: {table.NumberOfIDEntries} (0x{table.NumberOfIDEntries:X})"); builder.AppendLine(); builder.AppendLine($"{padding}Entries"); builder.AppendLine($"{padding}-------------------------"); if (table.NumberOfNameEntries == 0 && table.NumberOfIDEntries == 0) { builder.AppendLine($"{padding}No entries"); builder.AppendLine(); } else { if (table.Entries == null) return; for (int i = 0; i < table.Entries.Length; i++) { var entry = table.Entries[i]; if (entry == null) continue; var newTypes = new List(types ?? new List()); if (entry.Name?.UnicodeString != null) newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString)); else newTypes.Add(entry.IntegerID); PrintResourceDirectoryEntry(entry, level + 1, newTypes, builder); } } } /// /// Pretty print the resource directory entry information /// /// StringBuilder to append information to private static void PrintResourceDirectoryEntry(SabreTools.Models.PortableExecutable.ResourceDirectoryEntry entry, int level, List types, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Item level: {level}"); if (entry.NameOffset != default) { builder.AppendLine($"{padding}Name offset: {entry.NameOffset} (0x{entry.NameOffset:X})"); builder.AppendLine($"{padding}Name ({entry.Name?.Length ?? 0}): {(entry.Name?.UnicodeString == null ? "[NULL]" : Encoding.UTF8.GetString(entry.Name.UnicodeString))}"); } else { builder.AppendLine($"{padding}Integer ID: {entry.IntegerID} (0x{entry.IntegerID:X})"); } if (entry.DataEntry != null) PrintResourceDataEntry(entry.DataEntry, level: level + 1, types, builder); else if (entry.Subdirectory != null) PrintResourceDirectoryTable(entry.Subdirectory, level: level + 1, types, builder); } /// /// Pretty print the resource data entry information /// /// StringBuilder to append information to private static void PrintResourceDataEntry(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, List types, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); // TODO: Use ordered list of base types to determine the shape of the data builder.AppendLine($"{padding}Base types: {(types == null ? "[NULL]" : string.Join(", ", types))}"); builder.AppendLine($"{padding}Entry level: {level}"); builder.AppendLine($"{padding}Data RVA: {entry.DataRVA} (0x{entry.DataRVA:X})"); builder.AppendLine($"{padding}Size: {entry.Size} (0x{entry.Size:X})"); builder.AppendLine($"{padding}Codepage: {entry.Codepage} (0x{entry.Codepage:X})"); builder.AppendLine($"{padding}Reserved: {entry.Reserved} (0x{entry.Reserved:X})"); // TODO: Print out per-type data if (types != null && types.Count > 0 && types[0] is uint resourceType) { switch ((SabreTools.Models.PortableExecutable.ResourceType)resourceType) { case SabreTools.Models.PortableExecutable.ResourceType.RT_CURSOR: PrintResourceRT_CURSOR(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_BITMAP: PrintResourceRT_BITMAP(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ICON: PrintResourceRT_ICON(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MENU: PrintResourceRT_MENU(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_DIALOG: PrintResourceRT_DIALOG(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_STRING: PrintResourceRT_STRING(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_FONTDIR: PrintResourceRT_FONTDIR(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_FONT: PrintResourceRT_FONT(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ACCELERATOR: PrintResourceRT_ACCELERATOR(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_RCDATA: PrintResourceRT_RCDATA(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MESSAGETABLE: PrintResourceRT_MESSAGETABLE(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR: PrintResourceRT_GROUP_CURSOR(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_ICON: PrintResourceRT_GROUP_ICON(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_VERSION: PrintResourceRT_VERSION(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_DLGINCLUDE: PrintResourceRT_DLGINCLUDE(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_PLUGPLAY: PrintResourceRT_PLUGPLAY(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_VXD: PrintResourceRT_VXD(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ANICURSOR: PrintResourceRT_ANICURSOR(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ANIICON: PrintResourceRT_ANIICON(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_HTML: PrintResourceRT_HTML(entry, level, builder); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MANIFEST: PrintResourceRT_MANIFEST(entry, level, builder); break; default: PrintResourceUNKNOWN(entry, level, types[0], builder); break; } } else if (types != null && types.Count > 0 && types[0] is string resourceString) { PrintResourceUNKNOWN(entry, level, types[0], builder); } builder.AppendLine(); } /// /// Print an RT_CURSOR resource /// /// StringBuilder to append information to private static void PrintResourceRT_CURSOR(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Hardware-dependent cursor resource found, not parsed yet"); } /// /// Print an RT_BITMAP resource /// /// StringBuilder to append information to private static void PrintResourceRT_BITMAP(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Bitmap resource found, not parsed yet"); } /// /// Print an RT_ICON resource /// /// StringBuilder to append information to private static void PrintResourceRT_ICON(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Hardware-dependent icon resource found, not parsed yet"); } /// /// Print an RT_MENU resource /// /// StringBuilder to append information to private static void PrintResourceRT_MENU(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.MenuResource menu = null; #else SabreTools.Models.PortableExecutable.MenuResource? menu = null; #endif try { menu = entry.AsMenu(); } catch { } if (menu == null) { builder.AppendLine($"{padding}Menu resource found, but malformed"); return; } if (menu.MenuHeader != null) { builder.AppendLine($"{padding}Version: {menu.MenuHeader.Version} (0x{menu.MenuHeader.Version:X})"); builder.AppendLine($"{padding}Header size: {menu.MenuHeader.HeaderSize} (0x{menu.MenuHeader.HeaderSize:X})"); builder.AppendLine(); builder.AppendLine($"{padding}Menu items"); builder.AppendLine($"{padding}-------------------------"); if (menu.MenuItems == null || menu.MenuItems.Length == 0) { builder.AppendLine($"{padding}No menu items"); return; } for (int i = 0; i < menu.MenuItems.Length; i++) { var menuItem = menu.MenuItems[i]; builder.AppendLine($"{padding}Menu item {i}"); if (menuItem == null) { builder.AppendLine($"{padding} [NULL]"); continue; } if (menuItem.NormalMenuText != null) { builder.AppendLine($"{padding} Resource info: {menuItem.NormalResInfo} (0x{menuItem.NormalResInfo:X})"); builder.AppendLine($"{padding} Menu text: {menuItem.NormalMenuText} (0x{menuItem.NormalMenuText:X})"); } else { builder.AppendLine($"{padding} Item type: {menuItem.PopupItemType} (0x{menuItem.PopupItemType:X})"); builder.AppendLine($"{padding} State: {menuItem.PopupState} (0x{menuItem.PopupState:X})"); builder.AppendLine($"{padding} ID: {menuItem.PopupID} (0x{menuItem.PopupID:X})"); builder.AppendLine($"{padding} Resource info: {menuItem.PopupResInfo} (0x{menuItem.PopupResInfo:X})"); builder.AppendLine($"{padding} Menu text: {menuItem.PopupMenuText} (0x{menuItem.PopupMenuText:X})"); } } } else if (menu.ExtendedMenuHeader != null) { builder.AppendLine($"{padding}Version: {menu.ExtendedMenuHeader.Version} (0x{menu.ExtendedMenuHeader.Version:X})"); builder.AppendLine($"{padding}Offset: {menu.ExtendedMenuHeader.Offset} (0x{menu.ExtendedMenuHeader.Offset:X})"); builder.AppendLine($"{padding}Help ID: {menu.ExtendedMenuHeader.HelpID} (0x{menu.ExtendedMenuHeader.HelpID:X})"); builder.AppendLine(); builder.AppendLine($"{padding}Menu items"); builder.AppendLine($"{padding}-------------------------"); if (menu.ExtendedMenuHeader.Offset == 0 || menu.ExtendedMenuItems == null || menu.ExtendedMenuItems.Length == 0) { builder.AppendLine($"{padding}No menu items"); return; } for (int i = 0; i < menu.ExtendedMenuItems.Length; i++) { var menuItem = menu.ExtendedMenuItems[i]; builder.AppendLine($"{padding}Menu item {i}"); if (menuItem == null) { builder.AppendLine($"{padding} [NULL]"); continue; } builder.AppendLine($"{padding} Item type: {menuItem.ItemType} (0x{menuItem.ItemType:X})"); builder.AppendLine($"{padding} State: {menuItem.State} (0x{menuItem.State:X})"); builder.AppendLine($"{padding} ID: {menuItem.ID} (0x{menuItem.ID:X})"); builder.AppendLine($"{padding} Flags: {menuItem.Flags} (0x{menuItem.Flags:X})"); builder.AppendLine($"{padding} Menu text: {menuItem.MenuText} (0x{menuItem.MenuText:X})"); } } else { builder.AppendLine($"{padding}Menu resource found, but malformed"); } } /// /// Print an RT_DIALOG resource /// /// StringBuilder to append information to private static void PrintResourceRT_DIALOG(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.DialogBoxResource dialogBox = null; #else SabreTools.Models.PortableExecutable.DialogBoxResource? dialogBox = null; #endif try { dialogBox = entry.AsDialogBox(); } catch { } if (dialogBox == null) { builder.AppendLine($"{padding}Dialog box resource found, but malformed"); return; } if (dialogBox.DialogTemplate != null) { builder.AppendLine($"{padding}Style: {dialogBox.DialogTemplate.Style} (0x{dialogBox.DialogTemplate.Style:X})"); builder.AppendLine($"{padding}Extended style: {dialogBox.DialogTemplate.ExtendedStyle} (0x{dialogBox.DialogTemplate.ExtendedStyle:X})"); builder.AppendLine($"{padding}Item count: {dialogBox.DialogTemplate.ItemCount} (0x{dialogBox.DialogTemplate.ItemCount:X})"); builder.AppendLine($"{padding}X-coordinate of upper-left corner: {dialogBox.DialogTemplate.PositionX} (0x{dialogBox.DialogTemplate.PositionX:X})"); builder.AppendLine($"{padding}Y-coordinate of upper-left corner: {dialogBox.DialogTemplate.PositionY} (0x{dialogBox.DialogTemplate.PositionY:X})"); builder.AppendLine($"{padding}Width of the dialog box: {dialogBox.DialogTemplate.WidthX} (0x{dialogBox.DialogTemplate.WidthX:X})"); builder.AppendLine($"{padding}Height of the dialog box: {dialogBox.DialogTemplate.HeightY} (0x{dialogBox.DialogTemplate.HeightY:X})"); builder.AppendLine($"{padding}Menu resource: {dialogBox.DialogTemplate.MenuResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Menu resource ordinal: {dialogBox.DialogTemplate.MenuResourceOrdinal} (0x{dialogBox.DialogTemplate.MenuResourceOrdinal:X})"); builder.AppendLine($"{padding}Class resource: {dialogBox.DialogTemplate.ClassResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Class resource ordinal: {dialogBox.DialogTemplate.ClassResourceOrdinal} (0x{dialogBox.DialogTemplate.ClassResourceOrdinal:X})"); builder.AppendLine($"{padding}Title resource: {dialogBox.DialogTemplate.TitleResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Point size value: {dialogBox.DialogTemplate.PointSizeValue} (0x{dialogBox.DialogTemplate.PointSizeValue:X})"); builder.AppendLine($"{padding}Typeface: {dialogBox.DialogTemplate.Typeface ?? "[EMPTY]"}"); builder.AppendLine(); builder.AppendLine($"{padding}Dialog item templates"); builder.AppendLine($"{padding}-------------------------"); if (dialogBox.DialogTemplate.ItemCount == 0 || dialogBox.DialogItemTemplates == null || dialogBox.DialogItemTemplates.Length == 0) { builder.AppendLine($"{padding}No dialog item templates"); return; } for (int i = 0; i < dialogBox.DialogItemTemplates.Length; i++) { var dialogItemTemplate = dialogBox.DialogItemTemplates[i]; builder.AppendLine($"{padding}Dialog item template {i}"); if (dialogItemTemplate == null) { builder.AppendLine($"{padding} [NULL]"); continue; } builder.AppendLine($"{padding} Style: {dialogItemTemplate.Style} (0x{dialogItemTemplate.Style:X})"); builder.AppendLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle} (0x{dialogItemTemplate.ExtendedStyle:X})"); builder.AppendLine($"{padding} X-coordinate of upper-left corner: {dialogItemTemplate.PositionX} (0x{dialogItemTemplate.PositionX:X})"); builder.AppendLine($"{padding} Y-coordinate of upper-left corner: {dialogItemTemplate.PositionY} (0x{dialogItemTemplate.PositionY:X})"); builder.AppendLine($"{padding} Width of the control: {dialogItemTemplate.WidthX} (0x{dialogItemTemplate.WidthX:X})"); builder.AppendLine($"{padding} Height of the control: {dialogItemTemplate.HeightY} (0x{dialogItemTemplate.HeightY:X})"); builder.AppendLine($"{padding} ID: {dialogItemTemplate.ID} (0x{dialogItemTemplate.ID:X})"); builder.AppendLine($"{padding} Class resource: {dialogItemTemplate.ClassResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal} (0x{dialogItemTemplate.ClassResourceOrdinal:X})"); builder.AppendLine($"{padding} Title resource: {dialogItemTemplate.TitleResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding} Title resource ordinal: {dialogItemTemplate.TitleResourceOrdinal} (0x{dialogItemTemplate.TitleResourceOrdinal:X})"); builder.AppendLine($"{padding} Creation data size: {dialogItemTemplate.CreationDataSize} (0x{dialogItemTemplate.CreationDataSize:X})"); if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) builder.AppendLine($"{padding} Creation data: {(dialogItemTemplate.CreationData == null ? "[NULL]" : BitConverter.ToString(dialogItemTemplate.CreationData).Replace("-", string.Empty))}"); else builder.AppendLine($"{padding} Creation data: [EMPTY]"); } } else if (dialogBox.ExtendedDialogTemplate != null) { builder.AppendLine($"{padding}Version: {dialogBox.ExtendedDialogTemplate.Version} (0x{dialogBox.ExtendedDialogTemplate.Version:X})"); builder.AppendLine($"{padding}Signature: {dialogBox.ExtendedDialogTemplate.Signature} (0x{dialogBox.ExtendedDialogTemplate.Signature:X})"); builder.AppendLine($"{padding}Help ID: {dialogBox.ExtendedDialogTemplate.HelpID} (0x{dialogBox.ExtendedDialogTemplate.HelpID:X})"); builder.AppendLine($"{padding}Extended style: {dialogBox.ExtendedDialogTemplate.ExtendedStyle} (0x{dialogBox.ExtendedDialogTemplate.ExtendedStyle:X})"); builder.AppendLine($"{padding}Style: {dialogBox.ExtendedDialogTemplate.Style} (0x{dialogBox.ExtendedDialogTemplate.Style:X})"); builder.AppendLine($"{padding}Item count: {dialogBox.ExtendedDialogTemplate.DialogItems} (0x{dialogBox.ExtendedDialogTemplate.DialogItems:X})"); builder.AppendLine($"{padding}X-coordinate of upper-left corner: {dialogBox.ExtendedDialogTemplate.PositionX} (0x{dialogBox.ExtendedDialogTemplate.PositionX:X})"); builder.AppendLine($"{padding}Y-coordinate of upper-left corner: {dialogBox.ExtendedDialogTemplate.PositionY} (0x{dialogBox.ExtendedDialogTemplate.PositionY:X})"); builder.AppendLine($"{padding}Width of the dialog box: {dialogBox.ExtendedDialogTemplate.WidthX} (0x{dialogBox.ExtendedDialogTemplate.WidthX:X})"); builder.AppendLine($"{padding}Height of the dialog box: {dialogBox.ExtendedDialogTemplate.HeightY} (0x{dialogBox.ExtendedDialogTemplate.HeightY:X})"); builder.AppendLine($"{padding}Menu resource: {dialogBox.ExtendedDialogTemplate.MenuResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Menu resource ordinal: {dialogBox.ExtendedDialogTemplate.MenuResourceOrdinal} (0x{dialogBox.ExtendedDialogTemplate.MenuResourceOrdinal:X})"); builder.AppendLine($"{padding}Class resource: {dialogBox.ExtendedDialogTemplate.ClassResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Class resource ordinal: {dialogBox.ExtendedDialogTemplate.ClassResourceOrdinal} (0x{dialogBox.ExtendedDialogTemplate.ClassResourceOrdinal:X})"); builder.AppendLine($"{padding}Title resource: {dialogBox.ExtendedDialogTemplate.TitleResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding}Point size: {dialogBox.ExtendedDialogTemplate.PointSize} (0x{dialogBox.ExtendedDialogTemplate.PointSize:X})"); builder.AppendLine($"{padding}Weight: {dialogBox.ExtendedDialogTemplate.Weight} (0x{dialogBox.ExtendedDialogTemplate.Weight:X})"); builder.AppendLine($"{padding}Italic: {dialogBox.ExtendedDialogTemplate.Italic} (0x{dialogBox.ExtendedDialogTemplate.Italic:X})"); builder.AppendLine($"{padding}Character set: {dialogBox.ExtendedDialogTemplate.CharSet} (0x{dialogBox.ExtendedDialogTemplate.CharSet:X})"); builder.AppendLine($"{padding}Typeface: {dialogBox.ExtendedDialogTemplate.Typeface ?? "[EMPTY]"}"); builder.AppendLine(); builder.AppendLine($"{padding}Dialog item templates"); builder.AppendLine($"{padding}-------------------------"); if (dialogBox.ExtendedDialogTemplate.DialogItems == 0 || dialogBox.ExtendedDialogItemTemplates == null || dialogBox.ExtendedDialogItemTemplates.Length == 0) { builder.AppendLine($"{padding}No dialog item templates"); return; } for (int i = 0; i < dialogBox.ExtendedDialogItemTemplates.Length; i++) { var dialogItemTemplate = dialogBox.ExtendedDialogItemTemplates[i]; builder.AppendLine($"{padding}Dialog item template {i}"); if (dialogItemTemplate == null) { builder.AppendLine($"{padding} [NULL]"); continue; } builder.AppendLine($"{padding} Help ID: {dialogItemTemplate.HelpID} (0x{dialogItemTemplate.HelpID:X})"); builder.AppendLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle} (0x{dialogItemTemplate.ExtendedStyle:X})"); builder.AppendLine($"{padding} Style: {dialogItemTemplate.Style} (0x{dialogItemTemplate.Style:X})"); builder.AppendLine($"{padding} X-coordinate of upper-left corner: {dialogItemTemplate.PositionX} (0x{dialogItemTemplate.PositionX:X})"); builder.AppendLine($"{padding} Y-coordinate of upper-left corner: {dialogItemTemplate.PositionY} (0x{dialogItemTemplate.PositionY:X})"); builder.AppendLine($"{padding} Width of the control: {dialogItemTemplate.WidthX} (0x{dialogItemTemplate.WidthX:X})"); builder.AppendLine($"{padding} Height of the control: {dialogItemTemplate.HeightY} (0x{dialogItemTemplate.HeightY:X})"); builder.AppendLine($"{padding} ID: {dialogItemTemplate.ID} (0x{dialogItemTemplate.ID:X})"); builder.AppendLine($"{padding} Class resource: {dialogItemTemplate.ClassResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal} (0x{dialogItemTemplate.ClassResourceOrdinal:X})"); builder.AppendLine($"{padding} Title resource: {dialogItemTemplate.TitleResource ?? "[EMPTY]"}"); builder.AppendLine($"{padding} Title resource ordinal: {dialogItemTemplate.TitleResourceOrdinal} (0x{dialogItemTemplate.TitleResourceOrdinal:X})"); builder.AppendLine($"{padding} Creation data size: {dialogItemTemplate.CreationDataSize} (0x{dialogItemTemplate.CreationDataSize:X})"); if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) builder.AppendLine($"{padding} Creation data: {(dialogItemTemplate.CreationData == null ? "[NULL]" : BitConverter.ToString(dialogItemTemplate.CreationData).Replace("-", string.Empty))}"); else builder.AppendLine($"{padding} Creation data: [EMPTY]"); } } else { builder.AppendLine($"{padding}Dialog box resource found, but malformed"); } } /// /// Print an RT_STRING resource /// /// StringBuilder to append information to private static void PrintResourceRT_STRING(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 Dictionary stringTable = null; #else Dictionary? stringTable = null; #endif try { stringTable = entry.AsStringTable(); } catch { } if (stringTable == null) { builder.AppendLine($"{padding}String table resource found, but malformed"); return; } foreach (var kvp in stringTable) { int index = kvp.Key; #if NET48 string stringValue = kvp.Value; #else string? stringValue = kvp.Value; #endif builder.AppendLine($"{padding}String entry {index}: {stringValue}"); } } /// /// Print an RT_FONTDIR resource /// /// StringBuilder to append information to private static void PrintResourceRT_FONTDIR(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Font directory resource found, not parsed yet"); } /// /// Print an RT_FONT resource /// /// StringBuilder to append information to private static void PrintResourceRT_FONT(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Font resource found, not parsed yet"); } /// /// Print an RT_ACCELERATOR resource /// /// StringBuilder to append information to private static void PrintResourceRT_ACCELERATOR(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.AcceleratorTableEntry[] acceleratorTable = null; #else SabreTools.Models.PortableExecutable.AcceleratorTableEntry[]? acceleratorTable = null; #endif try { acceleratorTable = entry.AsAcceleratorTableResource(); } catch { } if (acceleratorTable == null) { builder.AppendLine($"{padding}Accelerator table resource found, but malformed"); return; } for (int i = 0; i < acceleratorTable.Length; i++) { var acceleratorTableEntry = acceleratorTable[i]; builder.AppendLine($"{padding}Accelerator Table Entry {i}:"); builder.AppendLine($"{padding} Flags: {acceleratorTableEntry.Flags} (0x{acceleratorTableEntry.Flags:X})"); builder.AppendLine($"{padding} Ansi: {acceleratorTableEntry.Ansi} (0x{acceleratorTableEntry.Ansi:X})"); builder.AppendLine($"{padding} Id: {acceleratorTableEntry.Id} (0x{acceleratorTableEntry.Id:X})"); builder.AppendLine($"{padding} Padding: {acceleratorTableEntry.Padding} (0x{acceleratorTableEntry.Padding:X})"); } } /// /// Print an RT_RCDATA resource /// /// StringBuilder to append information to private static void PrintResourceRT_RCDATA(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Application-defined resource found, not parsed yet"); // Then print the data, if needed if (entry.Data == null) { builder.AppendLine($"{padding}Data: [NULL] (This may indicate a very large resource)"); } else { int offset = 0; #if NET48 byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); #else byte[]? magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); #endif if (magic == null) { // No-op } else if (magic[0] == 0x4D && magic[1] == 0x5A) { builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately } else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54) { builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately } else { builder.AppendLine($"{padding}Data: {(magic == null ? "[NULL]" : BitConverter.ToString(magic).Replace('-', ' '))} ..."); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (Byte Data): {(entry.Data == null ? "[NULL]" : BitConverter.ToString(entry.Data).Replace('-', ' '))}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (ASCII): {Encoding.ASCII.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (UTF-8): {Encoding.UTF8.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (Unicode): {Encoding.Unicode.GetString(entry.Data)}"); } } } /// /// Print an RT_MESSAGETABLE resource /// /// StringBuilder to append information to private static void PrintResourceRT_MESSAGETABLE(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.MessageResourceData messageTable = null; #else SabreTools.Models.PortableExecutable.MessageResourceData? messageTable = null; #endif try { messageTable = entry.AsMessageResourceData(); } catch { } if (messageTable == null) { builder.AppendLine($"{padding}Message resource data found, but malformed"); return; } builder.AppendLine($"{padding}Number of blocks: {messageTable.NumberOfBlocks} (0x{messageTable.NumberOfBlocks:X})"); builder.AppendLine(); builder.AppendLine($"{padding}Message resource blocks"); builder.AppendLine($"{padding}-------------------------"); if (messageTable.NumberOfBlocks == 0 || messageTable.Blocks == null || messageTable.Blocks.Length == 0) { builder.AppendLine($"{padding}No message resource blocks"); } else { for (int i = 0; i < messageTable.Blocks.Length; i++) { var messageResourceBlock = messageTable.Blocks[i]; builder.AppendLine($"{padding}Message resource block {i}"); if (messageResourceBlock == null) { builder.AppendLine($"{padding} [NULL]"); continue; } builder.AppendLine($"{padding} Low ID: {messageResourceBlock.LowId} (0x{messageResourceBlock.LowId:X})"); builder.AppendLine($"{padding} High ID: {messageResourceBlock.HighId} (0x{messageResourceBlock.HighId:X})"); builder.AppendLine($"{padding} Offset to entries: {messageResourceBlock.OffsetToEntries} (0x{messageResourceBlock.OffsetToEntries:X})"); } } builder.AppendLine(); builder.AppendLine($"{padding}Message resource entries"); builder.AppendLine($"{padding}-------------------------"); if (messageTable.Entries == null || messageTable.Entries.Count == 0) { builder.AppendLine($"{padding}No message resource entries"); } else { foreach (var kvp in messageTable.Entries) { uint index = kvp.Key; var messageResourceEntry = kvp.Value; builder.AppendLine($"{padding}Message resource entry {index}"); if (messageResourceEntry == null) { builder.AppendLine($"{padding} [NULL]"); continue; } builder.AppendLine($"{padding} Length: {messageResourceEntry.Length} (0x{messageResourceEntry.Length:X})"); builder.AppendLine($"{padding} Flags: {messageResourceEntry.Flags} (0x{messageResourceEntry.Flags:X})"); builder.AppendLine($"{padding} Text: {messageResourceEntry.Text}"); } } } /// /// Print an RT_GROUP_CURSOR resource /// /// StringBuilder to append information to private static void PrintResourceRT_GROUP_CURSOR(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Hardware-independent cursor resource found, not parsed yet"); } /// /// Print an RT_GROUP_ICON resource /// /// StringBuilder to append information to private static void PrintResourceRT_GROUP_ICON(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Hardware-independent icon resource found, not parsed yet"); } /// /// Print an RT_VERSION resource /// /// StringBuilder to append information to private static void PrintResourceRT_VERSION(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.VersionInfo versionInfo = null; #else SabreTools.Models.PortableExecutable.VersionInfo? versionInfo = null; #endif try { versionInfo = entry.AsVersionInfo(); } catch { } if (versionInfo == null) { builder.AppendLine($"{padding}Version info resource found, but malformed"); return; } builder.AppendLine($"{padding}Length: {versionInfo.Length} (0x{versionInfo.Length:X})"); builder.AppendLine($"{padding}Value length: {versionInfo.ValueLength} (0x{versionInfo.ValueLength:X})"); builder.AppendLine($"{padding}Resource type: {versionInfo.ResourceType} (0x{versionInfo.ResourceType:X})"); builder.AppendLine($"{padding}Key: {versionInfo.Key}"); if (versionInfo.ValueLength != 0 && versionInfo.Value != null) { builder.AppendLine($"{padding}[Fixed File Info] Signature: {versionInfo.Value.Signature} (0x{versionInfo.Value.Signature:X})"); builder.AppendLine($"{padding}[Fixed File Info] Struct version: {versionInfo.Value.StrucVersion} (0x{versionInfo.Value.StrucVersion:X})"); builder.AppendLine($"{padding}[Fixed File Info] File version (MS): {versionInfo.Value.FileVersionMS} (0x{versionInfo.Value.FileVersionMS:X})"); builder.AppendLine($"{padding}[Fixed File Info] File version (LS): {versionInfo.Value.FileVersionLS} (0x{versionInfo.Value.FileVersionLS:X})"); builder.AppendLine($"{padding}[Fixed File Info] Product version (MS): {versionInfo.Value.ProductVersionMS} (0x{versionInfo.Value.ProductVersionMS:X})"); builder.AppendLine($"{padding}[Fixed File Info] Product version (LS): {versionInfo.Value.ProductVersionLS} (0x{versionInfo.Value.ProductVersionLS:X})"); builder.AppendLine($"{padding}[Fixed File Info] File flags mask: {versionInfo.Value.FileFlagsMask} (0x{versionInfo.Value.FileFlagsMask:X})"); builder.AppendLine($"{padding}[Fixed File Info] File flags: {versionInfo.Value.FileFlags} (0x{versionInfo.Value.FileFlags:X})"); builder.AppendLine($"{padding}[Fixed File Info] File OS: {versionInfo.Value.FileOS} (0x{versionInfo.Value.FileOS:X})"); builder.AppendLine($"{padding}[Fixed File Info] Type: {versionInfo.Value.FileType} (0x{versionInfo.Value.FileType:X})"); builder.AppendLine($"{padding}[Fixed File Info] Subtype: {versionInfo.Value.FileSubtype} (0x{versionInfo.Value.FileSubtype:X})"); builder.AppendLine($"{padding}[Fixed File Info] File date (MS): {versionInfo.Value.FileDateMS} (0x{versionInfo.Value.FileDateMS:X})"); builder.AppendLine($"{padding}[Fixed File Info] File date (LS): {versionInfo.Value.FileDateLS} (0x{versionInfo.Value.FileDateLS:X})"); } if (versionInfo.StringFileInfo != null) { builder.AppendLine($"{padding}[String File Info] Length: {versionInfo.StringFileInfo.Length} (0x{versionInfo.StringFileInfo.Length:X})"); builder.AppendLine($"{padding}[String File Info] Value length: {versionInfo.StringFileInfo.ValueLength} (0x{versionInfo.StringFileInfo.ValueLength:X})"); builder.AppendLine($"{padding}[String File Info] Resource type: {versionInfo.StringFileInfo.ResourceType} (0x{versionInfo.StringFileInfo.ResourceType:X})"); builder.AppendLine($"{padding}[String File Info] Key: {versionInfo.StringFileInfo.Key}"); builder.AppendLine($"{padding}Children:"); builder.AppendLine($"{padding}-------------------------"); if (versionInfo.StringFileInfo.Children == null || versionInfo.StringFileInfo.Children.Length == 0) { builder.AppendLine($"{padding}No string file info children"); } else { for (int i = 0; i < versionInfo.StringFileInfo.Children.Length; i++) { var stringFileInfoChildEntry = versionInfo.StringFileInfo.Children[i]; if (stringFileInfoChildEntry == null) { builder.AppendLine($"{padding} [String Table {i}] [NULL]"); continue; } builder.AppendLine($"{padding} [String Table {i}] Length: {stringFileInfoChildEntry.Length} (0x{stringFileInfoChildEntry.Length:X})"); builder.AppendLine($"{padding} [String Table {i}] Value length: {stringFileInfoChildEntry.ValueLength} (0x{stringFileInfoChildEntry.ValueLength:X})"); builder.AppendLine($"{padding} [String Table {i}] ResourceType: {stringFileInfoChildEntry.ResourceType} (0x{stringFileInfoChildEntry.ResourceType:X})"); builder.AppendLine($"{padding} [String Table {i}] Key: {stringFileInfoChildEntry.Key}"); builder.AppendLine($"{padding} [String Table {i}] Children:"); builder.AppendLine($"{padding} -------------------------"); if (stringFileInfoChildEntry.Children == null || stringFileInfoChildEntry.Children.Length == 0) { builder.AppendLine($"{padding} No string table {i} children"); } else { for (int j = 0; j < stringFileInfoChildEntry.Children.Length; j++) { var stringDataEntry = stringFileInfoChildEntry.Children[j]; if (stringDataEntry == null) { builder.AppendLine($"{padding} [String Data {j}] [NULL]"); continue; } builder.AppendLine($"{padding} [String Data {j}] Length: {stringDataEntry.Length} (0x{stringDataEntry.Length:X})"); builder.AppendLine($"{padding} [String Data {j}] Value length: {stringDataEntry.ValueLength} (0x{stringDataEntry.ValueLength:X})"); builder.AppendLine($"{padding} [String Data {j}] ResourceType: {stringDataEntry.ResourceType} (0x{stringDataEntry.ResourceType:X})"); builder.AppendLine($"{padding} [String Data {j}] Key: {stringDataEntry.Key}"); builder.AppendLine($"{padding} [String Data {j}] Value: {stringDataEntry.Value}"); } } } } } if (versionInfo.VarFileInfo != null) { builder.AppendLine($"{padding}[Var File Info] Length: {versionInfo.VarFileInfo.Length} (0x{versionInfo.VarFileInfo.Length:X})"); builder.AppendLine($"{padding}[Var File Info] Value length: {versionInfo.VarFileInfo.ValueLength} (0x{versionInfo.VarFileInfo.ValueLength:X})"); builder.AppendLine($"{padding}[Var File Info] Resource type: {versionInfo.VarFileInfo.ResourceType} (0x{versionInfo.VarFileInfo.ResourceType:X})"); builder.AppendLine($"{padding}[Var File Info] Key: {versionInfo.VarFileInfo.Key}"); builder.AppendLine($"{padding}Children:"); builder.AppendLine($"{padding}-------------------------"); if (versionInfo.VarFileInfo.Children == null || versionInfo.VarFileInfo.Children.Length == 0) { builder.AppendLine($"{padding}No var file info children"); } else { for (int i = 0; i < versionInfo.VarFileInfo.Children.Length; i++) { var varFileInfoChildEntry = versionInfo.VarFileInfo.Children[i]; if (varFileInfoChildEntry == null) { builder.AppendLine($"{padding} [String Table {i}] [NULL]"); continue; } builder.AppendLine($"{padding} [String Table {i}] Length: {varFileInfoChildEntry.Length} (0x{varFileInfoChildEntry.Length:X})"); builder.AppendLine($"{padding} [String Table {i}] Value length: {varFileInfoChildEntry.ValueLength} (0x{varFileInfoChildEntry.ValueLength:X})"); builder.AppendLine($"{padding} [String Table {i}] ResourceType: {varFileInfoChildEntry.ResourceType} (0x{varFileInfoChildEntry.ResourceType:X})"); builder.AppendLine($"{padding} [String Table {i}] Key: {varFileInfoChildEntry.Key}"); builder.AppendLine($"{padding} [String Table {i}] Value: {(varFileInfoChildEntry.Value == null ? "[NULL]" : string.Join(", ", varFileInfoChildEntry.Value))}"); } } } } /// /// Print an RT_DLGINCLUDE resource /// /// StringBuilder to append information to private static void PrintResourceRT_DLGINCLUDE(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}External header resource found, not parsed yet"); } /// /// Print an RT_PLUGPLAY resource /// /// StringBuilder to append information to private static void PrintResourceRT_PLUGPLAY(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Plug and Play resource found, not parsed yet"); } /// /// Print an RT_VXD resource /// /// StringBuilder to append information to private static void PrintResourceRT_VXD(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}VXD found, not parsed yet"); } /// /// Print an RT_ANICURSOR resource /// /// StringBuilder to append information to private static void PrintResourceRT_ANICURSOR(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Animated cursor found, not parsed yet"); } /// /// Print an RT_ANIICON resource /// /// StringBuilder to append information to private static void PrintResourceRT_ANIICON(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}Animated icon found, not parsed yet"); } /// /// Print an RT_HTML resource /// /// StringBuilder to append information to private static void PrintResourceRT_HTML(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); builder.AppendLine($"{padding}HTML resource found, not parsed yet"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (ASCII): {Encoding.ASCII.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (UTF-8): {Encoding.UTF8.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (Unicode): {Encoding.Unicode.GetString(entry.Data)}"); } /// /// Print an RT_MANIFEST resource /// /// StringBuilder to append information to private static void PrintResourceRT_MANIFEST(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); #if NET48 SabreTools.Models.PortableExecutable.AssemblyManifest assemblyManifest = null; #else SabreTools.Models.PortableExecutable.AssemblyManifest? assemblyManifest = null; #endif try { assemblyManifest = entry.AsAssemblyManifest(); } catch { } if (assemblyManifest == null) { builder.AppendLine($"{padding}Assembly manifest found, but malformed"); return; } builder.AppendLine($"{padding}Manifest version: {assemblyManifest.ManifestVersion}"); if (assemblyManifest.AssemblyIdentities != null && assemblyManifest.AssemblyIdentities.Length > 0) { for (int i = 0; i < assemblyManifest.AssemblyIdentities.Length; i++) { var assemblyIdentity = assemblyManifest.AssemblyIdentities[i]; if (assemblyIdentity == null) { builder.AppendLine($"{padding} [Assembly Identity {i}] [NULL]"); continue; } builder.AppendLine($"{padding}[Assembly Identity {i}] Name: {assemblyIdentity.Name}"); builder.AppendLine($"{padding}[Assembly Identity {i}] Version: {assemblyIdentity.Version}"); builder.AppendLine($"{padding}[Assembly Identity {i}] Type: {assemblyIdentity.Type}"); builder.AppendLine($"{padding}[Assembly Identity {i}] Processor architecture: {assemblyIdentity.ProcessorArchitecture}"); builder.AppendLine($"{padding}[Assembly Identity {i}] Public key token: {assemblyIdentity.PublicKeyToken}"); builder.AppendLine($"{padding}[Assembly Identity {i}] Language: {assemblyIdentity.Language}"); } } if (assemblyManifest.Description != null) builder.AppendLine($"{padding}[Assembly Description] Value: {assemblyManifest.Description.Value}"); if (assemblyManifest.COMInterfaceExternalProxyStub != null && assemblyManifest.COMInterfaceExternalProxyStub.Length > 0) { for (int i = 0; i < assemblyManifest.COMInterfaceExternalProxyStub.Length; i++) { var comInterfaceExternalProxyStub = assemblyManifest.COMInterfaceExternalProxyStub[i]; if (comInterfaceExternalProxyStub == null) { builder.AppendLine($"{padding} [COM Interface External Proxy Stub {i}] [NULL]"); continue; } builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] IID: {comInterfaceExternalProxyStub.IID}"); builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] Name: {comInterfaceExternalProxyStub.Name}"); builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] TLBID: {comInterfaceExternalProxyStub.TLBID}"); builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] Number of methods: {comInterfaceExternalProxyStub.NumMethods}"); builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] Proxy stub (CLSID32): {comInterfaceExternalProxyStub.ProxyStubClsid32}"); builder.AppendLine($"{padding}[COM Interface External Proxy Stub {i}] Base interface: {comInterfaceExternalProxyStub.BaseInterface}"); } } if (assemblyManifest.Dependency != null && assemblyManifest.Dependency.Length > 0) { for (int i = 0; i < assemblyManifest.Dependency.Length; i++) { var dependency = assemblyManifest.Dependency[i]; if (dependency?.DependentAssembly != null) { if (dependency.DependentAssembly.AssemblyIdentity != null) { builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Name: {dependency.DependentAssembly.AssemblyIdentity.Name}"); builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Version: {dependency.DependentAssembly.AssemblyIdentity.Version}"); builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Type: {dependency.DependentAssembly.AssemblyIdentity.Type}"); builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Processor architecture: {dependency.DependentAssembly.AssemblyIdentity.ProcessorArchitecture}"); builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Public key token: {dependency.DependentAssembly.AssemblyIdentity.PublicKeyToken}"); builder.AppendLine($"{padding}[Dependency {i} Assembly Identity] Language: {dependency.DependentAssembly.AssemblyIdentity.Language}"); } if (dependency.DependentAssembly.BindingRedirect != null && dependency.DependentAssembly.BindingRedirect.Length > 0) { for (int j = 0; j < dependency.DependentAssembly.BindingRedirect.Length; j++) { var bindingRedirect = dependency.DependentAssembly.BindingRedirect[j]; if (bindingRedirect == null) { builder.AppendLine($"{padding}[Dependency {i} Binding Redirect {j}] [NULL]"); continue; } builder.AppendLine($"{padding}[Dependency {i} Binding Redirect {j}] Old version: {bindingRedirect.OldVersion}"); builder.AppendLine($"{padding}[Dependency {i} Binding Redirect {j}] New version: {bindingRedirect.NewVersion}"); } } } if (dependency != null) builder.AppendLine($"{padding}[Dependency {i}] Optional: {dependency.Optional}"); } } if (assemblyManifest.File != null && assemblyManifest.File.Length > 0) { for (int i = 0; i < assemblyManifest.File.Length; i++) { var file = assemblyManifest.File[i]; if (file == null) { builder.AppendLine($"{padding}[File {i}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i}] Name: {file.Name}"); builder.AppendLine($"{padding}[File {i}] Hash: {file.Hash}"); builder.AppendLine($"{padding}[File {i}] Hash algorithm: {file.HashAlgorithm}"); builder.AppendLine($"{padding}[File {i}] Size: {file.Size}"); if (file.COMClass != null && file.COMClass.Length > 0) { for (int j = 0; j < file.COMClass.Length; j++) { var comClass = file.COMClass[j]; if (comClass == null) { builder.AppendLine($"{padding}[File {i} COM Class {j}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i} COM Class {j}] CLSID: {comClass.CLSID}"); builder.AppendLine($"{padding}[File {i} COM Class {j}] Threading model: {comClass.ThreadingModel}"); builder.AppendLine($"{padding}[File {i} COM Class {j}] Prog ID: {comClass.ProgID}"); builder.AppendLine($"{padding}[File {i} COM Class {j}] TLBID: {comClass.TLBID}"); builder.AppendLine($"{padding}[File {i} COM Class {j}] Description: {comClass.Description}"); if (comClass.ProgIDs != null && comClass.ProgIDs.Length > 0) { for (int k = 0; k < comClass.ProgIDs.Length; k++) { var progId = comClass.ProgIDs[k]; if (progId == null) { builder.AppendLine($"{padding}[File {i} COM Class {j} Prog ID {k}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i} COM Class {j} Prog ID {k}] Value: {progId.Value}"); } } } } if (file.COMInterfaceProxyStub != null && file.COMInterfaceProxyStub.Length > 0) { for (int j = 0; j < file.COMInterfaceProxyStub.Length; j++) { var comInterfaceProxyStub = file.COMInterfaceProxyStub[j]; if (comInterfaceProxyStub == null) { builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] IID: {comInterfaceProxyStub.IID}"); builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] Name: {comInterfaceProxyStub.Name}"); builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] TLBID: {comInterfaceProxyStub.TLBID}"); builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] Number of methods: {comInterfaceProxyStub.NumMethods}"); builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] Proxy stub (CLSID32): {comInterfaceProxyStub.ProxyStubClsid32}"); builder.AppendLine($"{padding}[File {i} COM Interface Proxy Stub {j}] Base interface: {comInterfaceProxyStub.BaseInterface}"); } } if (file.Typelib != null && file.Typelib.Length > 0) { for (int j = 0; j < file.Typelib.Length; j++) { var typeLib = file.Typelib[j]; if (typeLib == null) { builder.AppendLine($"{padding}[File {i} Type Lib {j}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i} Type Lib {j}] TLBID: {typeLib.TLBID}"); builder.AppendLine($"{padding}[File {i} Type Lib {j}] Version: {typeLib.Version}"); builder.AppendLine($"{padding}[File {i} Type Lib {j}] Help directory: {typeLib.HelpDir}"); builder.AppendLine($"{padding}[File {i} Type Lib {j}] Resource ID: {typeLib.ResourceID}"); builder.AppendLine($"{padding}[File {i} Type Lib {j}] Flags: {typeLib.Flags}"); } } if (file.WindowClass != null && file.WindowClass.Length > 0) { for (int j = 0; j < file.WindowClass.Length; j++) { var windowClass = file.WindowClass[j]; if (windowClass == null) { builder.AppendLine($"{padding}[File {i} Window Class {j}] [NULL]"); continue; } builder.AppendLine($"{padding}[File {i} Window Class {j}] Versioned: {windowClass.Versioned}"); builder.AppendLine($"{padding}[File {i} Window Class {j}] Value: {windowClass.Value}"); } } } } if (assemblyManifest.EverythingElse != null && assemblyManifest.EverythingElse.Length > 0) { for (int i = 0; i < assemblyManifest.EverythingElse.Length; i++) { var thing = assemblyManifest.EverythingElse[i]; if (thing is XmlElement element) { builder.AppendLine($"{padding}Unparsed XML Element {i}: {element.OuterXml}"); } else { builder.AppendLine($"{padding}Unparsed Item {i}: {thing}"); } } } } /// /// Print an UNKNOWN or custom resource /// /// StringBuilder to append information to private static void PrintResourceUNKNOWN(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, int level, object resourceType, StringBuilder builder) { string padding = new string(' ', (level + 1) * 2); // Print the type first if (resourceType is uint numericType) builder.AppendLine($"{padding}Type {(SabreTools.Models.PortableExecutable.ResourceType)numericType} found, not parsed yet"); else if (resourceType is string stringType) builder.AppendLine($"{padding}Type {stringType} found, not parsed yet"); else builder.AppendLine($"{padding}Unknown type {resourceType} found, not parsed yet"); // Then print the data, if needed if (entry.Data == null) { builder.AppendLine($"{padding}Data: [NULL] (This may indicate a very large resource)"); } else { int offset = 0; #if NET48 byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); #else byte[]? magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16)); #endif if (magic == null) { // No-op } else if (magic[0] == 0x4D && magic[1] == 0x5A) { builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately } else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54) { builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately } else { builder.AppendLine($"{padding}Data: {(magic == null ? "[NULL]" : BitConverter.ToString(magic).Replace('-', ' '))} ..."); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (Byte Data): {(entry.Data == null ? "[NULL]" : BitConverter.ToString(entry.Data).Replace('-', ' '))}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (ASCII): {Encoding.ASCII.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (UTF-8): {Encoding.UTF8.GetString(entry.Data)}"); //if (entry.Data != null) // builder.AppendLine($"{padding}Value (Unicode): {Encoding.Unicode.GetString(entry.Data)}"); } } } #if NET6_0_OR_GREATER /// public override string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(_model, _jsonSerializerOptions); #endif #endregion #region Debug Data /// /// Find CodeView debug data by path /// /// Partial path to check for /// Enumerable of matching debug data #if NET48 public IEnumerable FindCodeViewDebugTableByPath(string path) #else public IEnumerable FindCodeViewDebugTableByPath(string path) #endif { // Ensure that we have the debug data cached if (DebugData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif var nb10Found = DebugData.Select(r => r.Value) .Select(r => r as SabreTools.Models.PortableExecutable.NB10ProgramDatabase) .Where(n => n != null) .Where(n => n?.PdbFileName?.Contains(path) == true) .Select(n => n as object); var rsdsFound = DebugData.Select(r => r.Value) .Select(r => r as SabreTools.Models.PortableExecutable.RSDSProgramDatabase) .Where(r => r != null) .Where(r => r?.PathAndFileName?.Contains(path) == true) .Select(r => r as object); return nb10Found.Concat(rsdsFound); } /// /// Find unparsed debug data by string value /// /// String value to check for /// Enumerable of matching debug data #if NET48 public IEnumerable FindGenericDebugTableByValue(string value) #else public IEnumerable FindGenericDebugTableByValue(string value) #endif { // Ensure that we have the resource data cached if (DebugData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif return DebugData.Select(r => r.Value) .Select(b => b as byte[]) .Where(b => b != null) .Where(b => { try { #if NET48 string arrayAsASCII = Encoding.ASCII.GetString(b); #else string? arrayAsASCII = Encoding.ASCII.GetString(b!); #endif if (arrayAsASCII.Contains(value)) return true; } catch { } try { #if NET48 string arrayAsUTF8 = Encoding.UTF8.GetString(b); #else string? arrayAsUTF8 = Encoding.UTF8.GetString(b!); #endif if (arrayAsUTF8.Contains(value)) return true; } catch { } try { #if NET48 string arrayAsUnicode = Encoding.Unicode.GetString(b); #else string? arrayAsUnicode = Encoding.Unicode.GetString(b!); #endif if (arrayAsUnicode.Contains(value)) return true; } catch { } return false; }); } #endregion #region Debug Parsing /// /// Parse the debug directory table information /// private void ParseDebugTable() { // If there is no debug table if (DebugTable?.DebugDirectoryTable == null) return; // Loop through all debug table entries for (int i = 0; i < DebugTable.DebugDirectoryTable.Length; i++) { var entry = DebugTable.DebugDirectoryTable[i]; if (entry == null) continue; uint address = entry.PointerToRawData; uint size = entry.SizeOfData; #if NET48 byte[] entryData = ReadFromDataSource((int)address, (int)size); #else byte[]? entryData = ReadFromDataSource((int)address, (int)size); #endif if (entryData == null) continue; // If we have CodeView debug data, try to parse it if (entry.DebugType == SabreTools.Models.PortableExecutable.DebugType.IMAGE_DEBUG_TYPE_CODEVIEW) { // Read the signature int offset = 0; uint signature = entryData.ReadUInt32(ref offset); // Reset the offset offset = 0; // NB10 if (signature == 0x3031424E) { var nb10ProgramDatabase = entryData.AsNB10ProgramDatabase(ref offset); if (nb10ProgramDatabase != null) { _debugData[i] = nb10ProgramDatabase; continue; } } // RSDS else if (signature == 0x53445352) { var rsdsProgramDatabase = entryData.AsRSDSProgramDatabase(ref offset); if (rsdsProgramDatabase != null) { _debugData[i] = rsdsProgramDatabase; continue; } } } else { _debugData[i] = entryData; } } } #endregion #region Resource Data /// /// Find dialog box resources by title /// /// Dialog box title to check for /// Enumerable of matching resources #if NET48 public IEnumerable FindDialogByTitle(string title) #else public IEnumerable FindDialogByTitle(string title) #endif { // Ensure that we have the resource data cached if (ResourceData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif return ResourceData.Select(r => r.Value) .Select(r => r as SabreTools.Models.PortableExecutable.DialogBoxResource) .Where(d => d != null) .Where(d => { return (d?.DialogTemplate?.TitleResource?.Contains(title) ?? false) || (d?.ExtendedDialogTemplate?.TitleResource?.Contains(title) ?? false); }); } /// /// Find dialog box resources by contained item title /// /// Dialog box item title to check for /// Enumerable of matching resources #if NET48 public IEnumerable FindDialogBoxByItemTitle(string title) #else public IEnumerable FindDialogBoxByItemTitle(string title) #endif { // Ensure that we have the resource data cached if (ResourceData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif return ResourceData.Select(r => r.Value) .Select(r => r as SabreTools.Models.PortableExecutable.DialogBoxResource) .Where(d => d != null) .Where(d => { if (d?.DialogItemTemplates != null) { return d.DialogItemTemplates .Where(dit => dit?.TitleResource != null) .Any(dit => dit?.TitleResource?.Contains(title) == true); } else if (d?.ExtendedDialogItemTemplates != null) { return d.ExtendedDialogItemTemplates .Where(edit => edit?.TitleResource != null) .Any(edit => edit?.TitleResource?.Contains(title) == true); } return false; }); } /// /// Find string table resources by contained string entry /// /// String entry to check for /// Enumerable of matching resources #if NET48 public IEnumerable> FindStringTableByEntry(string entry) #else public IEnumerable?> FindStringTableByEntry(string entry) #endif { // Ensure that we have the resource data cached if (ResourceData == null) #if NET48 return Enumerable.Empty>(); #else return Enumerable.Empty?>(); #endif return ResourceData.Select(r => r.Value) #if NET48 .Select(r => r as Dictionary) #else .Select(r => r as Dictionary) #endif .Where(st => st != null) .Where(st => st?.Select(kvp => kvp.Value)? .Any(s => s != null && s.Contains(entry)) == true); } /// /// Find unparsed resources by type name /// /// Type name to check for /// Enumerable of matching resources #if NET48 public IEnumerable FindResourceByNamedType(string typeName) #else public IEnumerable FindResourceByNamedType(string typeName) #endif { // Ensure that we have the resource data cached if (ResourceData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif return ResourceData.Where(kvp => kvp.Key.Contains(typeName)) .Select(kvp => kvp.Value as byte[]) .Where(b => b != null); } /// /// Find unparsed resources by string value /// /// String value to check for /// Enumerable of matching resources #if NET48 public IEnumerable FindGenericResource(string value) #else public IEnumerable FindGenericResource(string value) #endif { // Ensure that we have the resource data cached if (ResourceData == null) #if NET48 return Enumerable.Empty(); #else return Enumerable.Empty(); #endif return ResourceData.Select(r => r.Value) .Select(r => r as byte[]) .Where(b => b != null) .Where(b => { try { #if NET48 string arrayAsASCII = Encoding.ASCII.GetString(b); #else string? arrayAsASCII = Encoding.ASCII.GetString(b!); #endif if (arrayAsASCII.Contains(value)) return true; } catch { } try { #if NET48 string arrayAsUTF8 = Encoding.UTF8.GetString(b); #else string? arrayAsUTF8 = Encoding.UTF8.GetString(b!); #endif if (arrayAsUTF8.Contains(value)) return true; } catch { } try { #if NET48 string arrayAsUnicode = Encoding.Unicode.GetString(b); #else string? arrayAsUnicode = Encoding.Unicode.GetString(b!); #endif if (arrayAsUnicode.Contains(value)) return true; } catch { } return false; }); } #endregion #region Resource Parsing /// /// Parse the resource directory table information /// private void ParseResourceDirectoryTable(SabreTools.Models.PortableExecutable.ResourceDirectoryTable table, List types) { if (table?.Entries == null) return; for (int i = 0; i < table.Entries.Length; i++) { var entry = table.Entries[i]; if (entry == null) continue; var newTypes = new List(types ?? new List()); if (entry.Name?.UnicodeString != null) newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString)); else newTypes.Add(entry.IntegerID); ParseResourceDirectoryEntry(entry, newTypes); } } /// /// Parse the name resource directory entry information /// private void ParseResourceDirectoryEntry(SabreTools.Models.PortableExecutable.ResourceDirectoryEntry entry, List types) { if (entry.DataEntry != null) ParseResourceDataEntry(entry.DataEntry, types); else if (entry.Subdirectory != null) ParseResourceDirectoryTable(entry.Subdirectory, types); } /// /// Parse the resource data entry information /// /// /// When caching the version information and assembly manifest, this code assumes that there is only one of each /// of those resources in the entire exectuable. This means that only the last found version or manifest will /// ever be cached. /// private void ParseResourceDataEntry(SabreTools.Models.PortableExecutable.ResourceDataEntry entry, List types) { // Create the key and value objects string key = types == null ? $"UNKNOWN_{Guid.NewGuid()}" : string.Join(", ", types); #if NET48 object value = entry.Data; #else object? value = entry.Data; #endif // If we have a known resource type if (types != null && types.Count > 0 && types[0] is uint resourceType) { try { switch ((SabreTools.Models.PortableExecutable.ResourceType)resourceType) { case SabreTools.Models.PortableExecutable.ResourceType.RT_CURSOR: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_BITMAP: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ICON: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MENU: value = entry.AsMenu(); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_DIALOG: value = entry.AsDialogBox(); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_STRING: value = entry.AsStringTable(); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_FONTDIR: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_FONT: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ACCELERATOR: value = entry.AsAcceleratorTableResource(); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_RCDATA: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MESSAGETABLE: value = entry.AsMessageResourceData(); break; case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_ICON: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_VERSION: _versionInfo = entry.AsVersionInfo(); value = _versionInfo; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_DLGINCLUDE: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_PLUGPLAY: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_VXD: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ANICURSOR: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_ANIICON: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_HTML: value = entry.Data; break; case SabreTools.Models.PortableExecutable.ResourceType.RT_MANIFEST: _assemblyManifest = entry.AsAssemblyManifest(); value = _versionInfo; break; default: value = entry.Data; break; } } catch { // Fall back on byte array data for malformed items value = entry.Data; } } // If we have a custom resource type else if (types != null && types.Count > 0 && types[0] is string) { value = entry.Data; } // Add the key and value to the cache _resourceData[key] = value; } #endregion #region Sections /// /// Determine if a section is contained within the section table /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// True if the section is in the executable, false otherwise #if NET48 public bool ContainsSection(string sectionName, bool exact = false) #else public bool ContainsSection(string? sectionName, bool exact = false) #endif { // If no section name is provided if (sectionName == null) return false; // Get all section names first if (SectionNames == null) return false; // If we're checking exactly, return only exact matches if (exact) return SectionNames.Any(n => n.Equals(sectionName)); // Otherwise, check if section name starts with the value else return SectionNames.Any(n => n.StartsWith(sectionName)); } /// /// Get the section index corresponding to the entry point, if possible /// /// Section index on success, null on error public int FindEntryPointSectionIndex() { // If the section table is missing if (SectionTable == null) return -1; #if NET6_0_OR_GREATER // If the address is missing if (OH_AddressOfEntryPoint == null) return -1; #endif // If we don't have an entry point #if NET48 if (OH_AddressOfEntryPoint.ConvertVirtualAddress(SectionTable) == 0) return -1; // Otherwise, find the section it exists within return OH_AddressOfEntryPoint.ContainingSectionIndex(SectionTable); #else if (OH_AddressOfEntryPoint.Value.ConvertVirtualAddress(SectionTable) == 0) return -1; // Otherwise, find the section it exists within return OH_AddressOfEntryPoint.Value.ContainingSectionIndex(SectionTable .Where(sh => sh != null) .Cast() .ToArray()); #endif } /// /// Get the first section based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section data on success, null on error #if NET48 public SabreTools.Models.PortableExecutable.SectionHeader GetFirstSection(string name, bool exact = false) #else public SabreTools.Models.PortableExecutable.SectionHeader? GetFirstSection(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the first index of the section int index = Array.IndexOf(SectionNames, name); if (index == -1) return null; // Return the section return SectionTable[index]; } /// /// Get the last section based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section data on success, null on error #if NET48 public SabreTools.Models.PortableExecutable.SectionHeader GetLastSection(string name, bool exact = false) #else public SabreTools.Models.PortableExecutable.SectionHeader? GetLastSection(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the last index of the section int index = Array.LastIndexOf(SectionNames, name); if (index == -1) return null; // Return the section return SectionTable[index]; } /// /// Get the section based on index, if possible /// /// Index of the section to check for /// Section data on success, null on error #if NET48 public SabreTools.Models.PortableExecutable.SectionHeader GetSection(int index) #else public SabreTools.Models.PortableExecutable.SectionHeader? GetSection(int index) #endif { // If we have no sections if (SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (index < 0 || index >= SectionTable.Length) return null; // Return the section return SectionTable[index]; } /// /// Get the first section data based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section data on success, null on error #if NET48 public byte[] GetFirstSectionData(string name, bool exact = false) #else public byte[]? GetFirstSectionData(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the first index of the section int index = Array.IndexOf(SectionNames, name); return GetSectionData(index); } /// /// Get the last section data based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section data on success, null on error #if NET48 public byte[] GetLastSectionData(string name, bool exact = false) #else public byte[]? GetLastSectionData(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the last index of the section int index = Array.LastIndexOf(SectionNames, name); return GetSectionData(index); } /// /// Get the section data based on index, if possible /// /// Index of the section to check for /// Section data on success, null on error #if NET48 public byte[] GetSectionData(int index) #else public byte[]? GetSectionData(int index) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (index < 0 || index >= SectionTable.Length) return null; // Get the section data from the table var section = SectionTable[index]; if (section == null) return null; uint address = section.VirtualAddress.ConvertVirtualAddress(SectionTable); if (address == 0) return null; // Set the section size uint size = section.SizeOfRawData; lock (_sourceDataLock) { // Create the section data array if we have to if (_sectionData == null) _sectionData = new byte[SectionNames.Length][]; // If we already have cached data, just use that immediately if (_sectionData[index] != null) return _sectionData[index]; // Populate the raw section data based on the source #if NET48 byte[] sectionData = ReadFromDataSource((int)address, (int)size); #else byte[]? sectionData = ReadFromDataSource((int)address, (int)size); #endif // Cache and return the section data, even if null _sectionData[index] = sectionData; return sectionData; } } /// /// Get the first section strings based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section strings on success, null on error #if NET48 public List GetFirstSectionStrings(string name, bool exact = false) #else public List? GetFirstSectionStrings(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the first index of the section int index = Array.IndexOf(SectionNames, name); return GetSectionStrings(index); } /// /// Get the last section strings based on name, if possible /// /// Name of the section to check for /// True to enable exact matching of names, false for starts-with /// Section strings on success, null on error #if NET48 public List GetLastSectionStrings(string name, bool exact = false) #else public List? GetLastSectionStrings(string? name, bool exact = false) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (!ContainsSection(name, exact)) return null; // Get the last index of the section int index = Array.LastIndexOf(SectionNames, name); return GetSectionStrings(index); } /// /// Get the section strings based on index, if possible /// /// Index of the section to check for /// Section strings on success, null on error #if NET48 public List GetSectionStrings(int index) #else public List? GetSectionStrings(int index) #endif { // If we have no sections if (SectionNames == null || !SectionNames.Any() || SectionTable == null || !SectionTable.Any()) return null; // If the section doesn't exist if (index < 0 || index >= SectionTable.Length) return null; // Get the section data from the table var section = SectionTable[index]; if (section == null) return null; uint address = section.VirtualAddress.ConvertVirtualAddress(SectionTable); if (address == 0) return null; // Set the section size uint size = section.SizeOfRawData; lock (_sourceDataLock) { // Create the section string array if we have to if (_sectionStringData == null) _sectionStringData = new List[SectionNames.Length]; // If we already have cached data, just use that immediately if (_sectionStringData[index] != null) return _sectionStringData[index]; // Populate the section string data based on the source #if NET48 List sectionStringData = ReadStringsFromDataSource((int)address, (int)size); #else List? sectionStringData = ReadStringsFromDataSource((int)address, (int)size); #endif // Cache and return the section string data, even if null _sectionStringData[index] = sectionStringData; return sectionStringData; } } #endregion #region Tables /// /// Get the table data based on index, if possible /// /// Index of the table to check for /// Table data on success, null on error #if NET48 public byte[] GetTableData(int index) #else public byte[]? GetTableData(int index) #endif { // If the table doesn't exist if (index < 0 || index > 16) return null; // Get the virtual address and size from the entries uint virtualAddress = 0, size = 0; switch (index) { case 1: virtualAddress = OH_ExportTable?.VirtualAddress ?? 0; size = OH_ExportTable?.Size ?? 0; break; case 2: virtualAddress = OH_ImportTable?.VirtualAddress ?? 0; size = OH_ImportTable?.Size ?? 0; break; case 3: virtualAddress = OH_ResourceTable?.VirtualAddress ?? 0; size = OH_ResourceTable?.Size ?? 0; break; case 4: virtualAddress = OH_ExceptionTable?.VirtualAddress ?? 0; size = OH_ExceptionTable?.Size ?? 0; break; case 5: virtualAddress = OH_CertificateTable?.VirtualAddress ?? 0; size = OH_CertificateTable?.Size ?? 0; break; case 6: virtualAddress = OH_BaseRelocationTable?.VirtualAddress ?? 0; size = OH_BaseRelocationTable?.Size ?? 0; break; case 7: virtualAddress = OH_Debug?.VirtualAddress ?? 0; size = OH_Debug?.Size ?? 0; break; case 8: // Architecture Table virtualAddress = 0; size = 0; break; case 9: virtualAddress = OH_GlobalPtr?.VirtualAddress ?? 0; size = OH_GlobalPtr?.Size ?? 0; break; case 10: virtualAddress = OH_ThreadLocalStorageTable?.VirtualAddress ?? 0; size = OH_ThreadLocalStorageTable?.Size ?? 0; break; case 11: virtualAddress = OH_LoadConfigTable?.VirtualAddress ?? 0; size = OH_LoadConfigTable?.Size ?? 0; break; case 12: virtualAddress = OH_BoundImport?.VirtualAddress ?? 0; size = OH_BoundImport?.Size ?? 0; break; case 13: virtualAddress = OH_ImportAddressTable?.VirtualAddress ?? 0; size = OH_ImportAddressTable?.Size ?? 0; break; case 14: virtualAddress = OH_DelayImportDescriptor?.VirtualAddress ?? 0; size = OH_DelayImportDescriptor?.Size ?? 0; break; case 15: virtualAddress = OH_CLRRuntimeHeader?.VirtualAddress ?? 0; size = OH_CLRRuntimeHeader?.Size ?? 0; break; case 16: // Reserved virtualAddress = 0; size = 0; break; } // If there is no section table if (SectionTable == null) return null; // Get the physical address from the virtual one uint address = virtualAddress.ConvertVirtualAddress(SectionTable); if (address == 0 || size == 0) return null; lock (_sourceDataLock) { // Create the table data array if we have to if (_tableData == null) _tableData = new byte[16][]; // If we already have cached data, just use that immediately if (_tableData[index] != null) return _tableData[index]; // Populate the raw table data based on the source #if NET48 byte[] tableData = ReadFromDataSource((int)address, (int)size); #else byte[]? tableData = ReadFromDataSource((int)address, (int)size); #endif // Cache and return the table data, even if null _tableData[index] = tableData; return tableData; } } /// /// Get the table strings based on index, if possible /// /// Index of the table to check for /// Table strings on success, null on error #if NET48 public List GetTableStrings(int index) #else public List? GetTableStrings(int index) #endif { // If the table doesn't exist if (index < 0 || index > 16) return null; // Get the virtual address and size from the entries uint virtualAddress = 0, size = 0; switch (index) { case 1: virtualAddress = OH_ExportTable?.VirtualAddress ?? 0; size = OH_ExportTable?.Size ?? 0; break; case 2: virtualAddress = OH_ImportTable?.VirtualAddress ?? 0; size = OH_ImportTable?.Size ?? 0; break; case 3: virtualAddress = OH_ResourceTable?.VirtualAddress ?? 0; size = OH_ResourceTable?.Size ?? 0; break; case 4: virtualAddress = OH_ExceptionTable?.VirtualAddress ?? 0; size = OH_ExceptionTable?.Size ?? 0; break; case 5: virtualAddress = OH_CertificateTable?.VirtualAddress ?? 0; size = OH_CertificateTable?.Size ?? 0; break; case 6: virtualAddress = OH_BaseRelocationTable?.VirtualAddress ?? 0; size = OH_BaseRelocationTable?.Size ?? 0; break; case 7: virtualAddress = OH_Debug?.VirtualAddress ?? 0; size = OH_Debug?.Size ?? 0; break; case 8: // Architecture Table virtualAddress = 0; size = 0; break; case 9: virtualAddress = OH_GlobalPtr?.VirtualAddress ?? 0; size = OH_GlobalPtr?.Size ?? 0; break; case 10: virtualAddress = OH_ThreadLocalStorageTable?.VirtualAddress ?? 0; size = OH_ThreadLocalStorageTable?.Size ?? 0; break; case 11: virtualAddress = OH_LoadConfigTable?.VirtualAddress ?? 0; size = OH_LoadConfigTable?.Size ?? 0; break; case 12: virtualAddress = OH_BoundImport?.VirtualAddress ?? 0; size = OH_BoundImport?.Size ?? 0; break; case 13: virtualAddress = OH_ImportAddressTable?.VirtualAddress ?? 0; size = OH_ImportAddressTable?.Size ?? 0; break; case 14: virtualAddress = OH_DelayImportDescriptor?.VirtualAddress ?? 0; size = OH_DelayImportDescriptor?.Size ?? 0; break; case 15: virtualAddress = OH_CLRRuntimeHeader?.VirtualAddress ?? 0; size = OH_CLRRuntimeHeader?.Size ?? 0; break; case 16: // Reserved virtualAddress = 0; size = 0; break; } // If there is no section table if (SectionTable == null) return null; // Get the physical address from the virtual one uint address = virtualAddress.ConvertVirtualAddress(SectionTable); if (address == 0 || size == 0) return null; lock (_sourceDataLock) { // Create the table string array if we have to if (_tableStringData == null) _tableStringData = new List[16]; // If we already have cached data, just use that immediately if (_tableStringData[index] != null) return _tableStringData[index]; // Populate the table string data based on the source #if NET48 List tableStringData = ReadStringsFromDataSource((int)address, (int)size); #else List? tableStringData = ReadStringsFromDataSource((int)address, (int)size); #endif // Cache and return the table string data, even if null _tableStringData[index] = tableStringData; return tableStringData; } } #endregion } }