diff --git a/BurnOutSharp/ExecutableType/Microsoft/Constants.cs b/BurnOutSharp/ExecutableType/Microsoft/Constants.cs index 7c3205a9..41bf3ae8 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Constants.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Constants.cs @@ -56,31 +56,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft public const ushort IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; - /* Directory Entries */ - - /* Export Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_EXPORT = 0; - /* Import Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_IMPORT = 1; - /* Resource Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; - /* Exception Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; - /* Security Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_SECURITY = 4; - /* Base Relocation Table */ - public const byte IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; - /* Debug Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_DEBUG = 6; - /* Description String */ - public const byte IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7; - /* Machine Value (MIPS GP) */ - public const byte IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; - /* TLS Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_TLS = 9; - /* Load Configuration Directory */ - public const byte IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; - #endregion #region IMAGE_SECTION_HEADER diff --git a/BurnOutSharp/ExecutableType/Microsoft/Enums.cs b/BurnOutSharp/ExecutableType/Microsoft/Enums.cs index 47080059..374d4bd2 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Enums.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Enums.cs @@ -1,15 +1,3 @@ -/* - * NEWEXE.H (C) Copyright Microsoft Corp 1984-1987 - * - * Data structure definitions for the OS/2 & Windows - * executable file format. - * - * Modified by IVS on 24-Jan-1991 for Resource DeCompiler - * (C) Copyright IVS 1991 - * - * http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h - */ - using System; namespace BurnOutSharp.ExecutableType.Microsoft @@ -21,6 +9,401 @@ namespace BurnOutSharp.ExecutableType.Microsoft PE, } + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#dll-characteristics + [Flags] + internal enum DllCharacteristics : ushort + { + /// + /// Reserved, must be zero. + /// + Reserved1 = 0x0001, + + /// + /// Reserved, must be zero. + /// + Reserved2 = 0x0002, + + /// + /// Reserved, must be zero. + /// + Reserved4 = 0x0004, + + /// + /// Reserved, must be zero. + /// + Reserved8 = 0x0008, + + /// + /// Image can handle a high entropy 64-bit virtual address space. + /// + IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020, + + /// + /// DLL can be relocated at load time. + /// + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040, + + /// + /// Code Integrity checks are enforced. + /// + IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080, + + /// + /// Image is NX compatible. + /// + IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100, + + /// + /// Isolation aware, but do not isolate the image. + /// + IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, + + /// + /// Does not use structured exception (SE) handling. + /// No SE handler may be called in this image. + /// + IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, + + /// + /// Do not bind the image. + /// + IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, + + /// + /// Image must execute in an AppContainer. + /// + IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000, + + /// + /// A WDM driver. + /// + IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, + + /// + /// Image supports Control Flow Guard. + /// + IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000, + + /// + /// Terminal Server aware. + /// + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000, + } + + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-data-directories-image-only + internal enum ImageDirectory : byte + { + /// + /// The export table address and size. (.edata) + /// + IMAGE_DIRECTORY_ENTRY_EXPORT = 0, + + /// + /// The import table address and size. (.idata) + /// + IMAGE_DIRECTORY_ENTRY_IMPORT = 1, + + /// + /// The resource table address and size. (.rsrc) + /// + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, + + /// + /// The exception table address and size. (.pdata) + /// + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, + + /// + /// The attribute certificate table address and size. + /// + IMAGE_DIRECTORY_ENTRY_SECURITY = 4, + + /// + /// The base relocation table address and size. (.reloc) + /// + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, + + /// + /// The debug data starting address and size. (.debug) + /// + IMAGE_DIRECTORY_ENTRY_DEBUG = 6, + + /// + /// Reserved, must be 0 + /// + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, + + /// + /// The RVA of the value to be stored in the global pointer register. + /// The size member of this structure must be set to zero. + /// + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, + + /// + /// The thread local storage (TLS) table address and size. (.tls) + /// + IMAGE_DIRECTORY_ENTRY_TLS = 9, + + /// + /// The load configuration table address and size. + /// + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, + + /// + /// The bound import table address and size. + /// + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, + + /// + /// The import address table address and size. + /// + IMAGE_DIRECTORY_ENTRY_IAT = 12, + + /// + /// The delay import descriptor address and size. + /// + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, + + /// + /// The CLR runtime header address and size. (.cormeta) + /// + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14, + + /// + /// Reserved, must be zero + /// + IMAGE_DIRECTORY_RESERVED = 15, + } + + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics + [Flags] + internal enum ImageObjectCharacteristics : ushort + { + /// + /// Image only, Windows CE, and Microsoft Windows NT and later. + /// This indicates that the file does not contain base relocations and must therefore be loaded at its preferred base address. + /// If the base address is not available, the loader reports an error. + /// The default behavior of the linker is to strip base relocations from executable (EXE) files. + /// + IMAGE_FILE_RELOCS_STRIPPED = 0x0001, + + /// + /// Image only. This indicates that the image file is valid and can be run. + /// If this flag is not set, it indicates a linker error. + /// + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002, + + /// + /// COFF line numbers have been removed. + /// This flag is deprecated and should be zero. + /// + [Obsolete] + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004, + + /// + /// COFF symbol table entries for local symbols have been removed. + /// This flag is deprecated and should be zero. + /// + [Obsolete] + IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008, + + /// + /// Obsolete. Aggressively trim working set. + /// This flag is deprecated for Windows 2000 and later and must be zero. + /// + [Obsolete] + IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010, + + /// + /// Application can handle > 2-GB addresses. + /// + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020, + + /// + /// This flag is reserved for future use. + /// + Reserved64 = 0x0040, + + /// + /// Little endian: the least significant bit (LSB) precedes the most significant bit (MSB) in memory. + /// This flag is deprecated and should be zero. + /// + [Obsolete] + IMAGE_FILE_BYTES_REVERSED_LO = 0x0080, + + /// + /// Machine is based on a 32-bit-word architecture. + /// + IMAGE_FILE_32BIT_MACHINE = 0x0100, + + /// + /// Debugging information is removed from the image file. + /// + IMAGE_FILE_DEBUG_STRIPPED = 0x0200, + + /// + /// If the image is on removable media, fully load it and copy it to the swap file. + /// + IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400, + + /// + /// If the image is on network media, fully load it and copy it to the swap file. + /// + IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800, + + /// + /// The image file is a system file, not a user program. + /// + IMAGE_FILE_SYSTEM = 0x1000, + + /// + /// The image file is a dynamic-link library (DLL). + /// Such files are considered executable files for almost all purposes, although they cannot be directly run. + /// + IMAGE_FILE_DLL = 0x2000, + + /// + /// The file should be run only on a uniprocessor machine. + /// + IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000, + + /// + /// Big endian: the MSB precedes the LSB in memory. + /// This flag is deprecated and should be zero. + /// + [Obsolete] + IMAGE_FILE_BYTES_REVERSED_HI = 0x8000, + } + + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types + internal enum MachineType : ushort + { + /// + /// The content of this field is assumed to be applicable to any machine type + /// + IMAGE_FILE_MACHINE_UNKNOWN = 0x0, + + /// + /// Matsushita AM33 + /// + IMAGE_FILE_MACHINE_AM33 = 0x1d3, + + /// + /// x64 + /// + IMAGE_FILE_MACHINE_AMD64 = 0x8664, + + /// + /// ARM little endian + /// + IMAGE_FILE_MACHINE_ARM = 0x1c0, + + /// + /// ARM64 little endian + /// + IMAGE_FILE_MACHINE_ARM64 = 0xaa64, + + /// + /// ARM Thumb-2 little endian + /// + IMAGE_FILE_MACHINE_ARMNT = 0x1c4, + + /// + /// EFI byte code + /// + IMAGE_FILE_MACHINE_EBC = 0xebc, + + /// + /// Intel 386 or later processors and compatible processors + /// + IMAGE_FILE_MACHINE_I386 = 0x14c, + + /// + /// Intel Itanium processor family + /// + IMAGE_FILE_MACHINE_IA64 = 0x200, + + /// + /// Mitsubishi M32R little endian + /// + IMAGE_FILE_MACHINE_M32R = 0x9041, + + /// + /// MIPS16 + /// + IMAGE_FILE_MACHINE_MIPS16 = 0x266, + + /// + /// MIPS with FPU + /// + IMAGE_FILE_MACHINE_MIPSFPU = 0x366, + + /// + /// MIPS16 with FPU + /// + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466, + + /// + /// Power PC little endian + /// + IMAGE_FILE_MACHINE_POWERPC = 0x1f0, + + /// + /// Power PC with floating point support + /// + IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1, + + /// + /// MIPS little endian + /// + IMAGE_FILE_MACHINE_R4000 = 0x166, + + /// + /// RISC-V 32-bit address space + /// + IMAGE_FILE_MACHINE_RISCV32 = 0x5032, + + /// + /// RISC-V 64-bit address space + /// + IMAGE_FILE_MACHINE_RISCV64 = 0x5064, + + /// + /// RISC-V 128-bit address space + /// + IMAGE_FILE_MACHINE_RISCV128 = 0x5128, + + /// + /// Hitachi SH3 + /// + IMAGE_FILE_MACHINE_SH3 = 0x1a2, + + /// + /// Hitachi SH3 DSP + /// + IMAGE_FILE_MACHINE_SH3DSP = 0x1a3, + + /// + /// Hitachi SH4 + /// + IMAGE_FILE_MACHINE_SH4 = 0x1a6, + + /// + /// Hitachi SH5 + /// + IMAGE_FILE_MACHINE_SH5 = 0x1a8, + + /// + /// Thumb + /// + IMAGE_FILE_MACHINE_THUMB = 0x1c2, + + /// + /// MIPS little-endian WCE v2 + /// + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169, + } + /// /// Format of NE_FLAGS(x): /// @@ -306,6 +689,25 @@ namespace BurnOutSharp.ExecutableType.Microsoft NSHUGE = 0x4000, } + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only + internal enum OptionalHeaderType : ushort + { + /// + /// ROM image + /// + ROMImage = 0x107, + + /// + /// Normal executable file + /// + PE32 = 0x10b, + + /// + /// PE32+ images allow for a 64-bit address space while limiting the image size to 2 gigabytes. + /// + PE32Plus = 0x20b, + } + /// /// Predefined Resource Types /// @@ -339,18 +741,220 @@ namespace BurnOutSharp.ExecutableType.Microsoft RT_ERROR = 0x7fff, } + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-flags [Flags] internal enum SectionCharacteristics : uint { - CodeSection = 0x00000020, - InitializedDataSection = 0x00000040, - UninitializedDataSection = 0x00000080, - SectionCannotBeCached = 0x04000000, - SectionIsNotPageable = 0x08000000, - SectionIsShared = 0x10000000, - ExecutableSection = 0x20000000, - ReadableSection = 0x40000000, - WritableSection = 0x80000000, + /// + /// Reserved for future use. + /// + Reserved0 = 0x00000000, + + /// + /// Reserved for future use. + /// + Reserved1 = 0x00000001, + + /// + /// Reserved for future use. + /// + Reserved2 = 0x00000002, + + /// + /// Reserved for future use. + /// + Reserved4 = 0x00000004, + + /// + /// The section should not be padded to the next boundary. + /// This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. + /// This is valid only for object files. + /// + IMAGE_SCN_TYPE_NO_PAD = 0x00000008, + + /// + /// Reserved for future use. + /// + Reserved16 = 0x00000010, + + /// + /// The section contains executable code. + /// + IMAGE_SCN_CNT_CODE = 0x00000020, + + /// + /// The section contains initialized data. + /// + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, + + /// + /// The section contains uninitialized data. + /// + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, + + /// + /// Reserved for future use. + /// + IMAGE_SCN_LNK_OTHER = 0x00000100, + + /// + /// The section contains comments or other information. + /// The .drectve section has this type. + /// This is valid for object files only. + /// + IMAGE_SCN_LNK_INFO = 0x00000200, + + /// + /// Reserved for future use. + /// + Reserved1024 = 0x00000400, + + /// + /// The section will not become part of the image. + /// This is valid only for object files. + /// + IMAGE_SCN_LNK_REMOVE = 0x00000800, + + /// + /// The section contains COMDAT data. + /// This is valid only for object files. + /// + IMAGE_SCN_LNK_COMDAT = 0x00001000, + + /// + /// The section contains data referenced through the global pointer (GP). + /// + IMAGE_SCN_GPREL = 0x00008000, + + /// + /// Reserved for future use. + /// + IMAGE_SCN_MEM_PURGEABLE = 0x00010000, + + /// + /// Reserved for future use. + /// + IMAGE_SCN_MEM_16BIT = 0x00020000, + + /// + /// Reserved for future use. + /// + IMAGE_SCN_MEM_LOCKED = 0x00040000, + + /// + /// Reserved for future use. + /// + IMAGE_SCN_MEM_PRELOAD = 0x00080000, + + /// + /// Align data on a 1-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_1BYTES = 0x00100000, + + /// + /// Align data on a 2-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_2BYTES = 0x00200000, + + /// + /// Align data on a 4-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_4BYTES = 0x00300000, + + /// + /// Align data on a 8-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_8BYTES = 0x00400000, + + /// + /// Align data on a 16-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_16BYTES = 0x00500000, + + /// + /// Align data on a 32-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_32BYTES = 0x00600000, + + /// + /// Align data on a 64-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_64BYTES = 0x00700000, + + /// + /// Align data on a 128-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_128BYTES = 0x00800000, + + /// + /// Align data on a 256-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_256BYTES = 0x00900000, + + /// + /// Align data on a 512-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_512BYTES = 0x00A00000, + + /// + /// Align data on a 1024-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000, + + /// + /// Align data on a 2048-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000, + + /// + /// Align data on a 4096-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000, + + /// + /// Align data on a 8192-byte boundary. Valid only for object files. + /// + IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000, + + /// + /// The section contains extended relocations. + /// + IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, + + /// + /// The section can be discarded as needed. + /// + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, + + /// + /// The section cannot be cached. + /// + IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, + + /// + /// The section is not pageable. + /// + IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, + + /// + /// The section can be shared in memory. + /// + IMAGE_SCN_MEM_SHARED = 0x10000000, + + /// + /// The section can be executed as code. + /// + IMAGE_SCN_MEM_EXECUTE = 0x20000000, + + /// + /// The section can be read. + /// + IMAGE_SCN_MEM_READ = 0x40000000, + + /// + /// The section can be written to. + /// + IMAGE_SCN_MEM_WRITE = 0x80000000, } [Flags] @@ -381,4 +985,78 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// NE_WIN386 = 0x4, } + + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#windows-subsystem + internal enum WindowsSubsystem : ushort + { + /// + /// An unknown subsystem + /// + IMAGE_SUBSYSTEM_UNKNOWN = 0, + + /// + /// Device drivers and native Windows processes + /// + IMAGE_SUBSYSTEM_NATIVE = 1, + + /// + /// The Windows graphical user interface (GUI) subsystem + /// + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, + + /// + /// The Windows character subsystem + /// + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, + + /// + /// The OS/2 character subsystem + /// + IMAGE_SUBSYSTEM_OS2_CUI = 5, + + /// + /// The Posix character subsystem + /// + IMAGE_SUBSYSTEM_POSIX_CUI = 7, + + /// + /// Native Win9x driver + /// + IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8, + + /// + /// Windows CE + /// + IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, + + /// + /// An Extensible Firmware Interface (EFI) application + /// + IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, + + /// + /// An EFI driver with boot services + /// + IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, + + /// + /// An EFI driver with run-time services + /// + IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, + + /// + /// An EFI ROM image + /// + IMAGE_SUBSYSTEM_EFI_ROM = 13, + + /// + /// XBOX + /// + IMAGE_SUBSYSTEM_XBOX = 14, + + /// + /// Windows boot application. + /// + IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16, + } } \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs index ad100a40..42f36217 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs @@ -1,15 +1,3 @@ -/* - * NEWEXE.H (C) Copyright Microsoft Corp 1984-1987 - * - * Data structure definitions for the OS/2 & Windows - * executable file format. - * - * Modified by IVS on 24-Jan-1991 for Resource DeCompiler - * (C) Copyright IVS 1991 - * - * http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h - */ - using System; using System.IO; using System.Runtime.InteropServices; @@ -20,7 +8,15 @@ namespace BurnOutSharp.ExecutableType.Microsoft [StructLayout(LayoutKind.Sequential)] internal class IMAGE_DATA_DIRECTORY { + /// + /// The first field, VirtualAddress, is actually the RVA of the table. + /// The RVA is the address of the table relative to the base address of the image when the table is loaded. + /// public uint VirtualAddress; + + /// + /// The second field gives the size in bytes. + /// public uint Size; public static IMAGE_DATA_DIRECTORY Deserialize(Stream stream) diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs index c41d74a3..53946f6b 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs @@ -1,15 +1,3 @@ -/* - * NEWEXE.H (C) Copyright Microsoft Corp 1984-1987 - * - * Data structure definitions for the OS/2 & Windows - * executable file format. - * - * Modified by IVS on 24-Jan-1991 for Resource DeCompiler - * (C) Copyright IVS 1991 - * - * http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h - */ - using System; using System.IO; using System.Runtime.InteropServices; @@ -20,27 +8,65 @@ namespace BurnOutSharp.ExecutableType.Microsoft [StructLayout(LayoutKind.Sequential)] internal class IMAGE_FILE_HEADER { + /// + /// After the MS-DOS stub, at the file offset specified at offset 0x3c, is a 4-byte signature that identifies the file as a PE format image file. + // This signature is "PE\0\0" (the letters "P" and "E" followed by two null bytes). + /// public uint Signature; - public ushort Machine; + + /// + /// The number that identifies the type of target machine. + /// + public MachineType Machine; + + /// + /// The number of sections. + /// This indicates the size of the section table, which immediately follows the headers. + /// public ushort NumberOfSections; + + /// + /// The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), which indicates when the file was created. + /// public uint TimeDateStamp; + + /// + /// The file offset of the COFF symbol table, or zero if no COFF symbol table is present. + /// This value should be zero for an image because COFF debugging information is deprecated. + /// + [Obsolete] public uint PointerToSymbolTable; + + /// + /// The number of entries in the symbol table. This data can be used to locate the string table, which immediately follows the symbol table. + /// This value should be zero for an image because COFF debugging information is deprecated. + /// + [Obsolete] public uint NumberOfSymbols; + + /// + /// The size of the optional header, which is required for executable files but not for object files. + // This value should be zero for an object file. + /// public ushort SizeOfOptionalHeader; - public ushort Characteristics; + + /// + /// The flags that indicate the attributes of the file. + /// + public ImageObjectCharacteristics Characteristics; public static IMAGE_FILE_HEADER Deserialize(Stream stream) { var ifh = new IMAGE_FILE_HEADER(); ifh.Signature = stream.ReadUInt32(); - ifh.Machine = stream.ReadUInt16(); + ifh.Machine = (MachineType)stream.ReadUInt16(); ifh.NumberOfSections = stream.ReadUInt16(); ifh.TimeDateStamp = stream.ReadUInt32(); ifh.PointerToSymbolTable = stream.ReadUInt32(); ifh.NumberOfSymbols = stream.ReadUInt32(); ifh.SizeOfOptionalHeader = stream.ReadUInt16(); - ifh.Characteristics = stream.ReadUInt16(); + ifh.Characteristics = (ImageObjectCharacteristics)stream.ReadUInt16(); return ifh; } @@ -50,13 +76,13 @@ namespace BurnOutSharp.ExecutableType.Microsoft var ifh = new IMAGE_FILE_HEADER(); ifh.Signature = BitConverter.ToUInt32(content, offset); offset += 4; - ifh.Machine = BitConverter.ToUInt16(content, offset); offset += 2; + ifh.Machine = (MachineType)BitConverter.ToUInt16(content, offset); offset += 2; ifh.NumberOfSections = BitConverter.ToUInt16(content, offset); offset += 2; ifh.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4; ifh.PointerToSymbolTable = BitConverter.ToUInt32(content, offset); offset += 4; ifh.NumberOfSymbols = BitConverter.ToUInt32(content, offset); offset += 4; ifh.SizeOfOptionalHeader = BitConverter.ToUInt16(content, offset); offset += 2; - ifh.Characteristics = BitConverter.ToUInt16(content, offset); offset += 2; + ifh.Characteristics = (ImageObjectCharacteristics)BitConverter.ToUInt16(content, offset); offset += 2; return ifh; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs index 4ee3974e..145f9fa5 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs @@ -1,15 +1,3 @@ -/* - * NEWEXE.H (C) Copyright Microsoft Corp 1984-1987 - * - * Data structure definitions for the OS/2 & Windows - * executable file format. - * - * Modified by IVS on 24-Jan-1991 for Resource DeCompiler - * (C) Copyright IVS 1991 - * - * http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h - */ - using System; using System.IO; using System.Runtime.InteropServices; @@ -20,49 +8,225 @@ namespace BurnOutSharp.ExecutableType.Microsoft [StructLayout(LayoutKind.Sequential)] internal class IMAGE_OPTIONAL_HEADER { - // Standard fields + #region Standard Fields - public ushort Magic; + /// + /// The unsigned integer that identifies the state of the image file. + /// The most common number is 0x10B, which identifies it as a normal executable file. + /// 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable. + /// + public OptionalHeaderType Magic; + + /// + /// The linker major version number. + /// public byte MajorLinkerVersion; + + /// + /// The linker minor version number. + /// public byte MinorLinkerVersion; + + /// + /// The size of the code (text) section, or the sum of all code sections if there are multiple sections. + /// public uint SizeOfCode; + + /// + /// The size of the initialized data section, or the sum of all such sections if there are multiple data sections. + /// public uint SizeOfInitializedData; + + /// + /// The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections. + /// public uint SizeOfUninitializedData; + + /// + /// The address of the entry point relative to the image base when the executable file is loaded into memory. + /// For program images, this is the starting address. + /// For device drivers, this is the address of the initialization function. + // An entry point is optional for DLLs. + /// When no entry point is present, this field must be zero. + /// public uint AddressOfEntryPoint; + + /// + /// The address that is relative to the image base of the beginning-of-code section when it is loaded into memory. + /// public uint BaseOfCode; + + /// + /// The address that is relative to the image base of the beginning-of-data section when it is loaded into memory. + /// public uint BaseOfData; - // NT additional fields. + #endregion - public uint ImageBase; + #region Windows-Specific Fields + + /// + /// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. + /// The default for DLLs is 0x10000000. + /// The default for Windows CE EXEs is 0x00010000. + /// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. + /// + public uint ImageBasePE32; + + /// + /// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K. + /// The default for DLLs is 0x10000000. + /// The default for Windows CE EXEs is 0x00010000. + /// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000. + /// + public ulong ImageBasePE32Plus; + + /// + /// The alignment (in bytes) of sections when they are loaded into memory. + /// It must be greater than or equal to FileAlignment. + /// The default is the page size for the architecture. + /// public uint SectionAlignment; + + /// + /// The alignment factor (in bytes) that is used to align the raw data of sections in the image file. + /// The value should be a power of 2 between 512 and 64 K, inclusive. + /// The default is 512. + /// If the SectionAlignment is less than the architecture's page size, then FileAlignment must match SectionAlignment. + /// public uint FileAlignment; + + /// + /// The major version number of the required operating system. + /// public ushort MajorOperatingSystemVersion; + + /// + /// The minor version number of the required operating system. + /// public ushort MinorOperatingSystemVersion; + + /// + /// The major version number of the image. + /// public ushort MajorImageVersion; + + /// + /// The minor version number of the image. + /// public ushort MinorImageVersion; + + /// + /// The major version number of the subsystem. + /// public ushort MajorSubsystemVersion; + + /// + /// The minor version number of the subsystem. + /// public ushort MinorSubsystemVersion; + + /// + /// Reserved, must be zero. + /// public uint Reserved1; + + /// + /// The size (in bytes) of the image, including all headers, as the image is loaded in memory. + /// It must be a multiple of SectionAlignment. + /// public uint SizeOfImage; + + /// + /// The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of FileAlignment. + /// public uint SizeOfHeaders; + + /// + /// The image file checksum. + /// The algorithm for computing the checksum is incorporated into IMAGHELP.DLL. + /// The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process. + /// public uint CheckSum; - public ushort Subsystem; - public ushort DllCharacteristics; - public uint SizeOfStackReserve; - public uint SizeOfStackCommit; - public uint SizeOfHeapReserve; - public uint SizeOfHeapCommit; + + /// + /// The subsystem that is required to run this image. + /// + public WindowsSubsystem Subsystem; + + /// + /// DLL Characteristics + /// + public DllCharacteristics DllCharacteristics; + + /// + /// The size of the stack to reserve. + /// Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public uint SizeOfStackReservePE32; + + /// + /// The size of the stack to reserve. + /// Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public ulong SizeOfStackReservePE32Plus; + + /// + /// The size of the stack to commit. + /// + public uint SizeOfStackCommitPE32; + + /// + /// The size of the stack to commit. + /// + public ulong SizeOfStackCommitPE32Plus; + + /// + /// The size of the local heap space to reserve. + /// Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public uint SizeOfHeapReservePE32; + + /// + /// The size of the local heap space to reserve. + /// Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached. + /// + public ulong SizeOfHeapReservePE32Plus; + + /// + /// The size of the local heap space to commit. + /// + public uint SizeOfHeapCommitPE32; + + /// + /// The size of the local heap space to commit. + /// + public ulong SizeOfHeapCommitPE32Plus; + + /// + /// Reserved, must be zero. + /// public uint LoaderFlags; + + /// + /// The number of data-directory entries in the remainder of the optional header. + /// Each describes a location and size. + /// public uint NumberOfRvaAndSizes; + + /// + /// Data-directory entries following the optional header + /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES)] - public IMAGE_DATA_DIRECTORY[] DataDirectory; + public IMAGE_DATA_DIRECTORY[] DataDirectories; + + #endregion public static IMAGE_OPTIONAL_HEADER Deserialize(Stream stream) { var ioh = new IMAGE_OPTIONAL_HEADER(); - ioh.Magic = stream.ReadUInt16(); + ioh.Magic = (OptionalHeaderType)stream.ReadUInt16(); ioh.MajorLinkerVersion = stream.ReadByteValue(); ioh.MinorLinkerVersion = stream.ReadByteValue(); ioh.SizeOfCode = stream.ReadUInt32(); @@ -70,9 +234,17 @@ namespace BurnOutSharp.ExecutableType.Microsoft ioh.SizeOfUninitializedData = stream.ReadUInt32(); ioh.AddressOfEntryPoint = stream.ReadUInt32(); ioh.BaseOfCode = stream.ReadUInt32(); - ioh.BaseOfData = stream.ReadUInt32(); - ioh.ImageBase = stream.ReadUInt32(); + // Only standard PE32 has this value + if (ioh.Magic == OptionalHeaderType.PE32) + ioh.BaseOfData = stream.ReadUInt32(); + + // PE32+ has an 8-byte value here + if (ioh.Magic == OptionalHeaderType.PE32Plus) + ioh.ImageBasePE32Plus = stream.ReadUInt64(); + else + ioh.ImageBasePE32 = stream.ReadUInt32(); + ioh.SectionAlignment = stream.ReadUInt32(); ioh.FileAlignment = stream.ReadUInt32(); ioh.MajorOperatingSystemVersion = stream.ReadUInt16(); @@ -85,18 +257,31 @@ namespace BurnOutSharp.ExecutableType.Microsoft ioh.SizeOfImage = stream.ReadUInt32(); ioh.SizeOfHeaders = stream.ReadUInt32(); ioh.CheckSum = stream.ReadUInt32(); - ioh.Subsystem = stream.ReadUInt16(); - ioh.DllCharacteristics = stream.ReadUInt16(); - ioh.SizeOfStackReserve = stream.ReadUInt32(); - ioh.SizeOfStackCommit = stream.ReadUInt32(); - ioh.SizeOfHeapReserve = stream.ReadUInt32(); - ioh.SizeOfHeapCommit = stream.ReadUInt32(); + ioh.Subsystem = (WindowsSubsystem)stream.ReadUInt16(); + ioh.DllCharacteristics = (DllCharacteristics)stream.ReadUInt16(); + + // PE32+ uses 8-byte values + if (ioh.Magic == OptionalHeaderType.PE32Plus) + { + ioh.SizeOfStackReservePE32Plus = stream.ReadUInt64(); + ioh.SizeOfStackCommitPE32Plus = stream.ReadUInt64(); + ioh.SizeOfHeapReservePE32Plus = stream.ReadUInt64(); + ioh.SizeOfHeapCommitPE32Plus = stream.ReadUInt64(); + } + else + { + ioh.SizeOfStackReservePE32 = stream.ReadUInt32(); + ioh.SizeOfStackCommitPE32 = stream.ReadUInt32(); + ioh.SizeOfHeapReservePE32 = stream.ReadUInt32(); + ioh.SizeOfHeapCommitPE32 = stream.ReadUInt32(); + } + ioh.LoaderFlags = stream.ReadUInt32(); ioh.NumberOfRvaAndSizes = stream.ReadUInt32(); - ioh.DataDirectory = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + ioh.DataDirectories = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { - ioh.DataDirectory[i] = IMAGE_DATA_DIRECTORY.Deserialize(stream); + ioh.DataDirectories[i] = IMAGE_DATA_DIRECTORY.Deserialize(stream); } return ioh; @@ -106,7 +291,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft { var ioh = new IMAGE_OPTIONAL_HEADER(); - ioh.Magic = BitConverter.ToUInt16(content, offset); offset += 2; + ioh.Magic = (OptionalHeaderType)BitConverter.ToUInt16(content, offset); offset += 2; ioh.MajorLinkerVersion = content[offset]; offset++; ioh.MinorLinkerVersion = content[offset]; offset++; ioh.SizeOfCode = BitConverter.ToUInt32(content, offset); offset += 4; @@ -114,9 +299,21 @@ namespace BurnOutSharp.ExecutableType.Microsoft ioh.SizeOfUninitializedData = BitConverter.ToUInt32(content, offset); offset += 4; ioh.AddressOfEntryPoint = BitConverter.ToUInt32(content, offset); offset += 4; ioh.BaseOfCode = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.BaseOfData = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.ImageBase = BitConverter.ToUInt32(content, offset); offset += 4; + // Only standard PE32 has this value + if (ioh.Magic == OptionalHeaderType.PE32) + ioh.BaseOfData = BitConverter.ToUInt32(content, offset); offset += 4; + + // PE32+ has an 8-bit value here + if (ioh.Magic == OptionalHeaderType.PE32Plus) + { + ioh.ImageBasePE32Plus = BitConverter.ToUInt64(content, offset); offset += 8; + } + else + { + ioh.ImageBasePE32 = BitConverter.ToUInt32(content, offset); offset += 4; + } + ioh.SectionAlignment = BitConverter.ToUInt32(content, offset); offset += 4; ioh.FileAlignment = BitConverter.ToUInt32(content, offset); offset += 4; ioh.MajorOperatingSystemVersion = BitConverter.ToUInt16(content, offset); offset += 2; @@ -129,18 +326,31 @@ namespace BurnOutSharp.ExecutableType.Microsoft ioh.SizeOfImage = BitConverter.ToUInt32(content, offset); offset += 4; ioh.SizeOfHeaders = BitConverter.ToUInt32(content, offset); offset += 4; ioh.CheckSum = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.Subsystem = BitConverter.ToUInt16(content, offset); offset += 2; - ioh.DllCharacteristics = BitConverter.ToUInt16(content, offset); offset += 2; - ioh.SizeOfStackReserve = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.SizeOfStackCommit = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.SizeOfHeapReserve = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.SizeOfHeapCommit = BitConverter.ToUInt32(content, offset); offset += 4; + ioh.Subsystem = (WindowsSubsystem)BitConverter.ToUInt16(content, offset); offset += 2; + ioh.DllCharacteristics = (DllCharacteristics)BitConverter.ToUInt16(content, offset); offset += 2; + + // PE32+ uses 8-byte values + if (ioh.Magic == OptionalHeaderType.PE32Plus) + { + ioh.SizeOfStackReservePE32Plus = BitConverter.ToUInt64(content, offset); offset += 8; + ioh.SizeOfStackCommitPE32Plus = BitConverter.ToUInt64(content, offset); offset += 8; + ioh.SizeOfHeapReservePE32Plus = BitConverter.ToUInt64(content, offset); offset += 8; + ioh.SizeOfHeapCommitPE32Plus = BitConverter.ToUInt64(content, offset); offset += 8; + } + else + { + ioh.SizeOfStackReservePE32 = BitConverter.ToUInt32(content, offset); offset += 4; + ioh.SizeOfStackCommitPE32 = BitConverter.ToUInt32(content, offset); offset += 4; + ioh.SizeOfHeapReservePE32 = BitConverter.ToUInt32(content, offset); offset += 4; + ioh.SizeOfHeapCommitPE32 = BitConverter.ToUInt32(content, offset); offset += 4; + } + ioh.LoaderFlags = BitConverter.ToUInt32(content, offset); offset += 4; ioh.NumberOfRvaAndSizes = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.DataDirectory = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + ioh.DataDirectories = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { - ioh.DataDirectory[i] = IMAGE_DATA_DIRECTORY.Deserialize(content, offset); offset += 8; + ioh.DataDirectories[i] = IMAGE_DATA_DIRECTORY.Deserialize(content, offset); offset += 8; } return ioh; diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs index bbdc9538..24b38106 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs @@ -1,15 +1,4 @@ -/* - * NEWEXE.H (C) Copyright Microsoft Corp 1984-1987 - * - * Data structure definitions for the OS/2 & Windows - * executable file format. - * - * Modified by IVS on 24-Jan-1991 for Resource DeCompiler - * (C) Copyright IVS 1991 - * - * http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h - */ - +using System; using System.IO; using System.Runtime.InteropServices; using BurnOutSharp.Tools; @@ -21,11 +10,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft { [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_SIZEOF_SHORT_NAME)] public byte[] Name; - - // Misc - public uint PhysicalAddress; public uint VirtualSize; - public uint VirtualAddress; public uint SizeOfRawData; public uint PointerToRawData; @@ -40,11 +25,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft var ish = new IMAGE_SECTION_HEADER(); ish.Name = stream.ReadBytes(Constants.IMAGE_SIZEOF_SHORT_NAME); - - // Misc - ish.PhysicalAddress = stream.ReadUInt32(); - ish.VirtualSize = ish.PhysicalAddress; - + ish.VirtualSize = stream.ReadUInt32(); ish.VirtualAddress = stream.ReadUInt32(); ish.SizeOfRawData = stream.ReadUInt32(); ish.PointerToRawData = stream.ReadUInt32(); @@ -56,5 +37,24 @@ namespace BurnOutSharp.ExecutableType.Microsoft return ish; } + + public static IMAGE_SECTION_HEADER Deserialize(byte[] content, int offset) + { + var ish = new IMAGE_SECTION_HEADER(); + + ish.Name = new byte[Constants.IMAGE_SIZEOF_SHORT_NAME]; + Array.Copy(content, offset, ish.Name, 0, Constants.IMAGE_SIZEOF_SHORT_NAME); offset += Constants.IMAGE_SIZEOF_SHORT_NAME; + ish.VirtualSize = BitConverter.ToUInt32(content, offset); offset += 4; + ish.VirtualAddress = BitConverter.ToUInt32(content, offset); offset += 4; + ish.SizeOfRawData = BitConverter.ToUInt32(content, offset); offset += 4; + ish.PointerToRawData = BitConverter.ToUInt32(content, offset); offset += 4; + ish.PointerToRelocations = BitConverter.ToUInt32(content, offset); offset += 4; + ish.PointerToLinenumbers = BitConverter.ToUInt32(content, offset); offset += 4; + ish.NumberOfRelocations = BitConverter.ToUInt16(content, offset); offset += 2; + ish.NumberOfLinenumbers = BitConverter.ToUInt16(content, offset); offset += 2; + ish.Characteristics = (SectionCharacteristics)BitConverter.ToUInt32(content, offset); offset += 4; + + return ish; + } } } \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/PEExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/PEExecutable.cs new file mode 100644 index 00000000..16e0380e --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/PEExecutable.cs @@ -0,0 +1,162 @@ +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Sections; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft +{ + /// + /// The PE file header consists of a Microsoft MS-DOS stub, the PE signature, the COFF file header, and an optional header. + /// A COFF object file header consists of a COFF file header and an optional header. + /// In both cases, the file headers are followed immediately by section headers. + /// + internal class PEExecutable + { + #region Headers + + /// + /// The MS-DOS stub is a valid application that runs under MS-DOS. + /// It is placed at the front of the EXE image. + /// The linker places a default stub here, which prints out the message "This program cannot be run in DOS mode" when the image is run in MS-DOS. + /// The user can specify a different stub by using the /STUB linker option. + /// At location 0x3c, the stub has the file offset to the PE signature. + /// This information enables Windows to properly execute the image file, even though it has an MS-DOS stub. + /// This file offset is placed at location 0x3c during linking. + // + public IMAGE_DOS_HEADER MSDOSStub; + + /// + /// At the beginning of an object file, or immediately after the signature of an image file, is a standard COFF file header in the following format. + /// Note that the Windows loader limits the number of sections to 96. + /// + public IMAGE_FILE_HEADER COFFFileHeader; + + /// + /// Every image file has an optional header that provides information to the loader. + /// This header is optional in the sense that some files (specifically, object files) do not have it. + /// For image files, this header is required. + /// An object file can have an optional header, but generally this header has no function in an object file except to increase its size. + /// + public IMAGE_OPTIONAL_HEADER OptionalHeader; + + /// + /// Each row of the section table is, in effect, a section header. + /// This table immediately follows the optional header, if any. + /// This positioning is required because the file header does not contain a direct pointer to the section table. + /// Instead, the location of the section table is determined by calculating the location of the first byte after the headers. + /// Make sure to use the size of the optional header as specified in the file header. + /// + public IMAGE_SECTION_HEADER[] SectionHeaders; + + #endregion + + #region Tables + + /// + /// The export data section, named .edata, contains information about symbols that other images can access through dynamic linking. + /// Exported symbols are generally found in DLLs, but DLLs can also import symbols. + // + public ExportDataSection ExportTable; + + /// + /// All image files that import symbols, including virtually all executable (EXE) files, have an .idata section. + // + public ImportDataSection ImportTable; + + #endregion + + // TODO: Add more and more parts of a standard PE executable, not just the header + // TODO: Add data directory table information here instead of in IMAGE_OPTIONAL_HEADER + + public static PEExecutable Deserialize(Stream stream) + { + PEExecutable pex = new PEExecutable(); + + try + { + pex.MSDOSStub = IMAGE_DOS_HEADER.Deserialize(stream); stream.Seek(pex.MSDOSStub.NewExeHeaderAddr, SeekOrigin.Begin); + pex.COFFFileHeader = IMAGE_FILE_HEADER.Deserialize(stream); + if (pex.COFFFileHeader.SizeOfOptionalHeader > 0) + pex.OptionalHeader = IMAGE_OPTIONAL_HEADER.Deserialize(stream); + + pex.SectionHeaders = new IMAGE_SECTION_HEADER[pex.COFFFileHeader.NumberOfSections]; + for (int i = 0; i < pex.COFFFileHeader.NumberOfSections; i++) + { + pex.SectionHeaders[i] = IMAGE_SECTION_HEADER.Deserialize(stream); + } + + // TODO: Uncomment these when RVA conversion works + // // Export Table + // var table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; + // if (table.VirtualSize > 0) + // { + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // stream.Seek(tableAddress, SeekOrigin.Begin); + // pex.ExportTable = ExportDataSection.Deserialize(stream); + // } + + // // Import Table + // table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_IMPORT]; + // if (table.VirtualSize > 0) + // { + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // stream.Seek(tableAddress, SeekOrigin.Begin); + // pex.ImportTable = ImportDataSection.Deserialize(stream, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0); // TODO: Figure out where this count comes from + // } + } + catch + { + return null; + } + + return pex; + } + + public static PEExecutable Deserialize(byte[] content, int offset) + { + PEExecutable pex = new PEExecutable(); + + try + { + unsafe + { + pex.MSDOSStub = IMAGE_DOS_HEADER.Deserialize(content, offset); offset = pex.MSDOSStub.NewExeHeaderAddr; + pex.COFFFileHeader = IMAGE_FILE_HEADER.Deserialize(content, offset); offset += Marshal.SizeOf(pex.COFFFileHeader); + if (pex.COFFFileHeader.SizeOfOptionalHeader > 0) + { + pex.OptionalHeader = IMAGE_OPTIONAL_HEADER.Deserialize(content, offset); offset += pex.COFFFileHeader.SizeOfOptionalHeader; + } + + pex.SectionHeaders = new IMAGE_SECTION_HEADER[pex.COFFFileHeader.NumberOfSections]; + for (int i = 0; i < pex.COFFFileHeader.NumberOfSections; i++) + { + pex.SectionHeaders[i] = IMAGE_SECTION_HEADER.Deserialize(content, offset); offset += 40; + } + + // TODO: Uncomment these when RVA conversion works + // // Export Table + // var table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; + // if (table.VirtualSize > 0) + // { + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // pex.ExportTable = ExportDataSection.Deserialize(content, tableAddress); + // } + + // // Import Table + // table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_IMPORT]; + // if (table.VirtualSize > 0) + // { + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // pex.ImportTable = ImportDataSection.Deserialize(content, tableAddress, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0); offset += Marshal.SizeOf(pex.ImportTable); // TODO: Figure out where this count comes from + // } + } + } + catch + { + return null; + } + + return pex; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExceptionHandlingSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExceptionHandlingSection.cs new file mode 100644 index 00000000..8e178bda --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExceptionHandlingSection.cs @@ -0,0 +1,19 @@ +using BurnOutSharp.ExecutableType.Microsoft.Tables; + +namespace BurnOutSharp.ExecutableType.Microsoft.Sections +{ + /// + /// The .pdata section contains an array of function table entries that are used for exception handling. + /// It is pointed to by the exception table entry in the image data directory. + /// The entries must be sorted according to the function addresses (the first field in each structure) before being emitted into the final image. + /// The target platform determines which of the three function table entry format variations described below is used. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-edata-section-image-only + internal class ExceptionHandlingSection + { + /// + /// Array of function table entries that are used for exception handling + /// + public FunctionTable FunctionTable; + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs new file mode 100644 index 00000000..23fd4c5c --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Tables; + +namespace BurnOutSharp.ExecutableType.Microsoft.Sections +{ + /// + /// The export data section, named .edata, contains information about symbols that other images can access through dynamic linking. + /// Exported symbols are generally found in DLLs, but DLLs can also import symbols. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-edata-section-image-only + internal class ExportDataSection + { + /// + /// A table with just one row (unlike the debug directory). + /// This table indicates the locations and sizes of the other export tables. + /// + public ExportDirectoryTable ExportDirectoryTable; + + /// + /// An array of RVAs of exported symbols. + /// These are the actual addresses of the exported functions and data within the executable code and data sections. + /// Other image files can import a symbol by using an index to this table (an ordinal) or, optionally, by using the public name that corresponds to the ordinal if a public name is defined. + /// + public ExportAddressTable ExportAddressTable; + + /// + /// An array of pointers to the public export names, sorted in ascending order. + /// + public ExportNamePointerTable NamePointerTable; + + /// + /// An array of the ordinals that correspond to members of the name pointer table. + /// The correspondence is by position; therefore, the name pointer table and the ordinal table must have the same number of members. + /// Each ordinal is an index into the export address table. + /// + public ExportOrdinalTable OrdinalTable; + + /// + /// A series of null-terminated ASCII strings. + /// Members of the name pointer table point into this area. + /// These names are the public names through which the symbols are imported and exported; they are not necessarily the same as the private names that are used within the image file. + /// + public ExportNameTable ExportNameTable; + + public static ExportDataSection Deserialize(Stream stream) + { + var eds = new ExportDataSection(); + + eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(stream); + eds.ExportAddressTable = ExportAddressTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from + eds.NamePointerTable = ExportNamePointerTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from + eds.OrdinalTable = ExportOrdinalTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from + //eds.ExportNameTable = ExportNameTable.Deserialize(stream); // TODO: set this table based on the NamePointerTable value + + return eds; + } + + public static ExportDataSection Deserialize(byte[] content, int offset) + { + var eds = new ExportDataSection(); + + unsafe + { + eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(content, offset); offset += Marshal.SizeOf(eds.ExportDirectoryTable); + eds.ExportAddressTable = ExportAddressTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: Figure out where this count comes from + eds.NamePointerTable = ExportNamePointerTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.NamePointerTable); // TODO: Figure out where this count comes from + eds.OrdinalTable = ExportOrdinalTable.Deserialize(content, offset, count: 0); offset += Marshal.SizeOf(eds.OrdinalTable); // TODO: Figure out where this count comes from + //eds.ExportNameTable = ExportNameTable.Deserialize(stream); offset += Marshal.SizeOf(eds.ExportAddressTable); // TODO: set this table based on the NamePointerTable value + } + + return eds; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs new file mode 100644 index 00000000..09fada0b --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using BurnOutSharp.ExecutableType.Microsoft.Tables; + +namespace BurnOutSharp.ExecutableType.Microsoft.Sections +{ + /// + /// All image files that import symbols, including virtually all executable (EXE) files, have an .idata section. + /// A typical file layout for the import information follows: + /// Directory Table + /// Null Directory Entry + /// DLL1 Import Lookup Table + /// Null + /// DLL2 Import Lookup Table + /// Null + /// DLL3 Import Lookup Table + /// Null + /// Hint-Name Table + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-idata-section + internal class ImportDataSection + { + /// + /// Import directory table + /// + public ImportDirectoryTable ImportDirectoryTable; + + /// + /// Import lookup tables + /// + public ImportLookupTable[] ImportLookupTables; + + /// + /// Hint/Name table + /// + public HintNameTable HintNameTable; + + public static ImportDataSection Deserialize(Stream stream, bool pe32plus, int hintCount) + { + var ids = new ImportDataSection(); + + ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(stream); + + List tempLookupTables = new List(); + while (true) + { + var tempLookupTable = ImportLookupTable.Deserialize(stream, pe32plus); + if (tempLookupTable.EntriesPE32 == null && tempLookupTable.EntriesPE32Plus == null) + break; + + tempLookupTables.Add(tempLookupTable); + } + + ids.HintNameTable = HintNameTable.Deserialize(stream, hintCount); + + return ids; + } + + public static ImportDataSection Deserialize(byte[] content, int offset, bool pe32plus, int hintCount) + { + var ids = new ImportDataSection(); + + ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(content, offset); offset += 20 * ids.ImportDirectoryTable.Entries.Length; + + List tempLookupTables = new List(); + while (true) + { + var tempLookupTable = ImportLookupTable.Deserialize(content, offset, pe32plus); + if (tempLookupTable.EntriesPE32 != null) + offset += 4 * tempLookupTable.EntriesPE32.Length; + else if (tempLookupTable.EntriesPE32Plus != null) + offset += 8 * tempLookupTable.EntriesPE32Plus.Length; + else + break; + + tempLookupTables.Add(tempLookupTable); + } + + // TODO: Update the offset, if possible + ids.HintNameTable = HintNameTable.Deserialize(content, offset, hintCount); + + return ids; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs new file mode 100644 index 00000000..4e7b1432 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The export address table contains the address of exported entry points and exported data and absolutes. + /// An ordinal number is used as an index into the export address table. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table + internal class ExportAddressTable + { + /// Number of entries is defined externally + public ExportAddressTableEntry[] Entries; + + public static ExportAddressTable Deserialize(Stream stream, int count) + { + var eat = new ExportAddressTable(); + + eat.Entries = new ExportAddressTableEntry[count]; + for (int i = 0; i < count; i++) + { + eat.Entries[i] = ExportAddressTableEntry.Deserialize(stream); + } + + return eat; + } + + public static ExportAddressTable Deserialize(byte[] content, int offset, int count) + { + var eat = new ExportAddressTable(); + + eat.Entries = new ExportAddressTableEntry[count]; + for (int i = 0; i < count; i++) + { + eat.Entries[i] = ExportAddressTableEntry.Deserialize(content, offset); offset += 4; + } + + return eat; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs new file mode 100644 index 00000000..3c910fbc --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each entry in the export address table is a field that uses one of two formats in the following table. + /// 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, which is an actual address in code or data. + /// Otherwise, the field is a forwarder RVA, which names a symbol in another DLL. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table + internal class ExportAddressTableEntry + { + /// + /// The address of the exported symbol when loaded into memory, relative to the image base. + /// For example, the address of an exported function. + /// + public uint ExportRVA; + + /// + /// The pointer to a null-terminated ASCII string in the export section. + /// This string must be within the range that is given by the export table data directory entry. + /// This string gives the DLL name and the name of the export (for example, "MYDLL.expfunc") or the DLL name and the ordinal number of the export (for example, "MYDLL.#27"). + /// + public uint ForwarderRVA; + + public static ExportAddressTableEntry Deserialize(Stream stream) + { + var eate = new ExportAddressTableEntry(); + + eate.ExportRVA = stream.ReadUInt32(); + eate.ForwarderRVA = eate.ExportRVA; + + return eate; + } + + public static ExportAddressTableEntry Deserialize(byte[] content, int offset) + { + var eate = new ExportAddressTableEntry(); + + eate.ExportRVA = BitConverter.ToUInt32(content, offset); offset += 4; + eate.ForwarderRVA = eate.ExportRVA; + + return eate; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs new file mode 100644 index 00000000..749dfd41 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs @@ -0,0 +1,112 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The export symbol information begins with the export directory table, which describes the remainder of the export symbol information. + /// The export directory table contains address information that is used to resolve imports to the entry points within this image. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table + internal class ExportDirectoryTable + { + /// + /// Reserved, must be 0. + /// + public uint ExportFlags; + + /// + /// The time and date that the export data was created. + /// + public uint TimeDateStamp; + + /// + /// The major version number. The major and minor version numbers can be set by the user. + /// + public ushort MajorVersion; + + /// + /// The minor version number. + /// + public ushort MinorVersion; + + /// + /// The address of the ASCII string that contains the name of the DLL. + /// This address is relative to the image base. + /// + public uint NameRVA; + + /// + /// The starting ordinal number for exports in this image. + /// This field specifies the starting ordinal number for the export address table. + /// It is usually set to 1. + /// + public uint OrdinalBase; + + /// + /// The number of entries in the export address table. + /// + public uint AddressTableEntries; + + /// + /// The number of entries in the name pointer table. + /// This is also the number of entries in the ordinal table. + /// + public uint NumberOfNamePointers; + + /// + /// The address of the export address table, relative to the image base. + /// + public uint ExportAddressTableRVA; + + /// + /// The address of the export name pointer table, relative to the image base. + /// The table size is given by the Number of Name Pointers field. + /// + public uint NamePointerRVA; + + /// + /// The address of the ordinal table, relative to the image base. + /// + public uint OrdinalTableRVA; + + public static ExportDirectoryTable Deserialize(Stream stream) + { + var edt = new ExportDirectoryTable(); + + edt.ExportFlags = stream.ReadUInt32(); + edt.TimeDateStamp = stream.ReadUInt32(); + edt.MajorVersion = stream.ReadUInt16(); + edt.MinorVersion = stream.ReadUInt16(); + edt.NameRVA = stream.ReadUInt32(); + edt.OrdinalBase = stream.ReadUInt32(); + edt.AddressTableEntries = stream.ReadUInt32(); + edt.NumberOfNamePointers = stream.ReadUInt32(); + edt.ExportAddressTableRVA = stream.ReadUInt32(); + edt.NamePointerRVA = stream.ReadUInt32(); + edt.OrdinalTableRVA = stream.ReadUInt32(); + + return edt; + } + + public static ExportDirectoryTable Deserialize(byte[] content, int offset) + { + var edt = new ExportDirectoryTable(); + + edt.ExportFlags = BitConverter.ToUInt32(content, offset); offset += 4; + edt.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4; + edt.MajorVersion = BitConverter.ToUInt16(content, offset); offset += 2; + edt.MinorVersion = BitConverter.ToUInt16(content, offset); offset += 2; + edt.NameRVA = BitConverter.ToUInt32(content, offset); offset += 4; + edt.OrdinalBase = BitConverter.ToUInt32(content, offset); offset += 4; + edt.AddressTableEntries = BitConverter.ToUInt32(content, offset); offset += 4; + edt.NumberOfNamePointers = BitConverter.ToUInt32(content, offset); offset += 4; + edt.ExportAddressTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + edt.NamePointerRVA = BitConverter.ToUInt32(content, offset); offset += 4; + edt.OrdinalTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + + return edt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs new file mode 100644 index 00000000..72956fca --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The export name pointer table is an array of addresses (RVAs) into the export name table. + /// The pointers are 32 bits each and are relative to the image base. + /// The pointers are ordered lexically to allow binary searches. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-name-pointer-table + internal class ExportNamePointerTable + { + /// Number of entries is defined externally + public uint[] Entries; + + public static ExportNamePointerTable Deserialize(Stream stream, int count) + { + var enpt = new ExportNamePointerTable(); + + enpt.Entries = new uint[count]; + for (int i = 0; i < count; i++) + { + enpt.Entries[i] = stream.ReadUInt32(); + } + + return enpt; + } + + public static ExportNamePointerTable Deserialize(byte[] content, int offset, int count) + { + var enpt = new ExportNamePointerTable(); + + enpt.Entries = new uint[count]; + for (int i = 0; i < count; i++) + { + enpt.Entries[i] = BitConverter.ToUInt32(content, offset); offset += 4; + } + + return enpt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNameTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNameTable.cs new file mode 100644 index 00000000..5f1c46f8 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNameTable.cs @@ -0,0 +1,14 @@ +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The export name table contains the actual string data that was pointed to by the export name pointer table. + /// The strings in this table are public names that other images can use to import the symbols. + /// These public export names are not necessarily the same as the private symbol names that the symbols have in their own image file and source code, although they can be. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table + internal class ExportNameTable + { + /// Number of entries is defined externally + public string[] Entries; + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs new file mode 100644 index 00000000..d33c5576 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The export ordinal table is an array of 16-bit unbiased indexes into the export address table. + /// Ordinals are biased by the Ordinal Base field of the export directory table. + /// In other words, the ordinal base must be subtracted from the ordinals to obtain true indexes into the export address table. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table + internal class ExportOrdinalTable + { + /// Number of entries is defined externally + public ushort[] Entries; + + public static ExportOrdinalTable Deserialize(Stream stream, int count) + { + var edt = new ExportOrdinalTable(); + + edt.Entries = new ushort[count]; + for (int i = 0; i < count; i++) + { + edt.Entries[i] = stream.ReadUInt16(); + } + + return edt; + } + + public static ExportOrdinalTable Deserialize(byte[] content, int offset, int count) + { + var edt = new ExportOrdinalTable(); + + edt.Entries = new ushort[count]; + for (int i = 0; i < count; i++) + { + edt.Entries[i] = BitConverter.ToUInt16(content, offset); offset += 2; + } + + return edt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs new file mode 100644 index 00000000..afa83c8f --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs @@ -0,0 +1,15 @@ +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The .pdata section contains an array of function table entries that are used for exception handling. + /// It is pointed to by the exception table entry in the image data directory. + /// The entries must be sorted according to the function addresses (the first field in each structure) before being emitted into the final image. + /// The target platform determines which of the three function table entry format variations described below is used. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-pdata-section + internal class FunctionTable + { + /// Number of entries is defined externally + public FunctionTableEntry[] Entries; + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs new file mode 100644 index 00000000..10732ff3 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs @@ -0,0 +1,78 @@ +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each entry in the export address table is a field that uses one of two formats in the following table. + /// 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, which is an actual address in code or data. + /// Otherwise, the field is a forwarder RVA, which names a symbol in another DLL. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-pdata-section + internal class FunctionTableEntry + { + #region 32-bit MIPS + + /// + /// The VA of the corresponding function. + /// + public uint MIPSBeginAddress; + + /// + /// The VA of the end of the function. + /// + public uint MIPSEndAddress; + + /// + /// The pointer to the exception handler to be executed. + /// + public uint MIPSExceptionHandler; + + /// + /// The pointer to additional information to be passed to the handler. + /// + public uint MIPSHandlerData; + + /// + /// The VA of the end of the function's prolog. + /// + public uint MIPSPrologEndAddress; + + #endregion + + #region ARM, PowerPC, SH3 and SH4 Windows CE + + /// + /// The VA of the corresponding function. + /// + public uint ARMBeginAddress; + + /// + /// The VA of the end of the function. + /// + /// 8 bits Prolog Length The number of instructions in the function's prolog. + /// 22 bits Function Length The number of instructions in the function. + /// 1 bit 32-bit Flag If set, the function consists of 32-bit instructions. If clear, the function consists of 16-bit instructions. + /// 1 bit Exception Flag If set, an exception handler exists for the function. Otherwise, no exception handler exists. + /// + public uint ARMLengthsAndFlags; + + #endregion + + #region x64 and Itanium + + /// + /// The RVA of the corresponding function. + /// + public uint X64BeginAddress; + + /// + /// The RVA of the end of the function. + /// + public uint X64EndAddress; + + /// + /// The RVA of the unwind information. + /// + public uint X64UnwindInformation; + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs new file mode 100644 index 00000000..70854c4d --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// One hint/name table suffices for the entire import section. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#hintname-table + internal class HintNameTable + { + /// Number of entries is defined externally + public HintNameTableEntry[] Entries; + + public static HintNameTable Deserialize(Stream stream, int count) + { + var hnt = new HintNameTable(); + + hnt.Entries = new HintNameTableEntry[count]; + for (int i = 0; i < count; i++) + { + hnt.Entries[i] = HintNameTableEntry.Deserialize(stream); + } + + return hnt; + } + + public static HintNameTable Deserialize(byte[] content, int offset, int count) + { + var hnt = new HintNameTable(); + + hnt.Entries = new HintNameTableEntry[count]; + for (int i = 0; i < count; i++) + { + hnt.Entries[i] = HintNameTableEntry.Deserialize(content, offset); + offset += 2 + hnt.Entries[i].Name.Length + hnt.Entries[i].Pad; + } + + return hnt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs new file mode 100644 index 00000000..838519a1 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using System.Linq; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each entry in the hint/name table has the following format + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#hintname-table + internal class HintNameTableEntry + { + /// + /// An index into the export name pointer table. + /// A match is attempted first with this value. + /// If it fails, a binary search is performed on the DLL's export name pointer table. + /// + public ushort Hint; + + /// + /// An ASCII string that contains the name to import. + /// This is the string that must be matched to the public name in the DLL. + /// This string is case sensitive and terminated by a null byte. + /// + public string Name; + + /// + /// A trailing zero-pad byte that appears after the trailing null byte, if necessary, to align the next entry on an even boundary. + /// + public byte Pad; + + public static HintNameTableEntry Deserialize(Stream stream) + { + var hnte = new HintNameTableEntry(); + + hnte.Hint = stream.ReadUInt16(); + hnte.Name = string.Empty; + while (true) + { + char c = stream.ReadChar(); + if (c == (char)0x00) + break; + + hnte.Name += c; + } + + // If the name length is not even, read and pad + if (hnte.Name.Length % 2 != 0) + { + stream.ReadByte(); + hnte.Pad = 1; + } + else + { + hnte.Pad = 0; + } + + return hnte; + } + + public static HintNameTableEntry Deserialize(byte[] content, int offset) + { + var hnte = new HintNameTableEntry(); + + hnte.Hint = BitConverter.ToUInt16(content, offset); offset += 2; + hnte.Name = string.Empty; + while (true) + { + char c = (char)content[offset]; offset += 1; + if (c == (char)0x00) + break; + + hnte.Name += c; + } + + // If the name length is not even, read and pad + if (hnte.Name.Length % 2 != 0) + { + offset += 1; + hnte.Pad = 1; + } + else + { + hnte.Pad = 0; + } + + return hnte; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs new file mode 100644 index 00000000..b849c47e --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The structure and content of the import address table are identical to those of the import lookup table, until the file is bound. + /// During binding, the entries in the import address table are overwritten with the 32-bit (for PE32) or 64-bit (for PE32+) addresses of the symbols that are being imported. + /// These addresses are the actual memory addresses of the symbols, although technically they are still called "virtual addresses." + /// The loader typically processes the binding. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-address-table + internal class ImportAddressTable + { + /// Number of entries is known after parsing + public ImportAddressTableEntry[] Entries; + + public static ImportAddressTable Deserialize(Stream stream) + { + var iat = new ImportAddressTable(); + + List tempEntries = new List(); + while (true) + { + var entry = ImportAddressTableEntry.Deserialize(stream); + tempEntries.Add(entry); + if (entry.IsNull()) + break; + } + + iat.Entries = tempEntries.ToArray(); + return iat; + } + + public static ImportAddressTable Deserialize(byte[] content, int offset) + { + var iat = new ImportAddressTable(); + + List tempEntries = new List(); + while (true) + { + var entry = ImportAddressTableEntry.Deserialize(content, offset); offset += 20; + tempEntries.Add(entry); + if (entry.IsNull()) + break; + } + + iat.Entries = tempEntries.ToArray(); + return iat; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs new file mode 100644 index 00000000..b73f21c6 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each import address entry has the following format + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-address-table + internal class ImportAddressTableEntry + { + /// + /// The RVA of the import lookup table. + /// This table contains a name or ordinal for each import. + /// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.) + /// + public uint ImportLookupTableRVA; + + /// + /// The stamp that is set to zero until the image is bound. + /// After the image is bound, this field is set to the time/data stamp of the DLL. + /// + public uint TimeDateStamp; + + /// + /// The index of the first forwarder reference. + /// + public uint ForwarderChain; + + /// + /// The address of an ASCII string that contains the name of the DLL. + /// This address is relative to the image base. + /// + public uint NameRVA; + + /// + /// The RVA of the import address table. + /// The contents of this table are identical to the contents of the import lookup table until the image is bound. + /// + public uint ImportAddressTableRVA; + + /// + /// Determine if the entry is null or not + /// This indicates the last entry in a table + /// + public bool IsNull() + { + return ImportLookupTableRVA == 0 + && TimeDateStamp == 0 + && ForwarderChain == 0 + && NameRVA == 0 + && ImportAddressTableRVA == 0; + } + + public static ImportAddressTableEntry Deserialize(Stream stream) + { + var iate = new ImportAddressTableEntry(); + + iate.ImportLookupTableRVA = stream.ReadUInt32(); + iate.TimeDateStamp = stream.ReadUInt32(); + iate.ForwarderChain = stream.ReadUInt32(); + iate.NameRVA = stream.ReadUInt32(); + iate.ImportAddressTableRVA = stream.ReadUInt32(); + + return iate; + } + + public static ImportAddressTableEntry Deserialize(byte[] content, int offset) + { + var iate = new ImportAddressTableEntry(); + + iate.ImportLookupTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + iate.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4; + iate.ForwarderChain = BitConverter.ToUInt32(content, offset); offset += 4; + iate.NameRVA = BitConverter.ToUInt32(content, offset); offset += 4; + iate.ImportAddressTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + + return iate; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs new file mode 100644 index 00000000..a7054246 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// The import information begins with the import directory table, which describes the remainder of the import information. + /// The import directory table contains address information that is used to resolve fixup references to the entry points within a DLL image. + /// The import directory table consists of an array of import directory entries, one entry for each DLL to which the image refers. + /// The last directory entry is empty (filled with null values), which indicates the end of the directory table. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table + internal class ImportDirectoryTable + { + /// Number of entries is known after parsing + public ImportDirectoryTableEntry[] Entries; + + public static ImportDirectoryTable Deserialize(Stream stream) + { + var idt = new ImportDirectoryTable(); + + List tempEntries = new List(); + while (true) + { + var entry = ImportDirectoryTableEntry.Deserialize(stream); + tempEntries.Add(entry); + if (entry.IsNull()) + break; + } + + idt.Entries = tempEntries.ToArray(); + return idt; + } + + public static ImportDirectoryTable Deserialize(byte[] content, int offset) + { + var idt = new ImportDirectoryTable(); + + List tempEntries = new List(); + while (true) + { + var entry = ImportDirectoryTableEntry.Deserialize(content, offset); offset += 20; + tempEntries.Add(entry); + if (entry.IsNull()) + break; + } + + idt.Entries = tempEntries.ToArray(); + return idt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs new file mode 100644 index 00000000..fb5a9221 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each import directory entry has the following format + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table + internal class ImportDirectoryTableEntry + { + /// + /// The RVA of the import lookup table. + /// This table contains a name or ordinal for each import. + /// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.) + /// + public uint ImportLookupTableRVA; + + /// + /// The stamp that is set to zero until the image is bound. + /// After the image is bound, this field is set to the time/data stamp of the DLL. + /// + public uint TimeDateStamp; + + /// + /// The index of the first forwarder reference. + /// + public uint ForwarderChain; + + /// + /// The address of an ASCII string that contains the name of the DLL. + /// This address is relative to the image base. + /// + public uint NameRVA; + + /// + /// The RVA of the import address table. + /// The contents of this table are identical to the contents of the import lookup table until the image is bound. + /// + public uint ImportAddressTableRVA; + + /// + /// Determine if the entry is null or not + /// This indicates the last entry in a table + /// + public bool IsNull() + { + return ImportLookupTableRVA == 0 + && TimeDateStamp == 0 + && ForwarderChain == 0 + && NameRVA == 0 + && ImportAddressTableRVA == 0; + } + + public static ImportDirectoryTableEntry Deserialize(Stream stream) + { + var idte = new ImportDirectoryTableEntry(); + + idte.ImportLookupTableRVA = stream.ReadUInt32(); + idte.TimeDateStamp = stream.ReadUInt32(); + idte.ForwarderChain = stream.ReadUInt32(); + idte.NameRVA = stream.ReadUInt32(); + idte.ImportAddressTableRVA = stream.ReadUInt32(); + + return idte; + } + + public static ImportDirectoryTableEntry Deserialize(byte[] content, int offset) + { + var idte = new ImportDirectoryTableEntry(); + + idte.ImportLookupTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + idte.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4; + idte.ForwarderChain = BitConverter.ToUInt32(content, offset); offset += 4; + idte.NameRVA = BitConverter.ToUInt32(content, offset); offset += 4; + idte.ImportAddressTableRVA = BitConverter.ToUInt32(content, offset); offset += 4; + + return idte; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs new file mode 100644 index 00000000..ef13cead --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// An import lookup table is an array of 32-bit numbers for PE32 or an array of 64-bit numbers for PE32+. + /// Each entry uses the bit-field format that is described in the following table. + /// In this format, bit 31 is the most significant bit for PE32 and bit 63 is the most significant bit for PE32+. + /// The collection of these entries describes all imports from a given DLL. + /// The last entry is set to zero (NULL) to indicate the end of the table. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-lookup-table + internal class ImportLookupTable + { + /// Number of entries is known after parsing + public uint[] EntriesPE32; + + /// Number of entries is known after parsing + public ulong[] EntriesPE32Plus; + + public static ImportLookupTable Deserialize(Stream stream, bool pe32plus) + { + var ilt = new ImportLookupTable(); + + // PE32+ has 8-byte values + if (pe32plus) + { + List tempEntries = new List(); + while (true) + { + ulong bitfield = stream.ReadUInt64(); + tempEntries.Add(bitfield); + if (bitfield == 0) + break; + } + + if (tempEntries.Count > 0) + ilt.EntriesPE32Plus = tempEntries.ToArray(); + } + else + { + List tempEntries = new List(); + while (true) + { + uint bitfield = stream.ReadUInt32(); + tempEntries.Add(bitfield); + if (bitfield == 0) + break; + } + + if (tempEntries.Count > 0) + ilt.EntriesPE32 = tempEntries.ToArray(); + } + + return ilt; + } + + public static ImportLookupTable Deserialize(byte[] content, int offset, bool pe32plus) + { + var ilt = new ImportLookupTable(); + + // PE32+ has 8-byte values + if (pe32plus) + { + List tempEntries = new List(); + while (true) + { + ulong bitfield = BitConverter.ToUInt64(content, offset); offset += 8; + tempEntries.Add(bitfield); + if (bitfield == 0) + break; + } + + if (tempEntries.Count > 0) + ilt.EntriesPE32Plus = tempEntries.ToArray(); + } + else + { + List tempEntries = new List(); + while (true) + { + uint bitfield = BitConverter.ToUInt32(content, offset); offset += 4; + tempEntries.Add(bitfield); + if (bitfield == 0) + break; + } + + if (tempEntries.Count > 0) + ilt.EntriesPE32 = tempEntries.ToArray(); + } + + return ilt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/Tools/EVORE.cs b/BurnOutSharp/Tools/EVORE.cs index d83d0cb8..0b757a24 100644 --- a/BurnOutSharp/Tools/EVORE.cs +++ b/BurnOutSharp/Tools/EVORE.cs @@ -23,6 +23,8 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft; +using BurnOutSharp.ExecutableType.Microsoft.Sections; +using BurnOutSharp.ExecutableType.Microsoft.Tables; namespace BurnOutSharp.Tools { @@ -44,7 +46,7 @@ namespace BurnOutSharp.Tools IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr); // Check if file is dll - return (ifh.Characteristics & 0x2000) != 0x2000; + return ifh.Characteristics.HasFlag(ImageObjectCharacteristics.IMAGE_FILE_DLL); } catch { @@ -103,18 +105,22 @@ namespace BurnOutSharp.Tools unsafe { // Read all of the executable header information - IMAGE_DOS_HEADER idh = IMAGE_DOS_HEADER.Deserialize(fileContent, 0); - IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr); - IMAGE_OPTIONAL_HEADER ioh = IMAGE_OPTIONAL_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr + Marshal.SizeOf(ifh)); - - // Find the import directory entry - IMAGE_DATA_DIRECTORY idei = ioh.DataDirectory[Constants.IMAGE_DIRECTORY_ENTRY_IMPORT]; + PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); + // Find the import directory entry + IMAGE_DATA_DIRECTORY idei = pex.OptionalHeader.DataDirectories[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_IMPORT]; + // Set the table index and size - int tableIndex = (int)idei.VirtualAddress; + int tableIndex = (int)ConvertVirtualAddress(idei.VirtualAddress, pex.SectionHeaders); int tableSize = (int)idei.Size; if (tableIndex <= 0 || tableSize <= 0) return null; + + // Load the table from index + ImportDataSection idata = ImportDataSection.Deserialize(fileContent, tableIndex, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0); + ImportDirectoryTable idt = idata.ImportDirectoryTable; + + // TODO: Use the known layout to determine the names in a more automated way instead of having to iterate // Retrieve the table data byte[] tableData = new byte[tableSize]; @@ -203,29 +209,15 @@ namespace BurnOutSharp.Tools /// Byte array representing the executable /// Pointer to the location in the array to read from /// An array of section headers, null on error - private static IMAGE_SECTION_HEADER[] ReadSections(byte[] fileContent) + internal static IMAGE_SECTION_HEADER[] ReadSections(byte[] fileContent) { if (fileContent == null) return null; try { - unsafe - { - IMAGE_DOS_HEADER idh = IMAGE_DOS_HEADER.Deserialize(fileContent, 0); - IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr); - - var sections = new IMAGE_SECTION_HEADER[ifh.NumberOfSections]; - - int index = idh.NewExeHeaderAddr + Marshal.SizeOf(ifh) + ifh.SizeOfOptionalHeader; - for (int i = 0; i < ifh.NumberOfSections; i++) - { - sections[i] = ReadSection(fileContent, index); index += 40; - } - - return sections; - } - + PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); + return pex.SectionHeaders; } catch { @@ -234,57 +226,27 @@ namespace BurnOutSharp.Tools } /// - /// Read a single section header from a PE executable + /// Convert a virtual address to a physical one /// - /// Byte array representing the executable - /// Pointer to the location in the array to read from - /// Section header, null on error - private static IMAGE_SECTION_HEADER ReadSection(byte[] fileContent, int ptr) + /// Virtual address to convert + /// Array of sections to check against + /// Physical address, 0 on error + internal static uint ConvertVirtualAddress(uint virtualAddress, IMAGE_SECTION_HEADER[] sections) { - try + // Loop through all of the sections + for (int i = 0; i < sections.Length; i++) { - // Get the size of a section header for later - int sectionSize = Marshal.SizeOf(); + // If the section is invalid, just skip it + if (sections[i] == null) + continue; - // If the contents are null or the wrong size, we can't read a section - if (fileContent == null || fileContent.Length < sectionSize) - return null; - - // Create a new section and try our best to read one - IMAGE_SECTION_HEADER section = null; - IntPtr tempPtr = IntPtr.Zero; - try - { - // Get the pointer to where the section will go - tempPtr = Marshal.AllocHGlobal(sectionSize); - - // If we couldn't get the space, just return null - if (tempPtr == IntPtr.Zero) - return null; - - // Copy from the array to the new space - Marshal.Copy(fileContent, ptr, tempPtr, sectionSize); - - // Get the new section and return - section = Marshal.PtrToStructure(tempPtr); - } - catch - { - // We don't care what the error was - return null; - } - finally - { - if (tempPtr != IntPtr.Zero) - Marshal.FreeHGlobal(tempPtr); - } - - return section; - } - catch - { - return null; + // Attempt to derive the physical address from the current section + var section = sections[i]; + if (virtualAddress >= section.VirtualAddress && virtualAddress <= section.VirtualAddress + section.VirtualSize) + return section.PointerToRawData + virtualAddress - section.VirtualAddress; } + + return 0; } } }