diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs similarity index 97% rename from BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs rename to BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs index 3c910fbc..427de590 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs @@ -2,7 +2,7 @@ using System; using System.IO; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft.Tables +namespace BurnOutSharp.ExecutableType.Microsoft.Entries { /// /// Each entry in the export address table is a field that uses one of two formats in the following table. diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/FunctionTableEntry.cs similarity index 97% rename from BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs rename to BurnOutSharp/ExecutableType/Microsoft/Entries/FunctionTableEntry.cs index 10732ff3..9e6e20d8 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/FunctionTableEntry.cs @@ -1,4 +1,4 @@ -namespace BurnOutSharp.ExecutableType.Microsoft.Tables +namespace BurnOutSharp.ExecutableType.Microsoft.Entries { /// /// Each entry in the export address table is a field that uses one of two formats in the following table. diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs similarity index 97% rename from BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs rename to BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs index 838519a1..9500e18e 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs @@ -1,9 +1,8 @@ using System; using System.IO; -using System.Linq; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft.Tables +namespace BurnOutSharp.ExecutableType.Microsoft.Entries { /// /// Each entry in the hint/name table has the following format diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs similarity index 98% rename from BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs rename to BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs index b73f21c6..c493a2ba 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs @@ -2,7 +2,7 @@ using System; using System.IO; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft.Tables +namespace BurnOutSharp.ExecutableType.Microsoft.Entries { /// /// Each import address entry has the following format diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs similarity index 98% rename from BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs rename to BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs index fb5a9221..78c3bfa6 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs @@ -2,7 +2,7 @@ using System; using System.IO; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft.Tables +namespace BurnOutSharp.ExecutableType.Microsoft.Entries { /// /// Each import directory entry has the following format diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs new file mode 100644 index 00000000..b6116213 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Entries +{ + /// + /// The resource directory string area consists of Unicode strings, which are word-aligned. + /// These strings are stored together after the last Resource Directory entry and before the first Resource Data entry. + /// This minimizes the impact of these variable-length strings on the alignment of the fixed-size directory entries. + /// + [StructLayout(LayoutKind.Sequential)] + internal class ResourceDirectoryString + { + /// + /// The size of the string, not including length field itself. + /// + public ushort Length; + + /// + /// The variable-length Unicode string data, word-aligned. + /// + public char[] UnicodeString; + + public static ResourceDirectoryString Deserialize(Stream stream) + { + var rds = new ResourceDirectoryString(); + + rds.Length = stream.ReadUInt16(); + rds.UnicodeString = stream.ReadChars(rds.Length, Encoding.Unicode); + + return rds; + } + } +} diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs new file mode 100644 index 00000000..92971263 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Entries +{ + /// + /// The directory entries make up the rows of a table. + /// Each resource directory entry has the following format. + /// Whether the entry is a Name or ID entry is indicated by the + /// resource directory table, which indicates how many Name and + /// ID entries follow it (remember that all the Name entries + /// precede all the ID entries for the table). All entries for + /// the table are sorted in ascending order: the Name entries + /// by case-sensitive string and the ID entries by numeric value. + /// Offsets are relative to the address in the IMAGE_DIRECTORY_ENTRY_RESOURCE DataDirectory. + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#resource-directory-entries + internal class ResourceDirectoryTableEntry + { + #region Name Entry + + /// + /// The offset of a string that gives the Type, Name, or Language ID entry, depending on level of table. + /// + public uint NameOffset; + + /// + /// A 32-bit integer that identifies the Type, Name, or Language ID entry. + /// + public uint IntegerId => NameOffset; + + /// + /// High bit 0. Address of a Resource Data entry (a leaf). + /// + public uint DataEntryOffset; + + /// + /// High bit 1. The lower 31 bits are the address of another resource directory table (the next level down). + /// + public uint SubdirectoryOffset => DataEntryOffset; + + #endregion + + /// + /// Determine if an entry represents a leaf or another directory table + /// + public bool IsResourceDataEntry() => (DataEntryOffset & (1 << 31)) == 0; + + public static ResourceDirectoryTableEntry Deserialize(Stream stream) + { + var idte = new ResourceDirectoryTableEntry(); + + idte.NameOffset = stream.ReadUInt32(); + idte.DataEntryOffset = stream.ReadUInt32(); + + return idte; + } + + public static ResourceDirectoryTableEntry Deserialize(byte[] content, int offset) + { + var idte = new ResourceDirectoryTableEntry(); + + idte.NameOffset = BitConverter.ToUInt32(content, offset); offset += 4; + idte.DataEntryOffset = BitConverter.ToUInt32(content, offset); offset += 4; + + return idte; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs similarity index 90% rename from BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs rename to BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs index 53946f6b..9445e7fe 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs @@ -3,10 +3,10 @@ using System.IO; using System.Runtime.InteropServices; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft +namespace BurnOutSharp.ExecutableType.Microsoft.Headers { [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_FILE_HEADER + internal class CommonObjectFileFormatHeader { /// /// 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. @@ -55,9 +55,9 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// public ImageObjectCharacteristics Characteristics; - public static IMAGE_FILE_HEADER Deserialize(Stream stream) + public static CommonObjectFileFormatHeader Deserialize(Stream stream) { - var ifh = new IMAGE_FILE_HEADER(); + var ifh = new CommonObjectFileFormatHeader(); ifh.Signature = stream.ReadUInt32(); ifh.Machine = (MachineType)stream.ReadUInt16(); @@ -71,9 +71,9 @@ namespace BurnOutSharp.ExecutableType.Microsoft return ifh; } - public static IMAGE_FILE_HEADER Deserialize(byte[] content, int offset) + public static CommonObjectFileFormatHeader Deserialize(byte[] content, int offset) { - var ifh = new IMAGE_FILE_HEADER(); + var ifh = new CommonObjectFileFormatHeader(); ifh.Signature = BitConverter.ToUInt32(content, offset); offset += 4; ifh.Machine = (MachineType)BitConverter.ToUInt16(content, offset); offset += 2; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs new file mode 100644 index 00000000..058dae53 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs @@ -0,0 +1,211 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Headers +{ + /// + /// The MS-DOS EXE format, also known as MZ after its signature (the initials of Microsoft engineer Mark Zbykowski), + /// was introduced with MS-DOS 2.0 (version 1.0 only sported the simple COM format). It is designed as a relocatable + /// executable running under real mode. As such, only DOS and Windows 9x can use this format natively, but there are + /// several free DOS emulators (e.g., DOSBox) that support it and that run under various operating systems (e.g., + /// Linux, Amiga, Windows NT, etc.). Although they can exist on their own, MZ executables are embedded in all NE, LE, + /// and PE executables, usually as stubs so that when they are ran under DOS, they display a warning. + /// + /// https://wiki.osdev.org/MZ + [StructLayout(LayoutKind.Sequential)] + internal class MSDOSExecutableHeader + { + #region Standard Fields + + /// + /// 0x5A4D (ASCII for 'M' and 'Z') [00] + /// + public ushort Magic; + + /// + /// Number of bytes in the last page. [02] + /// + public ushort LastPageBytes; + + /// + /// Number of whole/partial pages. [04] + /// + public ushort Pages; + + /// + /// Number of entries in the relocation table. [06] + /// + public ushort Relocations; + + /// + /// The number of paragraphs taken up by the header.It can be any value, as the loader + /// just uses it to find where the actual executable data starts. It may be larger than + /// what the "standard" fields take up, and you may use it if you want to include your + /// own header metadata, or put the relocation table there, or use it for any other purpose. [08] + /// + public ushort HeaderParagraphSize; + + /// + /// The number of paragraphs required by the program, excluding the PSP and program image. + /// If no free block is big enough, the loading stops. [0A] + /// + public ushort MinimumExtraParagraphs; + + /// + /// The number of paragraphs requested by the program. + /// If no free block is big enough, the biggest one possible is allocated. [0C] + /// + public ushort MaximumExtraParagraphs; + + /// + /// Relocatable segment address for SS. [0E] + /// + public ushort InitialSSValue; + + /// + /// Initial value for SP. [10] + /// + public ushort InitialSPValue; + + /// + /// When added to the sum of all other words in the file, the result should be zero. [12] + /// + public ushort Checksum; + + /// + /// Initial value for IP. [14] + /// + public ushort InitialIPValue; + + /// + /// Relocatable segment address for CS. [16] + /// + public ushort InitialCSValue; + + /// + /// The (absolute) offset to the relocation table. [18] + /// + public ushort RelocationTableAddr; + + /// + /// Value used for overlay management. + /// If zero, this is the main executable. [1A] + /// + public ushort OverlayNumber; + + #endregion + + #region PE Extensions + + /// + /// Reserved words [1C] + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES1WDS)] + public ushort[] Reserved1; + + /// + /// Defined by name but no other information is given; typically zeroes [24] + /// + public ushort OEMIdentifier; + + /// + /// Defined by name but no other information is given; typically zeroes [26] + /// + public ushort OEMInformation; + + /// + /// Reserved words [28] + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES2WDS)] + public ushort[] Reserved2; + + /// + /// Starting address of the PE header [3C] + /// + public int NewExeHeaderAddr; + + #endregion + + public static MSDOSExecutableHeader Deserialize(Stream stream, bool asStub = true) + { + MSDOSExecutableHeader idh = new MSDOSExecutableHeader(); + + idh.Magic = stream.ReadUInt16(); + idh.LastPageBytes = stream.ReadUInt16(); + idh.Pages = stream.ReadUInt16(); + idh.Relocations = stream.ReadUInt16(); + idh.HeaderParagraphSize = stream.ReadUInt16(); + idh.MinimumExtraParagraphs = stream.ReadUInt16(); + idh.MaximumExtraParagraphs = stream.ReadUInt16(); + idh.InitialSSValue = stream.ReadUInt16(); + idh.InitialSPValue = stream.ReadUInt16(); + idh.Checksum = stream.ReadUInt16(); + idh.InitialIPValue = stream.ReadUInt16(); + idh.InitialCSValue = stream.ReadUInt16(); + idh.RelocationTableAddr = stream.ReadUInt16(); + idh.OverlayNumber = stream.ReadUInt16(); + + // If we're not reading as a stub, return now + if (!asStub) + return idh; + + idh.Reserved1 = new ushort[Constants.ERES1WDS]; + for (int i = 0; i < Constants.ERES1WDS; i++) + { + idh.Reserved1[i] = stream.ReadUInt16(); + } + idh.OEMIdentifier = stream.ReadUInt16(); + idh.OEMInformation = stream.ReadUInt16(); + idh.Reserved2 = new ushort[Constants.ERES2WDS]; + for (int i = 0; i < Constants.ERES2WDS; i++) + { + idh.Reserved2[i] = stream.ReadUInt16(); + } + idh.NewExeHeaderAddr = stream.ReadInt32(); + + return idh; + } + + public static MSDOSExecutableHeader Deserialize(byte[] content, int offset, bool asStub = true) + { + MSDOSExecutableHeader idh = new MSDOSExecutableHeader(); + + idh.Magic = BitConverter.ToUInt16(content, offset); offset += 2; + idh.LastPageBytes = BitConverter.ToUInt16(content, offset); offset += 2; + idh.Pages = BitConverter.ToUInt16(content, offset); offset += 2; + idh.Relocations = BitConverter.ToUInt16(content, offset); offset += 2; + idh.HeaderParagraphSize = BitConverter.ToUInt16(content, offset); offset += 2; + idh.MinimumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2; + idh.MaximumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2; + idh.InitialSSValue = BitConverter.ToUInt16(content, offset); offset += 2; + idh.InitialSPValue = BitConverter.ToUInt16(content, offset); offset += 2; + idh.Checksum = BitConverter.ToUInt16(content, offset); offset += 2; + idh.InitialIPValue = BitConverter.ToUInt16(content, offset); offset += 2; + idh.InitialCSValue = BitConverter.ToUInt16(content, offset); offset += 2; + idh.RelocationTableAddr = BitConverter.ToUInt16(content, offset); offset += 2; + idh.OverlayNumber = BitConverter.ToUInt16(content, offset); offset += 2; + + // If we're not reading as a stub, return now + if (!asStub) + return idh; + + idh.Reserved1 = new ushort[Constants.ERES1WDS]; + for (int i = 0; i < Constants.ERES1WDS; i++) + { + idh.Reserved1[i] = BitConverter.ToUInt16(content, offset); offset += 2; + } + idh.OEMIdentifier = BitConverter.ToUInt16(content, offset); offset += 2; + idh.OEMInformation = BitConverter.ToUInt16(content, offset); offset += 2; + idh.Reserved2 = new ushort[Constants.ERES2WDS]; + for (int i = 0; i < Constants.ERES2WDS; i++) + { + idh.Reserved2[i] = BitConverter.ToUInt16(content, offset); offset += 2; + } + idh.NewExeHeaderAddr = BitConverter.ToInt32(content, offset); offset += 4; + + return idh; + } + } +} diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs similarity index 92% rename from BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs rename to BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs index 145f9fa5..6fc771fc 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs @@ -3,10 +3,24 @@ using System.IO; using System.Runtime.InteropServices; using BurnOutSharp.Tools; -namespace BurnOutSharp.ExecutableType.Microsoft +namespace BurnOutSharp.ExecutableType.Microsoft.Headers { + /// + /// 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. + /// + /// Note that the size of the optional header is not fixed. + /// The SizeOfOptionalHeader field in the COFF header must be used to validate that a probe into the file + /// for a particular data directory does not go beyond SizeOfOptionalHeader. + /// + /// The NumberOfRvaAndSizes field of the optional header should also be used to ensure that no probe for + /// a particular data directory entry goes beyond the optional header. + /// In addition, it is important to validate the optional header magic number for format compatibility. + /// [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_OPTIONAL_HEADER + internal class OptionalHeader { #region Standard Fields @@ -222,9 +236,9 @@ namespace BurnOutSharp.ExecutableType.Microsoft #endregion - public static IMAGE_OPTIONAL_HEADER Deserialize(Stream stream) + public static OptionalHeader Deserialize(Stream stream) { - var ioh = new IMAGE_OPTIONAL_HEADER(); + var ioh = new OptionalHeader(); ioh.Magic = (OptionalHeaderType)stream.ReadUInt16(); ioh.MajorLinkerVersion = stream.ReadByteValue(); @@ -287,9 +301,9 @@ namespace BurnOutSharp.ExecutableType.Microsoft return ioh; } - public static IMAGE_OPTIONAL_HEADER Deserialize(byte[] content, int offset) + public static OptionalHeader Deserialize(byte[] content, int offset) { - var ioh = new IMAGE_OPTIONAL_HEADER(); + var ioh = new OptionalHeader(); ioh.Magic = (OptionalHeaderType)BitConverter.ToUInt16(content, offset); offset += 2; ioh.MajorLinkerVersion = content[offset]; offset++; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs new file mode 100644 index 00000000..18eb2f97 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs @@ -0,0 +1,132 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Headers +{ + /// + /// 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. + /// + [StructLayout(LayoutKind.Sequential)] + internal class SectionHeader + { + /// + /// 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. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_SIZEOF_SHORT_NAME)] + public byte[] 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. + /// + public uint 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. + /// + public uint 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. + /// + public uint 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. + /// + public uint 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. + /// + public uint 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. + /// + [Obsolete] + public uint PointerToLinenumbers; + + /// + /// The number of relocation entries for the section. + /// This is set to zero for executable images. + /// + public ushort NumberOfRelocations; + + /// + /// The number of line-number entries for the section. + /// This value should be zero for an image because COFF debugging information is deprecated. + /// + [Obsolete] + public ushort NumberOfLinenumbers; + + /// + /// The flags that describe the characteristics of the section. + /// + public SectionCharacteristics Characteristics; + + public static SectionHeader Deserialize(Stream stream) + { + var ish = new SectionHeader(); + + ish.Name = stream.ReadBytes(Constants.IMAGE_SIZEOF_SHORT_NAME); + ish.VirtualSize = stream.ReadUInt32(); + ish.VirtualAddress = stream.ReadUInt32(); + ish.SizeOfRawData = stream.ReadUInt32(); + ish.PointerToRawData = stream.ReadUInt32(); + ish.PointerToRelocations = stream.ReadUInt32(); + ish.PointerToLinenumbers = stream.ReadUInt32(); + ish.NumberOfRelocations = stream.ReadUInt16(); + ish.NumberOfLinenumbers = stream.ReadUInt16(); + ish.Characteristics = (SectionCharacteristics)stream.ReadUInt32(); + + return ish; + } + + public static SectionHeader Deserialize(byte[] content, int offset) + { + var ish = new SectionHeader(); + + 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/IMAGE_DOS_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs deleted file mode 100644 index 2315b614..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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; - -namespace BurnOutSharp.ExecutableType.Microsoft -{ - /// - /// DOS 1, 2, 3 .EXE header - /// - [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_DOS_HEADER - { - public ushort Magic; // 00 Magic number - public ushort LastPageBytes; // 02 Bytes on last page of file - public ushort Pages; // 04 Pages in file - public ushort Relocations; // 06 Relocations - public ushort HeaderParagraphSize; // 08 Size of header in paragraphs - public ushort MinimumExtraParagraphs; // 0A Minimum extra paragraphs needed - public ushort MaximumExtraParagraphs; // 0C Maximum extra paragraphs needed - public ushort InitialSSValue; // 0E Initial (relative) SS value - public ushort InitialSPValue; // 10 Initial SP value - public ushort Checksum; // 12 Checksum - public ushort InitialIPValue; // 14 Initial IP value - public ushort InitialCSValue; // 16 Initial (relative) CS value - public ushort RelocationTableAddr; // 18 File address of relocation table - public ushort OverlayNumber; // 1A Overlay number - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES1WDS)] - public ushort[] Reserved1; // 1C Reserved words - public ushort OEMIdentifier; // 24 OEM identifier (for e_oeminfo) - public ushort OEMInformation; // 26 OEM information; e_oemid specific - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES2WDS)] - public ushort[] Reserved2; // 28 Reserved words - public int NewExeHeaderAddr; // 3C File address of new exe header - - public static IMAGE_DOS_HEADER Deserialize(Stream stream) - { - IMAGE_DOS_HEADER idh = new IMAGE_DOS_HEADER(); - - idh.Magic = stream.ReadUInt16(); - idh.LastPageBytes = stream.ReadUInt16(); - idh.Pages = stream.ReadUInt16(); - idh.Relocations = stream.ReadUInt16(); - idh.HeaderParagraphSize = stream.ReadUInt16(); - idh.MinimumExtraParagraphs = stream.ReadUInt16(); - idh.MaximumExtraParagraphs = stream.ReadUInt16(); - idh.InitialSSValue = stream.ReadUInt16(); - idh.InitialSPValue = stream.ReadUInt16(); - idh.Checksum = stream.ReadUInt16(); - idh.InitialIPValue = stream.ReadUInt16(); - idh.InitialCSValue = stream.ReadUInt16(); - idh.RelocationTableAddr = stream.ReadUInt16(); - idh.OverlayNumber = stream.ReadUInt16(); - idh.Reserved1 = new ushort[Constants.ERES1WDS]; - for (int i = 0; i < Constants.ERES1WDS; i++) - { - idh.Reserved1[i] = stream.ReadUInt16(); - } - idh.OEMIdentifier = stream.ReadUInt16(); - idh.OEMInformation = stream.ReadUInt16(); - idh.Reserved2 = new ushort[Constants.ERES2WDS]; - for (int i = 0; i < Constants.ERES2WDS; i++) - { - idh.Reserved2[i] = stream.ReadUInt16(); - } - idh.NewExeHeaderAddr = stream.ReadInt32(); - - return idh; - } - - public static IMAGE_DOS_HEADER Deserialize(byte[] content, int offset) - { - IMAGE_DOS_HEADER idh = new IMAGE_DOS_HEADER(); - - idh.Magic = BitConverter.ToUInt16(content, offset); offset += 2; - idh.LastPageBytes = BitConverter.ToUInt16(content, offset); offset += 2; - idh.Pages = BitConverter.ToUInt16(content, offset); offset += 2; - idh.Relocations = BitConverter.ToUInt16(content, offset); offset += 2; - idh.HeaderParagraphSize = BitConverter.ToUInt16(content, offset); offset += 2; - idh.MinimumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2; - idh.MaximumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2; - idh.InitialSSValue = BitConverter.ToUInt16(content, offset); offset += 2; - idh.InitialSPValue = BitConverter.ToUInt16(content, offset); offset += 2; - idh.Checksum = BitConverter.ToUInt16(content, offset); offset += 2; - idh.InitialIPValue = BitConverter.ToUInt16(content, offset); offset += 2; - idh.InitialCSValue = BitConverter.ToUInt16(content, offset); offset += 2; - idh.RelocationTableAddr = BitConverter.ToUInt16(content, offset); offset += 2; - idh.OverlayNumber = BitConverter.ToUInt16(content, offset); offset += 2; - idh.Reserved1 = new ushort[Constants.ERES1WDS]; - for (int i = 0; i < Constants.ERES1WDS; i++) - { - idh.Reserved1[i] = BitConverter.ToUInt16(content, offset); offset += 2; - } - idh.OEMIdentifier = BitConverter.ToUInt16(content, offset); offset += 2; - idh.OEMInformation = BitConverter.ToUInt16(content, offset); offset += 2; - idh.Reserved2 = new ushort[Constants.ERES2WDS]; - for (int i = 0; i < Constants.ERES2WDS; i++) - { - idh.Reserved2[i] = BitConverter.ToUInt16(content, offset); offset += 2; - } - idh.NewExeHeaderAddr = BitConverter.ToInt32(content, offset); offset += 4; - - return idh; - } - } -} diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIRECTORY.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIRECTORY.cs deleted file mode 100644 index e121984f..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIRECTORY.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.IO; -using System.Runtime.InteropServices; -using BurnOutSharp.Tools; - -namespace BurnOutSharp.ExecutableType.Microsoft -{ - [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_RESOURCE_DIRECTORY - { - public uint Characteristics; - public uint TimeDateStamp; - public ushort MajorVersion; - public ushort MinorVersion; - public ushort NumberOfNamedEntries; - public ushort NumberOfIdEntries; - - public static IMAGE_RESOURCE_DIRECTORY Deserialize(Stream stream) - { - var ird = new IMAGE_RESOURCE_DIRECTORY(); - - ird.Characteristics = stream.ReadUInt32(); - ird.TimeDateStamp = stream.ReadUInt32(); - ird.MajorVersion = stream.ReadUInt16(); - ird.MinorVersion = stream.ReadUInt16(); - ird.NumberOfNamedEntries = stream.ReadUInt16(); - ird.NumberOfIdEntries = stream.ReadUInt16(); - - return ird; - } - } -} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIR_STRING_U.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIR_STRING_U.cs deleted file mode 100644 index 522d79da..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_RESOURCE_DIR_STRING_U.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.IO; -using System.Runtime.InteropServices; -using BurnOutSharp.Tools; - -namespace BurnOutSharp.ExecutableType.Microsoft -{ - [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_RESOURCE_DIR_STRING_U - { - public ushort Length; - public char[] NameString; - - public static IMAGE_RESOURCE_DIR_STRING_U Deserialize(Stream stream) - { - var irdsu = new IMAGE_RESOURCE_DIR_STRING_U(); - - irdsu.Length = stream.ReadUInt16(); - irdsu.NameString = stream.ReadChars(irdsu.Length); - - return irdsu; - } - } -} diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs deleted file mode 100644 index 24b38106..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_SECTION_HEADER.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using BurnOutSharp.Tools; - -namespace BurnOutSharp.ExecutableType.Microsoft -{ - [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_SECTION_HEADER - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_SIZEOF_SHORT_NAME)] - public byte[] Name; - public uint VirtualSize; - public uint VirtualAddress; - public uint SizeOfRawData; - public uint PointerToRawData; - public uint PointerToRelocations; - public uint PointerToLinenumbers; - public ushort NumberOfRelocations; - public ushort NumberOfLinenumbers; - public SectionCharacteristics Characteristics; - - public static IMAGE_SECTION_HEADER Deserialize(Stream stream) - { - var ish = new IMAGE_SECTION_HEADER(); - - ish.Name = stream.ReadBytes(Constants.IMAGE_SIZEOF_SHORT_NAME); - ish.VirtualSize = stream.ReadUInt32(); - ish.VirtualAddress = stream.ReadUInt32(); - ish.SizeOfRawData = stream.ReadUInt32(); - ish.PointerToRawData = stream.ReadUInt32(); - ish.PointerToRelocations = stream.ReadUInt32(); - ish.PointerToLinenumbers = stream.ReadUInt32(); - ish.NumberOfRelocations = stream.ReadUInt16(); - ish.NumberOfLinenumbers = stream.ReadUInt16(); - ish.Characteristics = (SectionCharacteristics)stream.ReadUInt32(); - - 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/PortableExecutable.cs similarity index 67% rename from BurnOutSharp/ExecutableType/Microsoft/PEExecutable.cs rename to BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs index 16e0380e..b73b3aa1 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/PEExecutable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs @@ -1,7 +1,7 @@ using System.IO; using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.ExecutableType.Microsoft.Sections; -using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft { @@ -10,7 +10,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// 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 + internal class PortableExecutable { #region Headers @@ -23,13 +23,13 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// 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; + public MSDOSExecutableHeader DOSStubHeader; /// /// 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; + public CommonObjectFileFormatHeader ImageFileHeader; /// /// Every image file has an optional header that provides information to the loader. @@ -37,7 +37,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// 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; + public OptionalHeader OptionalHeader; /// /// Each row of the section table is, in effect, a section header. @@ -46,7 +46,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// 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; + public SectionHeader[] SectionTable; #endregion @@ -63,34 +63,41 @@ namespace BurnOutSharp.ExecutableType.Microsoft // public ImportDataSection ImportTable; + /// + /// Resources are indexed by a multiple-level binary-sorted tree structure. + // The general design can incorporate 2**31 levels. + // By convention, however, Windows uses three levels + // + public ResourceSection ResourceSection; + #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) + public static PortableExecutable Deserialize(Stream stream) { - PEExecutable pex = new PEExecutable(); + PortableExecutable pex = new PortableExecutable(); 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.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream); stream.Seek(pex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin); + pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(stream); + if (pex.ImageFileHeader.SizeOfOptionalHeader > 0) + pex.OptionalHeader = OptionalHeader.Deserialize(stream); - pex.SectionHeaders = new IMAGE_SECTION_HEADER[pex.COFFFileHeader.NumberOfSections]; - for (int i = 0; i < pex.COFFFileHeader.NumberOfSections; i++) + pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections]; + for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++) { - pex.SectionHeaders[i] = IMAGE_SECTION_HEADER.Deserialize(stream); + pex.SectionTable[i] = SectionHeader.Deserialize(stream); } - // TODO: Uncomment these when RVA conversion works + // TODO: Uncomment these when all directories are understod and implemented // // Export Table - // var table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; + // var table = pex.SectionTable[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; // if (table.VirtualSize > 0) // { - // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); // stream.Seek(tableAddress, SeekOrigin.Begin); // pex.ExportTable = ExportDataSection.Deserialize(stream); // } @@ -112,33 +119,33 @@ namespace BurnOutSharp.ExecutableType.Microsoft return pex; } - public static PEExecutable Deserialize(byte[] content, int offset) + public static PortableExecutable Deserialize(byte[] content, int offset) { - PEExecutable pex = new PEExecutable(); + PortableExecutable pex = new PortableExecutable(); 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.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, offset); offset = pex.DOSStubHeader.NewExeHeaderAddr; + pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(content, offset); offset += Marshal.SizeOf(pex.ImageFileHeader); + if (pex.ImageFileHeader.SizeOfOptionalHeader > 0) { - pex.OptionalHeader = IMAGE_OPTIONAL_HEADER.Deserialize(content, offset); offset += pex.COFFFileHeader.SizeOfOptionalHeader; + pex.OptionalHeader = OptionalHeader.Deserialize(content, offset); offset += pex.ImageFileHeader.SizeOfOptionalHeader; } - pex.SectionHeaders = new IMAGE_SECTION_HEADER[pex.COFFFileHeader.NumberOfSections]; - for (int i = 0; i < pex.COFFFileHeader.NumberOfSections; i++) + pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections]; + for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++) { - pex.SectionHeaders[i] = IMAGE_SECTION_HEADER.Deserialize(content, offset); offset += 40; + pex.SectionTable[i] = SectionHeader.Deserialize(content, offset); offset += 40; } - // TODO: Uncomment these when RVA conversion works + // TODO: Uncomment these when all directories are understod and implemented // // Export Table - // var table = pex.SectionHeaders[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; + // var table = pex.SectionTable[(byte)ImageDirectory.IMAGE_DIRECTORY_ENTRY_EXPORT]; // if (table.VirtualSize > 0) // { - // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionHeaders); + // int tableAddress = (int)EVORE.ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); // pex.ExportTable = ExportDataSection.Deserialize(content, tableAddress); // } diff --git a/BurnOutSharp/ExecutableType/Microsoft/RsrcString.cs b/BurnOutSharp/ExecutableType/Microsoft/RsrcString.cs deleted file mode 100644 index c410ba40..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/RsrcString.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.IO; -using System.Runtime.InteropServices; -using BurnOutSharp.Tools; - -namespace BurnOutSharp.ExecutableType.Microsoft -{ - /// - /// Resource type or name string - /// - /// TODO: Fix this because SizeConst = 0 is not valid - [StructLayout(LayoutKind.Sequential)] - internal class RsrcString - { - /// - /// Number of bytes in string - /// - public byte Length; - - /// - /// Next of string - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0)] - public char[] Text; - - public static RsrcString Deserialize(Stream stream) - { - var rs = new RsrcString(); - - rs.Length = stream.ReadByteValue(); - rs.Text = stream.ReadChars(rs.Length); - - return rs; - } - } -} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs new file mode 100644 index 00000000..95744587 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs @@ -0,0 +1,45 @@ +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Tables; + +namespace BurnOutSharp.ExecutableType.Microsoft.Sections +{ + /// + /// A series of resource directory tables relates all of the levels in the following way: + // Each directory table is followed by a series of directory entries that give the name or + // identifier (ID) for that level (Type, Name, or Language level) and an address of either + // a data description or another directory table. If the address points to a data description, + // then the data is a leaf in the tree. If the address points to another directory table, + // then that table lists directory entries at the next level down + /// + /// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-rsrc-section + internal class ResourceSection + { + /// + /// A table with just one row (unlike the debug directory). + /// This table indicates the locations and sizes of the other export tables. + /// + public ResourceDirectoryTable ResourceDirectoryTable; + + public static ResourceSection Deserialize(Stream stream) + { + var rs = new ResourceSection(); + + rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(stream); + + return rs; + } + + public static ResourceSection Deserialize(byte[] content, int offset) + { + var rs = new ResourceSection(); + + unsafe + { + rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, offset); offset += Marshal.SizeOf(rs.ResourceDirectoryTable); + } + + return rs; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs index 4e7b1432..42509524 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs @@ -1,4 +1,5 @@ using System.IO; +using BurnOutSharp.ExecutableType.Microsoft.Entries; namespace BurnOutSharp.ExecutableType.Microsoft.Tables { diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs index afa83c8f..1056dfe6 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/FunctionTable.cs @@ -1,3 +1,5 @@ +using BurnOutSharp.ExecutableType.Microsoft.Entries; + namespace BurnOutSharp.ExecutableType.Microsoft.Tables { /// diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs index 70854c4d..c17f4941 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; using System.IO; -using BurnOutSharp.Tools; +using BurnOutSharp.ExecutableType.Microsoft.Entries; namespace BurnOutSharp.ExecutableType.Microsoft.Tables { diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs index b849c47e..62d4dabd 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using BurnOutSharp.ExecutableType.Microsoft.Entries; namespace BurnOutSharp.ExecutableType.Microsoft.Tables { diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs index a7054246..71f806e5 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using BurnOutSharp.ExecutableType.Microsoft.Entries; namespace BurnOutSharp.ExecutableType.Microsoft.Tables { diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs new file mode 100644 index 00000000..2ff99503 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Entries; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Tables +{ + /// + /// Each resource directory table has the following format. + /// This data structure should be considered the heading of a table + /// because the table actually consists of directory entries and this structure + /// + [StructLayout(LayoutKind.Sequential)] + internal class ResourceDirectoryTable + { + /// + /// Resource flags. + /// This field is reserved for future use. + /// It is currently set to zero. + /// + public uint Characteristics; + + /// + /// The time that the resource data was created by the resource compiler. + /// + public uint TimeDateStamp; + + /// + /// The major version number, set by the user. + /// + public ushort MajorVersion; + + /// + /// The minor version number, set by the user. + /// + public ushort MinorVersion; + + /// + /// The number of directory entries immediately following + /// the table that use strings to identify Type, Name, or + /// Language entries (depending on the level of the table). + /// + public ushort NumberOfNamedEntries; + + /// + /// The number of directory entries immediately following + /// the Name entries that use numeric IDs for Type, Name, + /// or Language entries. + /// + public ushort NumberOfIdEntries; + + /// + /// The directory entries immediately following + /// the table that use strings to identify Type, Name, or + /// Language entries (depending on the level of the table). + /// + public ResourceDirectoryTableEntry[] NamedEntries; + + /// + /// The directory entries immediately following + /// the Name entries that use numeric IDs for Type, Name, + /// or Language entries. + /// + public ResourceDirectoryTableEntry[] IdEntries; + + // TODO: Determine how to store or reference the resource directory strings + // that immediately follow the last directory entry but before the data + + public static ResourceDirectoryTable Deserialize(Stream stream) + { + var rdt = new ResourceDirectoryTable(); + + rdt.Characteristics = stream.ReadUInt32(); + rdt.TimeDateStamp = stream.ReadUInt32(); + rdt.MajorVersion = stream.ReadUInt16(); + rdt.MinorVersion = stream.ReadUInt16(); + rdt.NumberOfNamedEntries = stream.ReadUInt16(); + rdt.NumberOfIdEntries = stream.ReadUInt16(); + + rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries]; + for (int i = 0; i < rdt.NumberOfNamedEntries; i++) + { + rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream); + } + + rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries]; + for (int i = 0; i < rdt.NumberOfIdEntries; i++) + { + rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream); + } + + return rdt; + } + + public static ResourceDirectoryTable Deserialize(byte[] content, int offset) + { + var rdt = new ResourceDirectoryTable(); + + rdt.Characteristics = BitConverter.ToUInt32(content, offset); offset += 4; + rdt.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4; + rdt.MajorVersion = BitConverter.ToUInt16(content, offset); offset += 2; + rdt.MinorVersion = BitConverter.ToUInt16(content, offset); offset += 2; + rdt.NumberOfNamedEntries = BitConverter.ToUInt16(content, offset); offset += 2; + rdt.NumberOfIdEntries = BitConverter.ToUInt16(content, offset); offset += 2; + + rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries]; + for (int i = 0; i < rdt.NumberOfNamedEntries; i++) + { + rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset); offset += 8; + } + + rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries]; + for (int i = 0; i < rdt.NumberOfIdEntries; i++) + { + rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset); offset += 8; + } + + return rdt; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/PackerType/AdvancedInstaller.cs b/BurnOutSharp/PackerType/AdvancedInstaller.cs index 5e68d954..8e6737ce 100644 --- a/BurnOutSharp/PackerType/AdvancedInstaller.cs +++ b/BurnOutSharp/PackerType/AdvancedInstaller.cs @@ -17,8 +17,8 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { // Get the sections from the executable, if possible - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - var sections = pex?.SectionHeaders; + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; if (sections == null) return null; @@ -26,7 +26,7 @@ namespace BurnOutSharp.PackerType var rdataSection = sections.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).StartsWith(".rdata")); if (rdataSection != null) { - int sectionAddr = (int)EVORE.ConvertVirtualAddress(rdataSection.VirtualAddress, sections); + int sectionAddr = (int)rdataSection.PointerToRawData; int sectionEnd = sectionAddr + (int)rdataSection.VirtualSize; var matchers = new List { diff --git a/BurnOutSharp/PackerType/Armadillo.cs b/BurnOutSharp/PackerType/Armadillo.cs index 05fd653d..b2398751 100644 --- a/BurnOutSharp/PackerType/Armadillo.cs +++ b/BurnOutSharp/PackerType/Armadillo.cs @@ -25,8 +25,8 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { // Get the sections from the executable, if possible - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - var sections = pex?.SectionHeaders; + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; if (sections == null) return null; @@ -38,7 +38,7 @@ namespace BurnOutSharp.PackerType // Loop through all "extension" sections foreach (var section in sections.Where(s => s != null && Encoding.ASCII.GetString(s.Name).Trim('\0').EndsWith("1"))) { - int sectionAddr = (int)EVORE.ConvertVirtualAddress(section.VirtualAddress, sections); + int sectionAddr = (int)section.PointerToRawData; int sectionEnd = sectionAddr + (int)section.VirtualSize; var matchers = new List { diff --git a/BurnOutSharp/PackerType/InnoSetup.cs b/BurnOutSharp/PackerType/InnoSetup.cs index 214a23b9..be1b6b49 100644 --- a/BurnOutSharp/PackerType/InnoSetup.cs +++ b/BurnOutSharp/PackerType/InnoSetup.cs @@ -22,8 +22,8 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { // Get the sections from the executable, if possible - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - var sections = pex?.SectionHeaders; + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; if (sections == null) return null; @@ -34,7 +34,7 @@ namespace BurnOutSharp.PackerType }); if (dataSection != null) { - int sectionAddr = (int)EVORE.ConvertVirtualAddress(dataSection.VirtualAddress, sections); + int sectionAddr = (int)dataSection.PointerToRawData; int sectionEnd = sectionAddr + (int)dataSection.VirtualSize; var matchers = new List { @@ -56,7 +56,7 @@ namespace BurnOutSharp.PackerType } // Get the DOS stub from the executable, if possible - var stub = pex?.MSDOSStub; + var stub = pex?.DOSStubHeader; if (stub == null) return null; diff --git a/BurnOutSharp/PackerType/InstallerVISE.cs b/BurnOutSharp/PackerType/InstallerVISE.cs index 7d316198..0060c7ed 100644 --- a/BurnOutSharp/PackerType/InstallerVISE.cs +++ b/BurnOutSharp/PackerType/InstallerVISE.cs @@ -22,8 +22,8 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { // Get the sections from the executable, if possible - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - var sections = pex?.SectionHeaders; + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; if (sections == null) return null; @@ -34,7 +34,7 @@ namespace BurnOutSharp.PackerType }); if (dataSection != null) { - int sectionAddr = (int)EVORE.ConvertVirtualAddress(dataSection.VirtualAddress, sections); + int sectionAddr = (int)dataSection.PointerToRawData; int sectionEnd = sectionAddr + (int)dataSection.VirtualSize; var matchers = new List { diff --git a/BurnOutSharp/PackerType/IntelInstallationFramework.cs b/BurnOutSharp/PackerType/IntelInstallationFramework.cs index 9ff1d0c8..c488968c 100644 --- a/BurnOutSharp/PackerType/IntelInstallationFramework.cs +++ b/BurnOutSharp/PackerType/IntelInstallationFramework.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Text; +using BurnOutSharp.ExecutableType.Microsoft; using BurnOutSharp.Matching; using BurnOutSharp.Tools; @@ -43,6 +46,18 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { + // Get the sections from the executable, if possible + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; + if (sections == null) + return null; + + // Assembly information lives in the .rsrc section + // I need to find out how to navigate the resources in general + // as well as figure out the specific resources for both + // file info and MUI (XML) info. Once I figure this out, + // that also opens the doors to easier assembly XML checks. + var fvinfo = Utilities.GetFileVersionInfo(file); string name = fvinfo?.FileDescription?.Trim(); diff --git a/BurnOutSharp/PackerType/UPX.cs b/BurnOutSharp/PackerType/UPX.cs index 3a7966e5..5b02bc51 100644 --- a/BurnOutSharp/PackerType/UPX.cs +++ b/BurnOutSharp/PackerType/UPX.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using BurnOutSharp.ExecutableType.Microsoft; +using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.Matching; using BurnOutSharp.Tools; @@ -16,8 +17,8 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includeDebug = false) { // Get the sections from the executable, if possible - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - var sections = pex?.SectionHeaders; + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var sections = pex?.SectionTable; if (sections == null) return null; @@ -92,7 +93,7 @@ namespace BurnOutSharp.PackerType /// Array of sections to check against /// Prefix of the sections to check for /// Real address of the section data, -1 on error - private int FindData(byte[] fileContent, IMAGE_SECTION_HEADER[] sections, string sectionPrefix) + private int FindData(byte[] fileContent, SectionHeader[] sections, string sectionPrefix) { // Get the two matching sections, if possible var firstSection = sections.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).StartsWith($"{sectionPrefix}0")); @@ -103,7 +104,7 @@ namespace BurnOutSharp.PackerType return -1; // Return the first section address - return (int)EVORE.ConvertVirtualAddress(firstSection.VirtualAddress, sections); + return (int)firstSection.PointerToRawData; } } } \ No newline at end of file diff --git a/BurnOutSharp/ProtectionType/SafeDisc.cs b/BurnOutSharp/ProtectionType/SafeDisc.cs index 1daf8c06..bed1e286 100644 --- a/BurnOutSharp/ProtectionType/SafeDisc.cs +++ b/BurnOutSharp/ProtectionType/SafeDisc.cs @@ -75,6 +75,8 @@ namespace BurnOutSharp.ProtectionType // (char)0x00 + (char)0x00 + BoG_ new ContentMatchSet(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, Get320to4xVersion, "SafeDisc"), + // TODO: These two following are section headers. They should be converted to section header checks instead + // stxt774 new ContentMatchSet(new byte?[] { 0x73, 0x74, 0x78, 0x74, 0x37, 0x37, 0x34 }, Get320to4xVersion, "SafeDisc"), diff --git a/BurnOutSharp/Tools/EVORE.cs b/BurnOutSharp/Tools/EVORE.cs index ebb8a905..cd311ed9 100644 --- a/BurnOutSharp/Tools/EVORE.cs +++ b/BurnOutSharp/Tools/EVORE.cs @@ -22,6 +22,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using BurnOutSharp.ExecutableType.Microsoft; +using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.ExecutableType.Microsoft.Sections; using BurnOutSharp.ExecutableType.Microsoft.Tables; @@ -35,7 +36,7 @@ namespace BurnOutSharp.Tools /// 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) + internal static uint ConvertVirtualAddress(uint virtualAddress, SectionHeader[] sections) { // Loop through all of the sections for (int i = 0; i < sections.Length; i++) @@ -65,8 +66,8 @@ namespace BurnOutSharp.Tools try { - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); - return pex.COFFFileHeader.Characteristics.HasFlag(ImageObjectCharacteristics.IMAGE_FILE_DLL); + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + return pex.ImageFileHeader.Characteristics.HasFlag(ImageObjectCharacteristics.IMAGE_FILE_DLL); } catch { @@ -125,13 +126,13 @@ namespace BurnOutSharp.Tools unsafe { // Read all of the executable header information - PEExecutable pex = PEExecutable.Deserialize(fileContent, 0); + PortableExecutable pex = PortableExecutable.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)ConvertVirtualAddress(idei.VirtualAddress, pex.SectionHeaders); + int tableIndex = (int)ConvertVirtualAddress(idei.VirtualAddress, pex.SectionTable); int tableSize = (int)idei.Size; if (tableIndex <= 0 || tableSize <= 0) return null; diff --git a/BurnOutSharp/Tools/Extensions.cs b/BurnOutSharp/Tools/Extensions.cs index faf7ec36..1163e384 100644 --- a/BurnOutSharp/Tools/Extensions.cs +++ b/BurnOutSharp/Tools/Extensions.cs @@ -39,11 +39,16 @@ namespace BurnOutSharp.Tools /// /// Read a character array from the stream /// - public static char[] ReadChars(this Stream stream, int count) + public static char[] ReadChars(this Stream stream, int count) => stream.ReadChars(count, Encoding.Default); + + /// + /// Read a character array from the stream + /// + public static char[] ReadChars(this Stream stream, int count, Encoding encoding) { byte[] buffer = new byte[count]; stream.Read(buffer, 0, count); - return Encoding.Default.GetString(buffer).ToCharArray(); + return encoding.GetString(buffer).ToCharArray(); } ///