/// Microsoft Portable Executable format. /// /// PE32 format for both images (executables) and objects (mscoff object files). /// /// Loosely based on Windows Kits\10\Include\10.0.17763.0\um\winnt.h /// /// Sources: /// - Microsoft Corporation, Microsoft Portable Executable and Common Object File Format Specification, Revision 6.0 - February 1999 /// - Microsoft Corporation, Microsoft Portable Executable and Common Object File Format Specification, Revision 8.3 – February 6, 2013 /// - Microsoft Corporation, PE Format, 2019-08-26 /// - https://github.com/Microsoft/microsoft-pdb/ /// /// Authors: dd86k <dd@dax.moe> /// Copyright: © dd86k <dd@dax.moe> /// License: BSD-3-Clause-Clear module adbg.object.format.pe; import core.stdc.inttypes; import core.stdc.stdlib; import adbg.error; import adbg.object.server; import adbg.machines : AdbgMachine; import adbg.utils.uid : UID; import adbg.utils.bit; // NOTE: Avoid the Windows base types as they are not defined outside "version (Windows)" // NOTE: Microsoft loader limits sections to 96 maximum //TODO: Function to check RVA bounds (within file_size) //TODO: Return everything as const(type)* // Memory implementation will potentially be read-only extern (C): /// Magic number for PE32 object files. enum MAGIC_PE32 = CHAR32!"PE\0\0"; private enum { /// Minimum file size for PE32. /// See: https://stackoverflow.com/a/47311684 MINIMUM_SIZE = 97, /// Specifications limit number of sections to 96. MAXIMUM_SECTIONS = 96, } enum : ushort { // PE_HEADER.Machine, likely all little-endian PE_MACHINE_UNKNOWN = 0, /// Any machine PE_MACHINE_ALPHAOLD = 0x183, /// Alpha (old value), unused PE_MACHINE_ALPHA = 0x184, /// Alpha AXP PE_MACHINE_ALPHA64 = 0x284, /// Alpha AXP 64-bit PE_MACHINE_AM33 = 0x1d3, /// Matsushita AM33 PE_MACHINE_AMD64 = 0x8664, /// x86-64 PE_MACHINE_ARM = 0x1c0, /// ARM little endian PE_MACHINE_ARMNT = 0x1c4, /// arm_a32 (ARMv7+ with thumb2) PE_MACHINE_ARM64 = 0xaa64, /// arm_a64 (AArch64) PE_MACHINE_EBC = 0xebc, /// EFI Byte-Code PE_MACHINE_I386 = 0x14c, /// x86 PE_MACHINE_IA64 = 0x200, /// Itanium PE_MACHINE_LOONGARCH32 = 0x6232, /// LoongArch32 PE_MACHINE_LOONGARCH64 = 0x6264, /// LoongArch64 PE_MACHINE_M32R = 0x9041, /// Mitsubishi M32R LSB PE_MACHINE_MIPS16 = 0x266, /// PE_MACHINE_MIPSFPU = 0x366, /// PE_MACHINE_MIPSFPU16 = 0x466, /// PE_MACHINE_POWERPC = 0x1f0, /// PE_MACHINE_POWERPCFP = 0x1f1, /// PE_MACHINE_R3000 = 0x162, /// MIPS I PE_MACHINE_R4000 = 0x166, /// MIPS II PE_MACHINE_R10000 = 0x168, /// MIPS III PE_MACHINE_RISCV32 = 0x5032, /// RISC-V (32-bit) PE_MACHINE_RISCV64 = 0x5064, /// RISC-V (64-bit) PE_MACHINE_RISCV128 = 0x5128, /// RISC-V (128-bit) PE_MACHINE_SH3 = 0x1a2, /// SuperH PE_MACHINE_SH3DSP = 0x1a3, /// SuperH + DSP PE_MACHINE_SH4 = 0x1a6, /// SuperH 4 PE_MACHINE_SH5 = 0x1a8, /// SuperH 5 PE_MACHINE_THUMB = 0x1c2, /// arm_t32 PE_MACHINE_WCEMIPSV2 = 0x169, /// MIPS WCE PE_MACHINE_CHPE_X86 = 0x3a64, /// ARM64X, source: SystemInformer // https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files PE_MACHINE_CLR = 0xC0EE, /// Pure MSIL. aka COM+ EE? } enum : ushort { // PE_HEADER.Characteristics flags PE_CHARACTERISTIC_RELOCS_STRIPPED = 0x0001, PE_CHARACTERISTIC_EXECUTABLE_IMAGE = 0x0002, PE_CHARACTERISTIC_LINE_NUMS_STRIPPED = 0x0004, PE_CHARACTERISTIC_LOCAL_SYMS_STRIPPED = 0x0008, PE_CHARACTERISTIC_AGGRESSIVE_WS_TRIM = 0x0010, // obsolete PE_CHARACTERISTIC_LARGE_ADDRESS_AWARE = 0x0020, PE_CHARACTERISTIC_16BIT_MACHINE = 0x0040, PE_CHARACTERISTIC_BYTES_REVERSED_LO = 0x0080, // obsolete PE_CHARACTERISTIC_32BIT_MACHINE = 0x0100, PE_CHARACTERISTIC_DEBUG_STRIPPED = 0x0200, PE_CHARACTERISTIC_REMOVABLE_RUN_FROM_SWAP = 0x0400, PE_CHARACTERISTIC_NET_RUN_FROM_SWAP = 0x0800, PE_CHARACTERISTIC_SYSTEM = 0x1000, PE_CHARACTERISTIC_DLL = 0x2000, PE_CHARACTERISTIC_UP_SYSTEM_ONLY = 0x4000, PE_CHARACTERISTIC_BYTES_REVERSED_HI = 0x8000 // obsolete } enum : ushort { // PE_OPTIONAL_HEADER.DllCharacteristics flags PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020, PE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040, PE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080, PE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100, PE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, PE_DLLCHARACTERISTICS_NO_SEH = 0x0400, PE_DLLCHARACTERISTICS_NO_BIND = 0x0800, PE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000, PE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, PE_DLLCHARACTERISTICS_GUARD_CF = 0x4000, PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000, } /// To be used with SECTION_CHARACTERISTIC_ALIGN fields. enum PE_SECTION_CHARACTERISTIC_ALIGN_MASK = 0x00F00000; enum { // PE_SECTION_ENTRY.Characteristics flags PE_SECTION_CHARACTERISTIC_TYPE_DSECT = 0x00000001, /// Reserved, undocumented PE_SECTION_CHARACTERISTIC_TYPE_NOLOAD = 0x00000002, /// Reserved, undocumented PE_SECTION_CHARACTERISTIC_TYPE_GROUP = 0x00000004, /// Reserved, undocumented PE_SECTION_CHARACTERISTIC_NO_PAD = 0x00000008, PE_SECTION_CHARACTERISTIC_TYPE_COPY = 0x00000010, /// Reserved, undocumented PE_SECTION_CHARACTERISTIC_CODE = 0x00000020, PE_SECTION_CHARACTERISTIC_INITIALIZED_DATA = 0x00000040, PE_SECTION_CHARACTERISTIC_UNINITIALIZED_DATA = 0x00000080, PE_SECTION_CHARACTERISTIC_LNK_OTHER = 0x00000100, /// Reserved PE_SECTION_CHARACTERISTIC_LNK_INFO = 0x00000200, PE_SECTION_CHARACTERISTIC_LNK_REMOVE = 0x00000800, PE_SECTION_CHARACTERISTIC_LNK_COMDAT = 0x00001000, PE_SECTION_CHARACTERISTIC_MEM_PROTECTED = 0x00004000, /// Reserved, undocumented PE_SECTION_CHARACTERISTIC_GPREL = 0x00008000, PE_SECTION_CHARACTERISTIC_MEM_PURGEABLE = 0x00010000, /// Reserved, aka SYSHEAP PE_SECTION_CHARACTERISTIC_MEM_16BIT = 0x00020000, /// Reserved PE_SECTION_CHARACTERISTIC_MEM_LOCKED = 0x00040000, /// Reserved PE_SECTION_CHARACTERISTIC_PRELOAD = 0x00080000, /// Reserved PE_SECTION_CHARACTERISTIC_ALIGN_1BYTES = 0x00100000, PE_SECTION_CHARACTERISTIC_ALIGN_2BYTES = 0x00200000, PE_SECTION_CHARACTERISTIC_ALIGN_4BYTES = 0x00300000, PE_SECTION_CHARACTERISTIC_ALIGN_8BYTES = 0x00400000, PE_SECTION_CHARACTERISTIC_ALIGN_16BYTES = 0x00500000, PE_SECTION_CHARACTERISTIC_ALIGN_32BYTES = 0x00600000, PE_SECTION_CHARACTERISTIC_ALIGN_64BYTES = 0x00700000, PE_SECTION_CHARACTERISTIC_ALIGN_128BYTES = 0x00800000, PE_SECTION_CHARACTERISTIC_ALIGN_256BYTES = 0x00900000, PE_SECTION_CHARACTERISTIC_ALIGN_512BYTES = 0x00A00000, PE_SECTION_CHARACTERISTIC_ALIGN_1024BYTES = 0x00B00000, PE_SECTION_CHARACTERISTIC_ALIGN_2048BYTES = 0x00C00000, PE_SECTION_CHARACTERISTIC_ALIGN_4096BYTES = 0x00D00000, PE_SECTION_CHARACTERISTIC_ALIGN_8192BYTES = 0x00E00000, PE_SECTION_CHARACTERISTIC_LNK_NRELOC_OVFL = 0x01000000, PE_SECTION_CHARACTERISTIC_MEM_DISCARDABLE = 0x02000000, PE_SECTION_CHARACTERISTIC_MEM_NOT_CACHED = 0x04000000, PE_SECTION_CHARACTERISTIC_MEM_NOT_PAGED = 0x08000000, PE_SECTION_CHARACTERISTIC_MEM_SHARED = 0x10000000, PE_SECTION_CHARACTERISTIC_MEM_EXECUTE = 0x20000000, PE_SECTION_CHARACTERISTIC_MEM_READ = 0x40000000, PE_SECTION_CHARACTERISTIC_MEM_WRITE = 0x80000000, } enum : ushort { // PE image format/magic PE_FMT_ROM = 0x0107, // No longer used? Docs no longer have it PE_FMT_32 = 0x010B, /// PE32 PE_FMT_64 = 0x020B, /// PE32+ } enum : ushort { // PE_HEADER PE_SUBSYSTEM_NATIVE = 1, PE_SUBSYSTEM_WINDOWS_GUI = 2, PE_SUBSYSTEM_WINDOWS_CUI = 3, PE_SUBSYSTEM_OS2_CUI = 5, PE_SUBSYSTEM_POSIX_CUI = 7, PE_SUBSYSTEM_NATIVE_WINDOWS = 8, PE_SUBSYSTEM_WINDOWS_CE_GUI = 9, PE_SUBSYSTEM_EFI_APPLICATION = 10, PE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, PE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, PE_SUBSYSTEM_EFI_ROM = 13, PE_SUBSYSTEM_XBOX = 14, PE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16, PE_SUBSYSTEM_XBOX_CODE_CATALOG = 17, } /// COFF file header (object and image) struct PE_HEADER { align(1): union { uint8_t[4] Signature; uint32_t Signature32; } uint16_t Machine; uint16_t NumberOfSections; uint32_t TimeDateStamp; // C time_t uint32_t PointerToSymbolTable; uint32_t NumberOfSymbols; uint16_t SizeOfOptionalHeader; uint16_t Characteristics; } // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx // Image only struct PE_OPTIONAL_HEADER { align(1): uint16_t Magic; // "Format" uint8_t MajorLinkerVersion; uint8_t MinorLinkerVersion; uint32_t SizeOfCode; uint32_t SizeOfInitializedData; uint32_t SizeOfUninitializedData; uint32_t AddressOfEntryPoint; uint32_t BaseOfCode; uint32_t BaseOfData; uint32_t ImageBase; uint32_t SectionAlignment; uint32_t FileAlignment; uint16_t MajorOperatingSystemVersion; uint16_t MinorOperatingSystemVersion; uint16_t MajorImageVersion; uint16_t MinorImageVersion; uint16_t MajorSubsystemVersion; uint16_t MinorSubsystemVersion; uint32_t Win32VersionValue; uint32_t SizeOfImage; uint32_t SizeOfHeaders; uint32_t CheckSum; uint16_t Subsystem; uint16_t DllCharacteristics; uint32_t SizeOfStackReserve; uint32_t SizeOfStackCommit; uint32_t SizeOfHeapReserve; uint32_t SizeOfHeapCommit; uint32_t LoaderFlags; /// Obsolete uint32_t NumberOfRvaAndSizes; } struct PE_OPTIONAL_HEADER64 { align(1): uint16_t Magic; // "Format" uint8_t MajorLinkerVersion; uint8_t MinorLinkerVersion; uint32_t SizeOfCode; uint32_t SizeOfInitializedData; uint32_t SizeOfUninitializedData; uint32_t AddressOfEntryPoint; uint32_t BaseOfCode; uint64_t ImageBase; uint32_t SectionAlignment; uint32_t FileAlignment; uint16_t MajorOperatingSystemVersion; uint16_t MinorOperatingSystemVersion; uint16_t MajorImageVersion; uint16_t MinorImageVersion; uint16_t MajorSubsystemVersion; uint16_t MinorSubsystemVersion; uint32_t Win32VersionValue; uint32_t SizeOfImage; uint32_t SizeOfHeaders; uint32_t CheckSum; uint16_t Subsystem; uint16_t DllCharacteristics; uint64_t SizeOfStackReserve; uint64_t SizeOfStackCommit; uint64_t SizeOfHeapReserve; uint64_t SizeOfHeapCommit; uint32_t LoaderFlags; // Obsolete uint32_t NumberOfRvaAndSizes; } struct PE_OPTIONAL_HEADERROM { uint16_t Magic; uint8_t MajorLinkerVersion; uint8_t MinorLinkerVersion; uint32_t SizeOfCode; uint32_t SizeOfInitializedData; uint32_t SizeOfUninitializedData; uint32_t AddressOfEntryPoint; uint32_t BaseOfCode; uint32_t BaseOfData; uint32_t BaseOfBss; uint32_t GprMask; uint32_t[4] CprMask; uint32_t GpValue; } struct PE_DIRECTORY_ENTRY { align(1): uint32_t rva; /// Relative Virtual Address uint32_t size; /// Size in bytes } // IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 // MS recommends checking NumberOfRvaAndSizes but it always been 16 struct PE_IMAGE_DATA_DIRECTORY { align(1): PE_DIRECTORY_ENTRY ExportTable; PE_DIRECTORY_ENTRY ImportTable; PE_DIRECTORY_ENTRY ResourceTable; PE_DIRECTORY_ENTRY ExceptionTable; PE_DIRECTORY_ENTRY CertificateTable; // File Pointer (instead of RVA) PE_DIRECTORY_ENTRY BaseRelocationTable; PE_DIRECTORY_ENTRY DebugDirectory; PE_DIRECTORY_ENTRY ArchitectureData; PE_DIRECTORY_ENTRY GlobalPtr; PE_DIRECTORY_ENTRY TLSTable; PE_DIRECTORY_ENTRY LoadConfigurationTable; PE_DIRECTORY_ENTRY BoundImportTable; PE_DIRECTORY_ENTRY ImportAddressTable; PE_DIRECTORY_ENTRY DelayImport; PE_DIRECTORY_ENTRY CLRHeader; // Used to be (or alias to) COM+ Runtime Header PE_DIRECTORY_ENTRY Reserved; } // // ANCHOR Directory structures // struct PE_EXPORT_DESCRIPTOR { align(1): uint32_t ExportFlags; uint32_t Timestamp; uint16_t MajorVersion; uint16_t MinorVersion; uint32_t Name; /// RVA uint32_t OrdinalBase; uint32_t AddressTableEntries; /// Number of export entries uint32_t NumberOfNamePointers; /// Same amount for ordinal uint32_t ExportAddressTable; /// RVA uint32_t NamePointer; /// RVA, "The address of the export name pointer table" uint32_t OrdinalTable; /// RVA } union PE_EXPORT_ENTRY { align(1): uint32_t Export; /// RVA uint32_t Forwarder; /// RVA } // IMAGE_IMPORT_DESCRIPTOR struct PE_IMPORT_DESCRIPTOR { align(1): uint32_t Characteristics; // used in WINNT.H but no longer descriptive uint32_t TimeDateStamp; // time_t uint32_t ForwarderChain; uint32_t Name; uint32_t FirstThunk; } /// Import Lookup Table entry structure struct PE_IMPORT_ENTRY32 { align(1): union { uint ordinal; /// Ordinal/Name Flag ushort number; /// Ordinal Number (val[31] is set) uint rva; /// Hint/Name Table RVA (val[31] is clear) } } /// Import Lookup Table entry structure struct PE_IMPORT_ENTRY64 { align(1): union { ulong ordinal; /// Ordinal/Name Flag ushort number; /// Ordinal Number (val2[31] is set) uint rva; /// Hint/Name Table RVA (val2[31] is clear) } } /// DEBUG Directory struct PE_DEBUG_DIRECTORY { align(1): uint32_t Characteristics; /// reserved, must be zero uint32_t TimeDateStamp; /// time and date that the debug data was created uint16_t MajorVersion; /// The major version number of the debug data format uint16_t MinorVersion; /// The minor version number of the debug data format uint32_t Type; /// The format of debugging information uint32_t SizeOfData; /// The size of the debug data (not including the debug directory itself) uint32_t AddressOfRawData; /// The address of the debug data relative to the image base uint32_t PointerToRawData; /// The file pointer to the debug data } // Debug Types enum : uint { /// An unknown value that is ignored by all tools PE_IMAGE_DEBUG_TYPE_UNKNOWN = 0, /// The COFF debug information (line numbers, symbol table, and string table). /// This type of debug information is also pointed to by fields in the file headers. PE_IMAGE_DEBUG_TYPE_COFF = 1, /// The Visual C++ debug information PE_IMAGE_DEBUG_TYPE_CODEVIEW = 2, /// The frame pointer omission (FPO) information. This information tells the /// debugger how to interpret nonstandard stack frames, which use the EBP /// register for a purpose other than as a frame pointer. PE_IMAGE_DEBUG_TYPE_FPO = 3, /// The location of DBG file. PE_IMAGE_DEBUG_TYPE_MISC = 4, /// A copy of .pdata section. PE_IMAGE_DEBUG_TYPE_EXCEPTION = 5, /// Reserved. PE_IMAGE_DEBUG_TYPE_FIXUP = 6, /// The mapping from an RVA in image to an RVA in source image. PE_IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7, /// The mapping from an RVA in source image to an RVA in image. PE_IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8, /// Reserved for Borland. PE_IMAGE_DEBUG_TYPE_BORLAND = 9, /// Reserved. PE_IMAGE_DEBUG_TYPE_RESERVED10 = 10, /// Reserved. PE_IMAGE_DEBUG_TYPE_CLSID = 11, /// Undocumented, from winnt.h PE_IMAGE_DEBUG_TYPE_VC_FEATURE = 12, /// Profile Guided Optimization. /// See: https://devblogs.microsoft.com/cppblog/pogo/ PE_IMAGE_DEBUG_TYPE_POGO = 13, /// Incremental Link Time Code Generation. /// See: https://devblogs.microsoft.com/cppblog/speeding-up-the-incremental-developer-build-scenario/ PE_IMAGE_DEBUG_TYPE_ILTCG = 14, /// Uses Intel MPX PE_IMAGE_DEBUG_TYPE_MPX = 15, /// PE determinism or reproducibility. PE_IMAGE_DEBUG_TYPE_REPRO = 16, /// Embedded Portable PDB Debug Directory Entry PE_IMAGE_DEBUG_TYPE_EMBEDDED = 17, /// SPGo debug types PE_IMAGE_DEBUG_TYPE_SPGO = 18, /// Crypto hash of the content of the symbol file the PE/COFF file was built with. PE_IMAGE_DEBUG_TYPE_HASH = 19, /// Extended DLL characteristics bits. PE_IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20, /// R2R PerfMap Debug Directory Entry PE_IMAGE_DEBUG_TYPE_R2R_PERFMAP = 21, } // Magics for debug structures enum : uint { PE_IMAGE_DEBUG_MAGIC_CODEVIEW_LINK510 = CHAR32!"NB02", /// MS LINK 5.10 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_LINK520 = CHAR32!"NB05", /// MS LINK 5.20 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_QUICKC = CHAR32!"NB07", /// Quick C for Windows 1.0 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_CV400 = CHAR32!"NB08", /// PDB 2.0+ / CodeView 4.00-4.05 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_CV410 = CHAR32!"NB09", /// PDB 2.0+ / CodeView 4.10 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_PDB20PLUS = CHAR32!"NB10", /// PDB 2.0+ / MS C/C++ PDB 2.0 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_CV500 = CHAR32!"NB11", /// PDB 2.0+ / CodeView 5.0 PE_IMAGE_DEBUG_MAGIC_CODEVIEW_CV700 = CHAR32!"RSDS", /// PDB 7.0 / CodeView 7.0 // Mono source has it set as 0x4244504d PE_IMAGE_DEBUG_MAGIC_EMBEDDED_PPDB = CHAR32!"MPDB", /// Portable PDB PE_IMAGE_DEBUG_MAGIC_PPDB = CHAR32!"BSJB", /// Portable PDB // Source: SystemInformer, except for full names PE_IMAGE_DEBUG_MAGIC_POGO_LTCG = CHAR32!"LTCG", /// Link-Time Code Generation PE_IMAGE_DEBUG_MAGIC_POGO_PGU = CHAR32!"PGU\0", /// Profile Guided Update (/LTCG:PGUPDATE) } struct PE_DEBUG_DATA_MISC { align(1): char[4] Signature; /// uint DataType; /// Must be 1 uint Length; /// Multiple of four; Total length of data block bool Unicode; /// If true, Unicode string byte[3] Reserved; byte[1] Data; } /// CodeView format for PDB 2.0 and above // See http://www.debuginfo.com/articles/debuginfomatch.html struct PE_DEBUG_DATA_CODEVIEW_PDB20 { align(1): // Old PE32 doc mentions "NB05" -- CodeView 4.0 or earlier? char[4] Signature; /// Magic: "NB09"/"NB10"/"NB11" bytes /// Offset to the start of the actual debug information from the /// beginning of the CodeView data. Zero if it's another file. uint32_t Offset; uint32_t Timestamp; /// uint32_t Age; /// incremented each time the executable is remade by the linker char[1] Path; /// Path to PDB (0-terminated) } /// CodeView format for PDB 7.0 // See http://www.godevtool.com/Other/pdb.htm // and http://www.debuginfo.com/articles/debuginfomatch.html struct PE_DEBUG_DATA_CODEVIEW_PDB70 { align(1): char[4] Signature; /// Magic: "RSDS" bytes UID Guid; /// GUID of PDB file, matches with PDB file uint32_t Age; /// incremented each time the executable is remade by the linker char[1] Path; /// Path to PDB (0-terminated UTF-8) } /// VC Featured data struct PE_DEBUG_DATA_VC_FEAT { align(1): uint prevc11; /// Pre-VC11 uint ccpp; /// C/C++ uint gs; /// /GS uint sdl; /// /SDL uint guardn; /// guardN } /// Declares that debugging information is embedded in the PE file at location /// specified by PointerToRawData. // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md // Version Major=any, Minor=0x0100 of the data format: struct PE_DEBUG_DATA_EMBEDDED { align(1): char[4] Signature; /// Magic: "MPDB" uint UncompressedSize; // SizeOfData - 8: PortablePdbImage // Portable PDB image compressed using Deflate algorithm ubyte[1] PortablePdbImage; } /// POGO Entry containing filename. Should be ending with .PGD (Profile-Guided Database). struct PE_DEBUG_POGO_ENTRY { uint Magic; uint Rva; uint Size; char[1] Name; } // aka MetadataRootHeader struct PE_DEBUG_DATA_PPDB { /// Magic signature for physical metadata : 0x424A5342. // or "BSJB" char[4] Signature; /// Major version, 1 (ignore on read) ushort MajorVersion; /// Minor version, 1 (ignore on read) ushort MinorVersion; /// Reserved, always 0. uint Reserved; /// Length of version string, multi-byte. uint Length; /// UTF-8 "Version" string. // 4-Byte aligned, maximum 255 (?). // Values: // - "PDB v1.00" with a value of 12 (.NET 6) // - "Standard CLI 2002" (17 chars, so rounded to 20 chars) char[1] Version; } // After MetadataRootHeader + Version string struct PE_DEBUG_DATA_PPDB_FLAGS { ushort Flags; ushort Streams; } struct PE_DEBUG_DATA_PPDB_STREAM { uint Offset; uint Size; char[1] Name; } /// Declares that the image has an associated PerfMap file containing a table /// mapping symbols to offsets for ready to run compilations. // Version Major=0x0001, Minor=0x0000 of the entry data format is following: struct PE_DEBUG_DATA_R2R_PERFMAP { align(1): char[4] Magic; /// "R2RM" /// Byte sequence uniquely identifying the associated PerfMap. ubyte[16] Signature; /// Version number of the PerfMap. Currently only version 1 is supported. uint Version; /// UTF-8 NUL-terminated path to the associated .r2rmap file. char[1] Path; } struct PE_LOAD_CONFIG_CODE_INTEGRITY { align(1): uint16_t Flags; // Flags to indicate if CI information is available, etc. uint16_t Catalog; // 0xFFFF means not available uint32_t CatalogOffset; uint32_t Reserved; // Additional bitmask to be defined later } /// IMAGE_LOAD_CONFIG_DIRECTORY32 //TODO: Map sizes to WindowsNT versions // Or very likely MSVC linker versions struct PE_LOAD_CONFIG_DIR32 { align(1): // Windows XP and after uint32_t Size; // Doc: Characteristics, header: Size, Windows XP=64 uint32_t TimeDateStamp; // time_t uint16_t MajorVersion; uint16_t MinorVersion; uint32_t GlobalFlagsClear; uint32_t GlobalFlagsSet; uint32_t CriticalSectionDefaultTimeout; uint32_t DeCommitFreeBlockThreshold; uint32_t DeCommitTotalBlockThreshold; uint32_t LockPrefixTable; uint32_t MaximumAllocationSize; uint32_t VirtualMemoryThreshold; uint32_t ProcessHeapFlags; uint32_t ProcessAffinityMask; uint16_t CSDVersion; uint16_t Reserved1; uint32_t EditList; // Windows 7 and later uint32_t SecurityCookie; uint32_t SEHandlerTable; uint32_t SEHandlerCount; uint32_t GuardCFCheckFunctionPointer; // Control Flow uint32_t GuardCFDispatchFunctionPointer; uint32_t GuardCFFunctionTable; uint32_t GuardCFFunctionCount; // Windows 8 and later? uint32_t GuardFlags; PE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity; uint32_t GuardAddressTakenIatEntryTable; uint32_t GuardAddressTakenIatEntryCount; uint32_t GuardLongJumpTargetTable; uint32_t GuardLongJumpTargetCount; // Windows 8's limit? // Windows 10 and later? uint32_t DynamicValueRelocTable; // VA uint32_t CHPEMetadataPointer; uint32_t GuardRFFailureRoutine; // VA uint32_t GuardRFFailureRoutineFunctionPointer; // VA uint32_t DynamicValueRelocTableOffset; uint16_t DynamicValueRelocTableSection; uint16_t Reserved2; uint32_t GuardRFVerifyStackPointerFunctionPointer; // VA uint32_t HotPatchTableOffset; uint32_t Reserved3; uint32_t EnclaveConfigurationPointer; // VA uint32_t VolatileMetadataPointer; // VA // 10.0.2261.0 uint GuardEHContinuationTable; // VA uint GuardEHContinuationCount; uint GuardXFGCheckFunctionPointer; // VA uint GuardXFGDispatchFunctionPointer; // VA uint GuardXFGTableDispatchFunctionPointer; // VA uint CastGuardOsDeterminedFailureMode; // VA uint GuardMemcpyFunctionPointer; // VA } /// IMAGE_LOAD_CONFIG_DIRECTORY64 //TODO: Map sizes to WindowsNT versions // Or MSVC linker versions struct PE_LOAD_CONFIG_DIR64 { align(1): uint32_t Size; // Characteristics uint32_t TimeDateStamp; // time_t uint16_t MajorVersion; uint16_t MinorVersion; uint32_t GlobalFlagsClear; uint32_t GlobalFlagsSet; uint32_t CriticalSectionDefaultTimeout; uint64_t DeCommitFreeBlockThreshold; uint64_t DeCommitTotalBlockThreshold; uint64_t LockPrefixTable; uint64_t MaximumAllocationSize; uint64_t VirtualMemoryThreshold; uint64_t ProcessAffinityMask; uint32_t ProcessHeapFlags; uint16_t CSDVersion; uint16_t Reserved1; uint64_t EditList; // Windows 7 and later uint64_t SecurityCookie; uint64_t SEHandlerTable; uint64_t SEHandlerCount; uint64_t GuardCFCheckFunctionPointer; // Control Flow uint64_t GuardCFDispatchFunctionPointer; uint64_t GuardCFFunctionTable; uint64_t GuardCFFunctionCount; uint32_t GuardFlags; // Windows 8 and later? PE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity; uint64_t GuardAddressTakenIatEntryTable; uint64_t GuardAddressTakenIatEntryCount; uint64_t GuardLongJumpTargetTable; uint64_t GuardLongJumpTargetCount; // Windows 10 and later? uint64_t DynamicValueRelocTable; // VA uint64_t CHPEMetadataPointer; // VA uint64_t GuardRFFailureRoutine; // VA uint64_t GuardRFFailureRoutineFunctionPointer; // VA uint32_t DynamicValueRelocTableOffset; uint16_t DynamicValueRelocTableSection; uint16_t Reserved2; uint64_t GuardRFVerifyStackPointerFunctionPointer; // VA uint32_t HotPatchTableOffset; uint32_t Reserved3; uint64_t EnclaveConfigurationPointer; // VA uint64_t VolatileMetadataPointer; // VA // 10.0.22621.0 ulong GuardEHContinuationTable; // VA ulong GuardEHContinuationCount; ulong GuardXFGCheckFunctionPointer; // VA ulong GuardXFGDispatchFunctionPointer; // VA ulong GuardXFGTableDispatchFunctionPointer; // VA ulong CastGuardOsDeterminedFailureMode; // VA ulong GuardMemcpyFunctionPointer; // VA } struct PE_SECTION_ENTRY { align(1): /// An 8-byte, null-padded UTF-8 encoded string. If /// the string is exactly 8 characters long, there is /// no terminating null. For longer names, this field /// contains a slash (/) that is followed by an ASCII /// representation of a decimal number that is an /// offset into the string table. Executable images /// do not use a string table and do not support /// section names longer than 8 characters. Long /// names in object files are truncated if they are /// emitted to an executable file. char[8] Name; /// The total size of the section when loaded into /// memory. If this value is greater than /// SizeOfRawData, the section is zero-padded. This /// field is valid only for executable images and /// should be set to zero for object files. uint32_t VirtualSize; /// For executable images, the address of the first /// byte of the section relative to the image base /// when the section is loaded into memory. For /// object files, this field is the address of the first /// byte before relocation is applied; for simplicity, /// compilers should set this to zero. Otherwise, it /// is an arbitrary value that is subtracted from /// offsets during relocation. uint32_t VirtualAddress; /// The size of the section (for object files) or the /// size of the initialized data on disk (for image /// files). For executable images, this must be a /// multiple of FileAlignment from the optional /// header. If this is less than VirtualSize, the /// remainder of the section is zero-filled. Because /// the SizeOfRawData field is rounded but the /// VirtualSize field is not, it is possible for /// SizeOfRawData to be greater than VirtualSize as /// well. When a section contains only uninitialized /// data, this field should be zero. uint32_t SizeOfRawData; /// The file pointer to the first page of the section /// within the COFF file. For executable images, this /// must be a multiple of FileAlignment from the /// optional header. For object files, the value /// should be aligned on a 4-byte boundary for best /// performance. When a section contains only /// uninitialized data, this field should be zero. uint32_t PointerToRawData; /// The file pointer to the beginning of relocation /// entries for the section. This is set to zero for /// executable images or if there are no /// relocations. uint32_t PointerToRelocations; /// The file pointer to the beginning of line-number /// entries for the section. This is set to zero if /// there are no COFF line numbers. This value /// should be zero for an image because COFF /// debugging information is deprecated. uint32_t PointerToLinenumbers; /// The number of relocation entries for the /// section. This is set to zero for executable /// images. uint16_t NumberOfRelocations; /// The number of line-number entries for the /// section. This value should be zero for an image /// because COFF debugging information is /// deprecated. uint16_t NumberOfLinenumbers; /// The flags that describe the characteristics of the /// section. uint32_t Characteristics; } /// (Internal) Called by the server to preload a PE object. /// Params: o = Object instance. /// Returns: Error code. int adbg_object_pe_load(adbg_object_t *o) { if (o.file_size < MINIMUM_SIZE) return adbg_oops(AdbgError.objectTooSmall); o.format = AdbgObject.pe; // Boundchecks are done later void *base = o.i.mz.newbase; o.i.pe.header = cast(PE_HEADER*)base; o.i.pe.opt_header = cast(PE_OPTIONAL_HEADER*)(base + PE_OFFSET_OPTHDR); with (o.i.pe.header) if (o.p.reversed) { Signature32 = adbg_bswap32(Signature32); Machine = adbg_bswap16(Machine); NumberOfSections = adbg_bswap16(NumberOfSections); TimeDateStamp = adbg_bswap32(TimeDateStamp); PointerToSymbolTable = adbg_bswap32(PointerToSymbolTable); NumberOfSymbols = adbg_bswap32(NumberOfSymbols); SizeOfOptionalHeader = adbg_bswap16(SizeOfOptionalHeader); Characteristics = adbg_bswap16(Characteristics); with (o.i.pe.opt_header) Magic = adbg_bswap16(Magic); } switch (o.i.pe.opt_header.Magic) { case PE_FMT_32: if (adbg_object_outboundpl(o, o.i.pe.opt_header, PE_OPTIONAL_HEADER.sizeof)) return adbg_oops(AdbgError.offsetBounds); o.i.pe.directory = cast(PE_IMAGE_DATA_DIRECTORY*)(base + PE_OFFSET_DIR_OPTHDR32); if (adbg_object_outboundpl(o, o.i.pe.directory, PE_IMAGE_DATA_DIRECTORY.sizeof)) return adbg_oops(AdbgError.offsetBounds); o.i.pe.sections = cast(PE_SECTION_ENTRY*)(base + PE_OFFSET_SEC_OPTHDR32); if (adbg_object_outboundpl(o, o.i.pe.sections, PE_SECTION_ENTRY.sizeof * o.i.pe.header.NumberOfSections)) return adbg_oops(AdbgError.offsetBounds); if (o.p.reversed) { PE_OPTIONAL_HEADER *hdr = o.i.pe.opt_header; hdr.SizeOfCode = adbg_bswap32(hdr.SizeOfCode); hdr.SizeOfInitializedData = adbg_bswap32(hdr.SizeOfInitializedData); hdr.SizeOfUninitializedData = adbg_bswap32(hdr.SizeOfUninitializedData); hdr.AddressOfEntryPoint = adbg_bswap32(hdr.AddressOfEntryPoint); hdr.BaseOfCode = adbg_bswap32(hdr.BaseOfCode); hdr.BaseOfData = adbg_bswap32(hdr.BaseOfData); hdr.ImageBase = adbg_bswap32(hdr.ImageBase); hdr.SectionAlignment = adbg_bswap32(hdr.SectionAlignment); hdr.FileAlignment = adbg_bswap32(hdr.FileAlignment); hdr.MajorOperatingSystemVersion = adbg_bswap16(hdr.MajorOperatingSystemVersion); hdr.MinorOperatingSystemVersion = adbg_bswap16(hdr.MinorOperatingSystemVersion); hdr.MajorImageVersion = adbg_bswap16(hdr.MajorImageVersion); hdr.MinorImageVersion = adbg_bswap16(hdr.MinorImageVersion); hdr.MajorSubsystemVersion = adbg_bswap16(hdr.MajorSubsystemVersion); hdr.MinorSubsystemVersion = adbg_bswap16(hdr.MinorSubsystemVersion); hdr.Win32VersionValue = adbg_bswap32(hdr.Win32VersionValue); hdr.SizeOfImage = adbg_bswap32(hdr.SizeOfImage); hdr.SizeOfHeaders = adbg_bswap32(hdr.SizeOfHeaders); hdr.CheckSum = adbg_bswap32(hdr.CheckSum); hdr.Subsystem = adbg_bswap16(hdr.Subsystem); hdr.DllCharacteristics = adbg_bswap16(hdr.DllCharacteristics); hdr.SizeOfStackReserve = adbg_bswap32(hdr.SizeOfStackReserve); hdr.SizeOfStackCommit = adbg_bswap32(hdr.SizeOfStackCommit); hdr.SizeOfHeapReserve = adbg_bswap32(hdr.SizeOfHeapReserve); hdr.SizeOfHeapCommit = adbg_bswap32(hdr.SizeOfHeapCommit); hdr.LoaderFlags = adbg_bswap32(hdr.LoaderFlags); hdr.NumberOfRvaAndSizes = adbg_bswap32(hdr.NumberOfRvaAndSizes); } break; case PE_FMT_64: if (adbg_object_outboundpl(o, o.i.pe.opt_header, PE_OPTIONAL_HEADER64.sizeof)) return adbg_oops(AdbgError.offsetBounds); o.i.pe.directory = cast(PE_IMAGE_DATA_DIRECTORY*)(base + PE_OFFSET_DIR_OPTHDR64); if (adbg_object_outboundpl(o, o.i.pe.directory, PE_IMAGE_DATA_DIRECTORY.sizeof)) return adbg_oops(AdbgError.offsetBounds); o.i.pe.sections = cast(PE_SECTION_ENTRY*)(base + PE_OFFSET_SEC_OPTHDR64); if (adbg_object_outboundpl(o, o.i.pe.sections, PE_SECTION_ENTRY.sizeof * o.i.pe.header.NumberOfSections)) return adbg_oops(AdbgError.offsetBounds); if (o.p.reversed) { PE_OPTIONAL_HEADER64 *hdr = o.i.pe.opt_header64; hdr.SizeOfCode = adbg_bswap32(hdr.SizeOfCode); hdr.SizeOfInitializedData = adbg_bswap32(hdr.SizeOfInitializedData); hdr.SizeOfUninitializedData = adbg_bswap32(hdr.SizeOfUninitializedData); hdr.AddressOfEntryPoint = adbg_bswap32(hdr.AddressOfEntryPoint); hdr.BaseOfCode = adbg_bswap32(hdr.BaseOfCode); hdr.ImageBase = adbg_bswap64(hdr.ImageBase); hdr.SectionAlignment = adbg_bswap32(hdr.SectionAlignment); hdr.FileAlignment = adbg_bswap32(hdr.FileAlignment); hdr.MajorOperatingSystemVersion = adbg_bswap16(hdr.MajorOperatingSystemVersion); hdr.MinorOperatingSystemVersion = adbg_bswap16(hdr.MinorOperatingSystemVersion); hdr.MajorImageVersion = adbg_bswap16(hdr.MajorImageVersion); hdr.MinorImageVersion = adbg_bswap16(hdr.MinorImageVersion); hdr.MajorSubsystemVersion = adbg_bswap16(hdr.MajorSubsystemVersion); hdr.MinorSubsystemVersion = adbg_bswap16(hdr.MinorSubsystemVersion); hdr.Win32VersionValue = adbg_bswap32(hdr.Win32VersionValue); hdr.SizeOfImage = adbg_bswap32(hdr.SizeOfImage); hdr.SizeOfHeaders = adbg_bswap32(hdr.SizeOfHeaders); hdr.CheckSum = adbg_bswap32(hdr.CheckSum); hdr.Subsystem = adbg_bswap16(hdr.Subsystem); hdr.DllCharacteristics = adbg_bswap16(hdr.DllCharacteristics); hdr.SizeOfStackReserve = adbg_bswap64(hdr.SizeOfStackReserve); hdr.SizeOfStackCommit = adbg_bswap64(hdr.SizeOfStackCommit); hdr.SizeOfHeapReserve = adbg_bswap64(hdr.SizeOfHeapReserve); hdr.SizeOfHeapCommit = adbg_bswap64(hdr.SizeOfHeapCommit); hdr.LoaderFlags = adbg_bswap32(hdr.LoaderFlags); hdr.NumberOfRvaAndSizes = adbg_bswap32(hdr.NumberOfRvaAndSizes); } break; case PE_FMT_ROM: // NOTE: ROM have no optional header and directories o.i.pe.directory = null; o.i.pe.sections = cast(PE_SECTION_ENTRY*)(base + PE_OFFSET_SEC_OPTHDRROM); if (adbg_object_outboundpl(o, o.i.pe.sections, PE_SECTION_ENTRY.sizeof * o.i.pe.header.NumberOfSections)) return adbg_oops(AdbgError.offsetBounds); if (o.p.reversed) { PE_OPTIONAL_HEADERROM *hdr = o.i.pe.opt_headerrom; hdr.SizeOfCode = adbg_bswap32(hdr.SizeOfCode); hdr.SizeOfInitializedData = adbg_bswap32(hdr.SizeOfInitializedData); hdr.SizeOfUninitializedData = adbg_bswap32(hdr.SizeOfUninitializedData); hdr.AddressOfEntryPoint = adbg_bswap32(hdr.AddressOfEntryPoint); hdr.BaseOfCode = adbg_bswap32(hdr.BaseOfCode); hdr.BaseOfData = adbg_bswap32(hdr.BaseOfData); hdr.BaseOfBss = adbg_bswap32(hdr.BaseOfBss); hdr.GprMask = adbg_bswap32(hdr.GprMask); hdr.CprMask[0] = adbg_bswap32(hdr.CprMask[0]); hdr.CprMask[1] = adbg_bswap32(hdr.CprMask[1]); hdr.CprMask[2] = adbg_bswap32(hdr.CprMask[2]); hdr.CprMask[3] = adbg_bswap32(hdr.CprMask[3]); hdr.GpValue = adbg_bswap32(hdr.GpValue); } return 0; default: return adbg_oops(AdbgError.objectInvalidClass); } if (o.p.reversed && o.i.pe.directory) with (o.i.pe.directory) { ExportTable.rva = adbg_bswap32(ExportTable.rva); ExportTable.size = adbg_bswap32(ExportTable.size); ImportTable.rva = adbg_bswap32(ImportTable.rva); ImportTable.size = adbg_bswap32(ImportTable.size); ResourceTable.rva = adbg_bswap32(ResourceTable.rva); ResourceTable.size = adbg_bswap32(ResourceTable.size); ExceptionTable.rva = adbg_bswap32(ExceptionTable.rva); ExceptionTable.size = adbg_bswap32(ExceptionTable.size); CertificateTable.rva = adbg_bswap32(CertificateTable.rva); CertificateTable.size = adbg_bswap32(CertificateTable.size); BaseRelocationTable.rva = adbg_bswap32(BaseRelocationTable.rva); BaseRelocationTable.size = adbg_bswap32(BaseRelocationTable.size); DebugDirectory.rva = adbg_bswap32(DebugDirectory.rva); DebugDirectory.size = adbg_bswap32(DebugDirectory.size); ArchitectureData.rva = adbg_bswap32(ArchitectureData.rva); ArchitectureData.size = adbg_bswap32(ArchitectureData.size); GlobalPtr.rva = adbg_bswap32(GlobalPtr.rva); GlobalPtr.size = adbg_bswap32(GlobalPtr.size); TLSTable.rva = adbg_bswap32(TLSTable.rva); TLSTable.size = adbg_bswap32(TLSTable.size); LoadConfigurationTable.rva = adbg_bswap32(LoadConfigurationTable.rva); LoadConfigurationTable.size = adbg_bswap32(LoadConfigurationTable.size); BoundImportTable.rva = adbg_bswap32(BoundImportTable.rva); BoundImportTable.size = adbg_bswap32(BoundImportTable.size); ImportAddressTable.rva = adbg_bswap32(ImportAddressTable.rva); ImportAddressTable.size = adbg_bswap32(ImportAddressTable.size); DelayImport.rva = adbg_bswap32(DelayImport.rva); DelayImport.size = adbg_bswap32(DelayImport.size); CLRHeader.rva = adbg_bswap32(CLRHeader.rva); CLRHeader.size = adbg_bswap32(CLRHeader.size); Reserved.rva = adbg_bswap32(Reserved.rva); Reserved.size = adbg_bswap32(Reserved.size); } if (o.p.reversed == false) return 0; if (o.i.pe.header.NumberOfSections) { o.i.pe.reversed_sections = cast(bool*) calloc(o.i.pe.header.NumberOfSections, bool.sizeof); if (o.i.pe.reversed_sections == null) return adbg_oops(AdbgError.crt); } with (o.i.pe.directory.ExportTable) if (size && rva) { size_t count = size / PE_EXPORT_DESCRIPTOR.sizeof; o.i.pe.reversed_dir_exports = false; o.i.pe.reversed_dir_export_entries = cast(bool*)calloc(count, bool.sizeof); if (o.i.pe.reversed_dir_export_entries == null) return adbg_oops(AdbgError.crt); } with (o.i.pe.directory.ImportTable) if (size && rva) { size_t count = size / PE_IMPORT_DESCRIPTOR.sizeof; o.i.pe.reversed_dir_imports = cast(bool*)calloc(count, bool.sizeof); if (o.i.pe.reversed_dir_imports == null) return adbg_oops(AdbgError.crt); } with (o.i.pe.directory.DebugDirectory) if (size && rva) { size_t count = size / PE_DEBUG_DIRECTORY.sizeof; o.i.pe.reversed_dir_debug = cast(bool*)calloc(count, bool.sizeof); if (o.i.pe.reversed_dir_debug == null) return adbg_oops(AdbgError.crt); } return 0; } //TODO: Calculate VA function // FileOffset = Section.RawPtr + (Directory.RVA - Section.RVA) // maps rva to section if found void* adbg_object_pe_locate(adbg_object_t *o, uint rva) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } uint sections = o.i.pe.header.NumberOfSections; for (uint si; si < sections; ++si) { PE_SECTION_ENTRY s = o.i.pe.sections[si]; uint va = s.VirtualAddress; version (Trace) trace("va=%x rva=%x", va, rva); if (va > rva || va + s.SizeOfRawData <= rva) continue; void* a = o.buffer + (s.PointerToRawData + (rva - va)); if (adbg_object_outboundp(o, a)) { adbg_oops(AdbgError.offsetBounds); return null; } return a; } version (Trace) trace("null"); adbg_oops(AdbgError.unfindable); return null; } PE_HEADER* adbg_object_pe_header(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } return o.i.pe.header; } PE_OPTIONAL_HEADER* adbg_object_pe_optheader(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } return o.i.pe.opt_header; } PE_OPTIONAL_HEADER64* adbg_object_pe_optheader64(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } return o.i.pe.opt_header64; } PE_OPTIONAL_HEADERROM* adbg_object_pe_optheaderrom(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } return o.i.pe.opt_headerrom; } PE_SECTION_ENTRY* adbg_object_pe_section(adbg_object_t *o, size_t index) { version (Trace) trace("o=%p index=%u", o, cast(uint)index); if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.sections == null) { adbg_oops(AdbgError.unavailable); return null; } if (index >= MAXIMUM_SECTIONS) { adbg_oops(AdbgError.indexBounds); return null; } if (index >= o.i.pe.header.NumberOfSections) { adbg_oops(AdbgError.indexBounds); return null; } PE_SECTION_ENTRY *section = &o.i.pe.sections[index]; if (o.p.reversed && o.i.pe.reversed_sections[index] == false) with (section) { VirtualSize = adbg_bswap32(VirtualSize); VirtualAddress = adbg_bswap32(VirtualAddress); SizeOfRawData = adbg_bswap32(SizeOfRawData); PointerToRawData = adbg_bswap32(PointerToRawData); PointerToRelocations = adbg_bswap32(PointerToRelocations); PointerToLinenumbers = adbg_bswap32(PointerToLinenumbers); NumberOfRelocations = adbg_bswap16(NumberOfRelocations); NumberOfLinenumbers = adbg_bswap16(NumberOfLinenumbers); Characteristics = adbg_bswap32(Characteristics); o.i.pe.reversed_sections[index] = true; } return section; } // TODO: // - [x] ExportTable // - [x] ImportTable // - [ ] ResourceTable // - [ ] ExceptionTable // - [ ] CertificateTable // - [ ] BaseRelocationTable // - [x] DebugDirectory // - [ ] ArchitectureData // - [ ] GlobalPtr // - [ ] TLSTable // - [ ] LoadConfigurationTable // - [ ] BoundImportTable // - [ ] ImportAddressTable // - [ ] DelayImport // - [ ] CLRHeader // // Export directory functions // // One descriptortable, multiple entries, because one module can emit one table. // Name -> Name of the module // ExportAddressTable -> raw address to RVAs // AddressTableEntries for count // RVA -> hint + entry PE_EXPORT_DESCRIPTOR* adbg_object_pe_export(adbg_object_t *o) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null) { adbg_oops(AdbgError.unavailable); // Due to PE-ROM return null; } // Set base with (o.i.pe) if (directory_exports == null) { directory_exports = cast(PE_EXPORT_DESCRIPTOR*) adbg_object_pe_locate(o, directory.ExportTable.rva); // Not found if (directory_exports == null) { adbg_oops(AdbgError.unavailable); return null; } } // adbg_object_pe_locate checked pointer bounds PE_EXPORT_DESCRIPTOR* exportdir = o.i.pe.directory_exports; // ExportFlags must be zero if (exportdir.ExportFlags != 0) { adbg_oops(AdbgError.unavailable); return null; } if (o.p.reversed && o.i.pe.reversed_dir_exports == false) with (exportdir) { ExportFlags = adbg_bswap32(ExportFlags); Timestamp = adbg_bswap32(Timestamp); MajorVersion = adbg_bswap16(MajorVersion); MinorVersion = adbg_bswap16(MinorVersion); Name = adbg_bswap32(Name); OrdinalBase = adbg_bswap32(OrdinalBase); AddressTableEntries = adbg_bswap32(AddressTableEntries); NumberOfNamePointers = adbg_bswap32(NumberOfNamePointers); ExportAddressTable = adbg_bswap32(ExportAddressTable); NamePointer = adbg_bswap32(NamePointer); OrdinalTable = adbg_bswap32(OrdinalTable); o.i.pe.reversed_dir_exports = true; } return exportdir; } const(char)* adbg_object_pe_export_name(adbg_object_t *o, PE_EXPORT_DESCRIPTOR *export_) { if (o == null || export_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null || o.i.pe.directory_exports == null) { adbg_oops(AdbgError.unavailable); // Due to PE-ROM return null; } const(char) *name = cast(const(char)*)o.i.pe.directory_exports - o.i.pe.directory.ExportTable.rva + export_.Name; if (adbg_object_outboundp(o, cast(void*)name)) { adbg_oops(AdbgError.offsetBounds); return null; } return name; } PE_EXPORT_ENTRY* adbg_object_pe_export_name_entry(adbg_object_t *o, PE_EXPORT_DESCRIPTOR *export_, size_t index) { if (o == null || export_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null || o.i.pe.directory_exports == null) { adbg_oops(AdbgError.unavailable); // Due to PE-ROM return null; } if (index >= export_.NumberOfNamePointers) { adbg_oops(AdbgError.indexBounds); return null; } // Check bounds with table RVA void *base = cast(void*)o.i.pe.directory_exports - o.i.pe.directory.ExportTable.rva + export_.NamePointer; if (adbg_object_outboundp(o, base)) { adbg_oops(AdbgError.offsetBounds); return null; } // Check bounds with name pointer and requested index PE_EXPORT_ENTRY *entry = cast(PE_EXPORT_ENTRY*)base + index; if (adbg_object_outboundp(o, entry)) { adbg_oops(AdbgError.offsetBounds); return null; } if (o.p.reversed && o.i.pe.reversed_dir_export_entries[index] == false) with (entry) { entry.Export = adbg_bswap32(entry.Export); o.i.pe.reversed_dir_export_entries[index] = true; } return entry; } const(char)* adbg_object_pe_export_name_string(adbg_object_t *o, PE_EXPORT_DESCRIPTOR *export_, PE_EXPORT_ENTRY *entry) { if (o == null || export_ == null || entry == null) { adbg_oops(AdbgError.invalidArgument); return null; } // NOTE: Export Table bounds // If the address specified is not within the export section (as // defined by the address and length that are indicated in the // optional header), the field is an Export RVA: an actual // address in code or data. Otherwise, the field is a Forwarder // RVA, which names a symbol in another DLL. //TODO: Forwarder check //if (entry.Export >= o.i.pe.directory.ExportTable.size) // return null; // Check bounds with table RVA void *base = cast(void*)o.i.pe.directory_exports - o.i.pe.directory.ExportTable.rva + entry.Export; if (adbg_object_outboundp(o, base)) { adbg_oops(AdbgError.offsetBounds); return null; } return cast(const(char)*)base; } // // Import directory functions // PE_IMPORT_DESCRIPTOR* adbg_object_pe_import(adbg_object_t *o, size_t index) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null) { adbg_oops(AdbgError.unavailable); // Due to PE-ROM return null; } size_t count = o.i.pe.directory.ImportTable.size / PE_IMPORT_DESCRIPTOR.sizeof; if (index >= count) { adbg_oops(AdbgError.indexBounds); return null; } // Set base if (o.i.pe.directory_imports == null) { o.i.pe.directory_imports = cast(PE_IMPORT_DESCRIPTOR*) adbg_object_pe_locate(o, o.i.pe.directory.ImportTable.rva); // Not found if (o.i.pe.directory_imports == null) { adbg_oops(AdbgError.unavailable); return null; } } PE_IMPORT_DESCRIPTOR* import_ = o.i.pe.directory_imports + index; if (import_.Characteristics == 0) { adbg_oops(AdbgError.unavailable); return null; } if (o.p.reversed && o.i.pe.reversed_dir_imports[index] == false) with (import_) { Characteristics = adbg_bswap32(Characteristics); TimeDateStamp = adbg_bswap32(TimeDateStamp); ForwarderChain = adbg_bswap32(ForwarderChain); Name = adbg_bswap32(Name); FirstThunk = adbg_bswap32(FirstThunk); o.i.pe.reversed_dir_imports[index] = true; } return import_; } char* adbg_object_pe_import_name(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_) { if (o == null || import_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null) { adbg_oops(AdbgError.unavailable); return null; } char *s = cast(char*)o.i.pe.directory_imports - o.i.pe.directory.ImportTable.rva + import_.Name; if (adbg_object_outboundp(o, s)) { adbg_oops(AdbgError.offsetBounds); return null; } return s; } //TODO: Byte-swap import look-up table entries PE_IMPORT_ENTRY32* adbg_object_pe_import_entry32(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_, size_t index) { if (o == null || import_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory_imports == null) { adbg_oops(AdbgError.unavailable); return null; } PE_IMPORT_ENTRY32* lte32 = cast(PE_IMPORT_ENTRY32*) (cast(char*)o.i.pe.directory_imports + (import_.Characteristics - o.i.pe.directory.ImportTable.rva)) + index; if (adbg_object_outboundp(o, lte32) || lte32.ordinal == 0) { adbg_oops(AdbgError.offsetBounds); return null; } return lte32; } ushort* adbg_object_pe_import_entry32_hint(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_, PE_IMPORT_ENTRY32 *im32) { if (o == null || import_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory_imports == null) { adbg_oops(AdbgError.unavailable); return null; } ushort* base = cast(ushort*) ((cast(char*)o.i.pe.directory_imports - o.i.pe.directory.ImportTable.rva) + im32.rva); if (adbg_object_outboundp(o, base)) { adbg_oops(AdbgError.offsetBounds); return null; } return base; } PE_IMPORT_ENTRY64* adbg_object_pe_import_entry64(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_, size_t index) { if (o == null || import_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory_imports == null) { adbg_oops(AdbgError.unavailable); return null; } PE_IMPORT_ENTRY64* lte64 = cast(PE_IMPORT_ENTRY64*) (cast(char*)o.i.pe.directory_imports + (import_.Characteristics - o.i.pe.directory.ImportTable.rva)) + index; version (Trace) trace("imports=%p lte64=%p fs=%zx", o.i.pe.directory_imports, lte64, o.file_size); if (adbg_object_outboundp(o, lte64) || lte64.ordinal == 0) { adbg_oops(AdbgError.offsetBounds); return null; } return lte64; } ushort* adbg_object_pe_import_entry64_hint(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_, PE_IMPORT_ENTRY64 *im64) { if (o == null || import_ == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory_imports == null) { adbg_oops(AdbgError.unavailable); return null; } ushort* base = cast(ushort*) ((cast(char*)o.i.pe.directory_imports - o.i.pe.directory.ImportTable.rva) + im64.rva); version (Trace) trace("base=%p fs=%zx", base, o.file_size); if (adbg_object_outboundp(o, base)) { adbg_oops(AdbgError.offsetBounds); return null; } return base; } // Import Directory Table -1,*-> lookup tables -1,1-> hint // Helper function /+const(char)* adbg_object_pe_import_string_entry(adbg_object_t *o, PE_IMPORT_DESCRIPTOR *import_, size_t index) { if (o == null || import_ == null) return null; if (o.i.pe.directory_imports == null) return null; switch (o.i.pe.opt_header.Magic) { case PE_FMT_32: PE_IMPORT_LTE32 *t32 = adbg_object_pe_import_lte32(o, import_, index); if (t32 == null) return null; return; case PE_FMT_64: return; default: return null; } }+/ // // Debug directory functions // PE_DEBUG_DIRECTORY* adbg_object_pe_debug_directory(adbg_object_t *o, size_t index) { if (o == null) { adbg_oops(AdbgError.invalidArgument); return null; } if (o.i.pe.directory == null) { adbg_oops(AdbgError.unavailable); // Due to PE-ROM return null; } size_t count = o.i.pe.directory.DebugDirectory.size / PE_DEBUG_DIRECTORY.sizeof; if (index >= count) { adbg_oops(AdbgError.indexBounds); return null; } // Set base if (o.i.pe.directory_debug == null) { o.i.pe.directory_debug = cast(PE_DEBUG_DIRECTORY*) adbg_object_pe_locate(o, o.i.pe.directory.DebugDirectory.rva); // Not found if (o.i.pe.directory_debug == null) { adbg_oops(AdbgError.unavailable); return null; } } PE_DEBUG_DIRECTORY* debug_ = o.i.pe.directory_debug + index; if (o.p.reversed && o.i.pe.reversed_dir_debug[index] == false) with (debug_) { Characteristics = adbg_bswap32(Characteristics); TimeDateStamp = adbg_bswap32(TimeDateStamp); MajorVersion = adbg_bswap16(MajorVersion); MinorVersion = adbg_bswap16(MinorVersion); Type = adbg_bswap32(Type); SizeOfData = adbg_bswap32(SizeOfData); AddressOfRawData = adbg_bswap32(AddressOfRawData); PointerToRawData = adbg_bswap32(PointerToRawData); o.i.pe.reversed_dir_debug[index] = true; } return debug_; } // // Other helpers // AdbgMachine adbg_object_pe_machine(ushort machine) { switch (machine) { case PE_MACHINE_I386: return AdbgMachine.i386; case PE_MACHINE_AMD64: return AdbgMachine.amd64; case PE_MACHINE_ALPHAOLD, PE_MACHINE_ALPHA: return AdbgMachine.alpha; case PE_MACHINE_ALPHA64: return AdbgMachine.alpha64; case PE_MACHINE_AM33: return AdbgMachine.am33; case PE_MACHINE_ARM: case PE_MACHINE_ARMNT: return AdbgMachine.arm; case PE_MACHINE_ARM64: return AdbgMachine.aarch64; case PE_MACHINE_EBC: return AdbgMachine.ebc; case PE_MACHINE_IA64: return AdbgMachine.ia64; case PE_MACHINE_LOONGARCH32: return AdbgMachine.loongarch32; case PE_MACHINE_LOONGARCH64: return AdbgMachine.loongarch64; case PE_MACHINE_M32R: return AdbgMachine.m32r; case PE_MACHINE_MIPS16: return AdbgMachine.mips16; case PE_MACHINE_MIPSFPU: return AdbgMachine.mipsfpu; case PE_MACHINE_MIPSFPU16: return AdbgMachine.mips16fpu; case PE_MACHINE_POWERPC: return AdbgMachine.ppc; case PE_MACHINE_POWERPCFP: return AdbgMachine.ppcfpu; case PE_MACHINE_R3000: return AdbgMachine.mips; case PE_MACHINE_R4000: return AdbgMachine.mipsii; case PE_MACHINE_R10000: return AdbgMachine.mipsiv; case PE_MACHINE_RISCV32: return AdbgMachine.riscv32; case PE_MACHINE_RISCV64: return AdbgMachine.riscv64; case PE_MACHINE_RISCV128: return AdbgMachine.riscv128; case PE_MACHINE_SH3: return AdbgMachine.sh3; case PE_MACHINE_SH3DSP: return AdbgMachine.sh3dsp; case PE_MACHINE_SH4: return AdbgMachine.sh4; case PE_MACHINE_SH5: return AdbgMachine.sh5; case PE_MACHINE_THUMB: return AdbgMachine.thumb; case PE_MACHINE_WCEMIPSV2: return AdbgMachine.mipswcele; case PE_MACHINE_CLR: return AdbgMachine.clr; default: return AdbgMachine.unknown; } } const(char) *adbg_object_pe_machine_string(ushort machine) { switch (machine) { case PE_MACHINE_UNKNOWN: return "None"; case PE_MACHINE_ALPHA: return "DEC Alpha"; case PE_MACHINE_ALPHA64: return "DEC Alpha (64-bit)"; case PE_MACHINE_AM33: return "Mitsubishi MN10300 (AM33)"; case PE_MACHINE_AMD64: return "x86-64"; case PE_MACHINE_ARM: return "ARM (32-bit)"; case PE_MACHINE_ARMNT: return "ARM Thumb-2 (32-bit)"; case PE_MACHINE_ARM64: return "ARM (64-bit)"; case PE_MACHINE_EBC: return "EFI Byte Code"; case PE_MACHINE_I386: return "Intel x86"; case PE_MACHINE_IA64: return "Intel Itanium Architecture 64"; case PE_MACHINE_LOONGARCH32: return "LoongArch32"; case PE_MACHINE_LOONGARCH64: return "LoongArch64"; case PE_MACHINE_M32R: return "Mitsubishi M32R"; case PE_MACHINE_MIPS16: return "MIPS16"; case PE_MACHINE_MIPSFPU: return "MIPS I with FPU"; case PE_MACHINE_MIPSFPU16: return "MIPS16 with FPU"; case PE_MACHINE_POWERPC: return "PowerPC"; case PE_MACHINE_POWERPCFP: return "PowerPC with FPU"; case PE_MACHINE_R3000: return "MIPS I (RS3000) Little-Endian"; case PE_MACHINE_R4000: return "MIPS III (R4000)"; case PE_MACHINE_R10000: return "MIPS IV (R10000)"; case PE_MACHINE_RISCV32: return "RISC-V (32-bit)"; case PE_MACHINE_RISCV64: return "RISC-V (64-bit)"; case PE_MACHINE_RISCV128: return "RISC-V (128-bit)"; case PE_MACHINE_SH3: return "Hitachi SuperH 3"; case PE_MACHINE_SH3DSP: return "Hitachi SuperH 3 DSP"; case PE_MACHINE_SH4: return "Hitachi SuperH 4"; case PE_MACHINE_SH5: return "Hitachi SuperH 5"; case PE_MACHINE_THUMB: return "ARM Thumb"; case PE_MACHINE_WCEMIPSV2: return "MIPS little-endian WCE v2"; case PE_MACHINE_CLR: return "Common Language Runtime"; default: return null; } } const(char) *adbg_object_pe_magic_string(ushort magic) { switch (magic) { case PE_FMT_32: return "PE32"; case PE_FMT_64: return "PE32+"; case PE_FMT_ROM: return "PE-ROM"; default: return null; } } const(char) *adbg_object_pe_subsys_string(ushort subsystem) { switch (subsystem) { case PE_SUBSYSTEM_NATIVE: return "Native"; case PE_SUBSYSTEM_WINDOWS_GUI: return "Windows GUI"; case PE_SUBSYSTEM_WINDOWS_CUI: return "Windows Console"; case PE_SUBSYSTEM_POSIX_CUI: return "Posix Console"; case PE_SUBSYSTEM_NATIVE_WINDOWS: return "Native Windows 9x Driver"; case PE_SUBSYSTEM_WINDOWS_CE_GUI: return "Windows CE GUI"; case PE_SUBSYSTEM_EFI_APPLICATION: return "EFI"; case PE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: return "EFI Boot Service Driver"; case PE_SUBSYSTEM_EFI_RUNTIME_DRIVER: return "EFI Runtime Driver"; case PE_SUBSYSTEM_EFI_ROM: return "EFI ROM"; case PE_SUBSYSTEM_XBOX: return "XBOX"; case PE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: return "Windows Boot"; case PE_SUBSYSTEM_XBOX_CODE_CATALOG: return "XBOX Code Catalog"; default: return null; } } const(char) *adbg_object_pe_debug_type_string(uint type) { switch (type) { case PE_IMAGE_DEBUG_TYPE_UNKNOWN: return "Unknown"; case PE_IMAGE_DEBUG_TYPE_COFF: return "COFF"; case PE_IMAGE_DEBUG_TYPE_CODEVIEW: return "CodeView / VC++"; case PE_IMAGE_DEBUG_TYPE_FPO: return "FPO (Frame Pointer Omission) Information"; case PE_IMAGE_DEBUG_TYPE_MISC: return "DBG File Location"; case PE_IMAGE_DEBUG_TYPE_EXCEPTION: return "Exception"; case PE_IMAGE_DEBUG_TYPE_FIXUP: return "FIXUP"; case PE_IMAGE_DEBUG_TYPE_OMAP_TO_SRC: return "Map RVA to source image RVA"; case PE_IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: return "Map source image RVA to RVA"; case PE_IMAGE_DEBUG_TYPE_BORLAND: return "Borland"; case PE_IMAGE_DEBUG_TYPE_RESERVED10: return "RESERVED10"; case PE_IMAGE_DEBUG_TYPE_CLSID: return "CLSID"; case PE_IMAGE_DEBUG_TYPE_VC_FEATURE: return "VC FEATURE"; case PE_IMAGE_DEBUG_TYPE_POGO: return "Profile Guided Optimization (POGO)"; case PE_IMAGE_DEBUG_TYPE_ILTCG: return "Incremental Link Time Code Generation (ILTCG)"; case PE_IMAGE_DEBUG_TYPE_MPX: return "Memory protection (Intel MPX)"; case PE_IMAGE_DEBUG_TYPE_REPRO: return "PE Reproducibility"; case PE_IMAGE_DEBUG_TYPE_EMBEDDED: return "Embedded Portable PDB Debug Directory Entry"; case PE_IMAGE_DEBUG_TYPE_HASH: return "PDB Hash"; case PE_IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS: return "DLL characteristics"; case PE_IMAGE_DEBUG_TYPE_R2R_PERFMAP: return "R2R PerfMap Debug Directory Entry"; default: return null; } } private: // Rough guesses for OS limits, offsets+4 since missing Size (already read) // If the count is still misleading, the size check will be performed by field //TODO: Shouldn't this depend more or less on the linker version? // Examples: // putty-x86 0.73: 92 // putty-amd64 0.73: 148 enum PE_LOAD_CONFIG32_LIMIT_XP = 64; enum PE_LOAD_CONFIG32_LIMIT_7 = PE_LOAD_CONFIG_DIR32.GuardFlags.offsetof + 4; enum PE_LOAD_CONFIG32_LIMIT_8 = PE_LOAD_CONFIG_DIR32.GuardLongJumpTargetCount.offsetof + 4; enum PE_LOAD_CONFIG64_LIMIT_XP = PE_LOAD_CONFIG_DIR64.SecurityCookie.offsetof + 4; enum PE_LOAD_CONFIG64_LIMIT_7 = PE_LOAD_CONFIG_DIR64.GuardFlags.offsetof + 4; enum PE_LOAD_CONFIG64_LIMIT_8 = PE_LOAD_CONFIG_DIR64.GuardLongJumpTargetCount.offsetof + 4; enum int PE_DIRECTORY_SIZE = PE_IMAGE_DATA_DIRECTORY.sizeof; enum int PE_OHDR_SIZE = PE_OPTIONAL_HEADER.sizeof + PE_DIRECTORY_SIZE; // PE32 enum int PE_OHDR64_SIZE = PE_OPTIONAL_HEADER64.sizeof + PE_DIRECTORY_SIZE; // PE32+ enum int PE_OHDRROM_SIZE = PE_OPTIONAL_HEADERROM.sizeof + PE_DIRECTORY_SIZE; // PE-ROM enum PE_OFFSET_OPTHDR = PE_HEADER.sizeof; enum PE_OFFSET_DIR_OPTHDR32 = PE_OFFSET_OPTHDR + PE_OPTIONAL_HEADER.sizeof; enum PE_OFFSET_DIR_OPTHDR64 = PE_OFFSET_OPTHDR + PE_OPTIONAL_HEADER64.sizeof; enum PE_OFFSET_DIR_OPTHDRROM = PE_OFFSET_OPTHDR + PE_OPTIONAL_HEADERROM.sizeof; enum PE_OFFSET_SEC_OPTHDR32 = PE_OFFSET_DIR_OPTHDR32 + PE_IMAGE_DATA_DIRECTORY.sizeof; enum PE_OFFSET_SEC_OPTHDR64 = PE_OFFSET_DIR_OPTHDR64 + PE_IMAGE_DATA_DIRECTORY.sizeof; enum PE_OFFSET_SEC_OPTHDRROM = PE_OFFSET_DIR_OPTHDRROM + PE_IMAGE_DATA_DIRECTORY.sizeof;