diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs similarity index 50% rename from BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs rename to BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs index 42f36217..2405fe2f 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.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_DATA_DIRECTORY + internal class DataDirectoryHeader { /// /// The first field, VirtualAddress, is actually the RVA of the table. @@ -19,24 +19,24 @@ namespace BurnOutSharp.ExecutableType.Microsoft /// public uint Size; - public static IMAGE_DATA_DIRECTORY Deserialize(Stream stream) + public static DataDirectoryHeader Deserialize(Stream stream) { - var idd = new IMAGE_DATA_DIRECTORY(); + var ddh = new DataDirectoryHeader(); - idd.VirtualAddress = stream.ReadUInt32(); - idd.Size = stream.ReadUInt32(); + ddh.VirtualAddress = stream.ReadUInt32(); + ddh.Size = stream.ReadUInt32(); - return idd; + return ddh; } - public static IMAGE_DATA_DIRECTORY Deserialize(byte[] content, int offset) + public static DataDirectoryHeader Deserialize(byte[] content, int offset) { - var idd = new IMAGE_DATA_DIRECTORY(); + var ddh = new DataDirectoryHeader(); - idd.VirtualAddress = BitConverter.ToUInt32(content, offset); offset += 4; - idd.Size = BitConverter.ToUInt32(content, offset); offset += 4; + ddh.VirtualAddress = BitConverter.ToUInt32(content, offset); offset += 4; + ddh.Size = BitConverter.ToUInt32(content, offset); offset += 4; - return idd; + return ddh; } } } \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs new file mode 100644 index 00000000..4964a9e9 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs @@ -0,0 +1,255 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Headers +{ + /// + /// The NE header is a relatively large structure with multiple characteristics. + /// Because of the age of the format some items are unclear in meaning. + /// + [StructLayout(LayoutKind.Sequential)] + internal class NewExecutableHeader + { + /// + /// "NE" [00] + /// + public ushort Magic; + + /// + /// The major linker version [02] + /// + public byte LinkerVersion; + + /// + /// The minor linker version [03] + /// + public byte LinkerRevision; + + /// + /// Offset of Entry Table [04] + /// + public ushort EntryTableOffset; + + /// + /// Length of entry table in bytes [06] + /// + public ushort EntryTableSize; + + /// + /// 32-bit CRC of entire contents of file [08] + /// + public uint CrcChecksum; + + /// + /// Program flags, bitmapped [0C] + /// + public byte ProgramFlags; + + /// + /// Application flags, bitmapped [0D] + /// + public byte ApplicationFlags; + + /// + /// Automatic data segment number [0E] + /// + public ushort Autodata; + + /// + /// Initial heap allocation [10] + /// + public ushort InitialHeapAlloc; + + /// + /// Initial stack allocation [12] + /// + public ushort InitialStackAlloc; + + /// + /// CS:IP entry point, CS is index into segment table [14] + /// + public uint InitialCSIPSetting; + + /// + /// SS:SP inital stack pointer, SS is index into segment table [18] + /// + public uint InitialSSSPSetting; + + /// + /// Number of segments in segment table [1C] + /// + public ushort FileSegmentCount; + + /// + /// Entries in Module Reference Table [1E] + /// + public ushort ModuleReferenceTableSize; + + /// + /// Size of non-resident name table [20] + /// + public ushort NonResidentNameTableSize; + + /// + /// Offset of Segment Table [22] + /// + public ushort SegmentTableOffset; + + /// + /// Offset of Resource Table [24] + /// + public ushort ResourceTableOffset; + + /// + /// Offset of resident name table [26] + /// + public ushort ResidentNameTableOffset; + + /// + /// Offset of Module Reference Table [28] + /// + public ushort ModuleReferenceTableOffset; + + /// + /// Offset of Imported Names Table [2A] + /// + public ushort ImportedNamesTableOffset; + + /// + /// Offset of Non-resident Names Table [2C] + /// + public uint NonResidentNamesTableOffset; + + /// + /// Count of moveable entry points listed in entry table [30] + /// + public ushort MovableEntriesCount; + + /// + /// File allignment size shift count (0-9 (default 512 byte pages)) [32] + /// + public ushort SegmentAlignmentShiftCount; + + /// + /// Count of resource table entries [34] + /// + public ushort ResourceEntriesCount; + + /// + /// Target operating system [36] + /// + public byte TargetOperatingSystem; + + /// + /// Other OS/2 flags [37] + /// + public byte AdditionalFlags; + + /// + /// Offset to return thunks or start of gangload area [38] + /// + public ushort ReturnThunkOffset; + + /// + /// Offset to segment reference thunks or size of gangload area [3A] + /// + public ushort SegmentReferenceThunkOffset; + + /// + /// Minimum code swap area size [3C] + /// + public ushort MinCodeSwapAreaSize; + + /// + /// Windows SDK revison number [3E] + /// + public byte WindowsSDKRevision; + + /// + /// Windows SDK version number [3F] + /// + public byte WindowsSDKVersion; + + public static NewExecutableHeader Deserialize(Stream stream) + { + var neh = new NewExecutableHeader(); + + neh.Magic = stream.ReadUInt16(); + neh.LinkerVersion = stream.ReadByteValue(); + neh.LinkerRevision = stream.ReadByteValue(); + neh.EntryTableOffset = stream.ReadUInt16(); + neh.EntryTableSize = stream.ReadUInt16(); + neh.CrcChecksum = stream.ReadUInt32(); + neh.ProgramFlags = stream.ReadByteValue(); + neh.ApplicationFlags = stream.ReadByteValue(); + neh.Autodata = stream.ReadUInt16(); + neh.InitialHeapAlloc = stream.ReadUInt16(); + neh.InitialStackAlloc = stream.ReadUInt16(); + neh.InitialCSIPSetting = stream.ReadUInt32(); + neh.InitialSSSPSetting = stream.ReadUInt32(); + neh.FileSegmentCount = stream.ReadUInt16(); + neh.ModuleReferenceTableSize = stream.ReadUInt16(); + neh.NonResidentNameTableSize = stream.ReadUInt16(); + neh.SegmentTableOffset = stream.ReadUInt16(); + neh.ResourceTableOffset = stream.ReadUInt16(); + neh.ResidentNameTableOffset = stream.ReadUInt16(); + neh.ModuleReferenceTableOffset = stream.ReadUInt16(); + neh.ImportedNamesTableOffset = stream.ReadUInt16(); + neh.NonResidentNamesTableOffset = stream.ReadUInt32(); + neh.MovableEntriesCount = stream.ReadUInt16(); + neh.SegmentAlignmentShiftCount = stream.ReadUInt16(); + neh.ResourceEntriesCount = stream.ReadUInt16(); + neh.TargetOperatingSystem = stream.ReadByteValue(); + neh.AdditionalFlags = stream.ReadByteValue(); + neh.ReturnThunkOffset = stream.ReadUInt16(); + neh.SegmentReferenceThunkOffset = stream.ReadUInt16(); + neh.MinCodeSwapAreaSize = stream.ReadUInt16(); + neh.WindowsSDKRevision = stream.ReadByteValue(); + neh.WindowsSDKVersion = stream.ReadByteValue(); + + return neh; + } + + public static NewExecutableHeader Deserialize(byte[] contents, int offset) + { + var neh = new NewExecutableHeader(); + + neh.Magic = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.LinkerVersion = contents[offset++]; + neh.LinkerRevision = contents[offset++]; + neh.EntryTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.EntryTableSize = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.CrcChecksum = BitConverter.ToUInt32(contents, offset); offset += 4; + neh.ProgramFlags = contents[offset++]; + neh.ApplicationFlags = contents[offset++]; + neh.Autodata = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.InitialHeapAlloc = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.InitialStackAlloc = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.InitialCSIPSetting = BitConverter.ToUInt32(contents, offset); offset += 4; + neh.InitialSSSPSetting = BitConverter.ToUInt32(contents, offset); offset += 4; + neh.FileSegmentCount = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ModuleReferenceTableSize = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.NonResidentNameTableSize = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.SegmentTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ResourceTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ResidentNameTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ModuleReferenceTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ImportedNamesTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.NonResidentNamesTableOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.MovableEntriesCount = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.SegmentAlignmentShiftCount = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.ResourceEntriesCount = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.TargetOperatingSystem = contents[offset++]; + neh.AdditionalFlags = contents[offset++]; + neh.ReturnThunkOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.SegmentReferenceThunkOffset = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.MinCodeSwapAreaSize = BitConverter.ToUInt16(contents, offset); offset += 2; + neh.WindowsSDKRevision = contents[offset++]; + neh.WindowsSDKVersion = contents[offset++]; + + return neh; + } + } +} diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs index 6fc771fc..beefb919 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs @@ -232,7 +232,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// Data-directory entries following the optional header /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES)] - public IMAGE_DATA_DIRECTORY[] DataDirectories; + public DataDirectoryHeader[] DataDirectories; #endregion @@ -292,10 +292,10 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers ioh.LoaderFlags = stream.ReadUInt32(); ioh.NumberOfRvaAndSizes = stream.ReadUInt32(); - ioh.DataDirectories = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + ioh.DataDirectories = new DataDirectoryHeader[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { - ioh.DataDirectories[i] = IMAGE_DATA_DIRECTORY.Deserialize(stream); + ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(stream); } return ioh; @@ -361,10 +361,10 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers ioh.LoaderFlags = BitConverter.ToUInt32(content, offset); offset += 4; ioh.NumberOfRvaAndSizes = BitConverter.ToUInt32(content, offset); offset += 4; - ioh.DataDirectories = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + ioh.DataDirectories = new DataDirectoryHeader[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { - ioh.DataDirectories[i] = IMAGE_DATA_DIRECTORY.Deserialize(content, offset); offset += 8; + ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(content, offset); offset += 8; } return ioh; diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OS2_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OS2_HEADER.cs deleted file mode 100644 index 3b7a4c2d..00000000 --- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OS2_HEADER.cs +++ /dev/null @@ -1,97 +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 -{ - /// - /// New .EXE header - /// - [StructLayout(LayoutKind.Sequential)] - internal class IMAGE_OS2_HEADER - { - public ushort Magic; // 00 Magic number NE_MAGIC - public byte LinkerVersion; // 02 Linker Version number - public byte LinkerRevision; // 03 Linker Revision number - public ushort EntryTableOffset; // 04 Offset of Entry Table - public ushort EntryTableSize; // 06 Number of bytes in Entry Table - public uint CrcChecksum; // 08 Checksum of whole file - public ushort Flags; // 0C Flag word - public ushort Autodata; // 0E Automatic data segment number - public ushort InitialHeapAlloc; // 10 Initial heap allocation - public ushort InitialStackAlloc; // 12 Initial stack allocation - public uint InitialCSIPSetting; // 14 Initial CS:IP setting - public uint InitialSSSPSetting; // 18 Initial SS:SP setting - public ushort FileSegmentCount; // 1C Count of file segments - public ushort ModuleReferenceTableSize; // 1E Entries in Module Reference Table - public ushort NonResidentNameTableSize; // 20 Size of non-resident name table - public ushort SegmentTableOffset; // 22 Offset of Segment Table - public ushort ResourceTableOffset; // 24 Offset of Resource Table - public ushort ResidentNameTableOffset; // 26 Offset of resident name table - public ushort ModuleReferenceTableOffset; // 28 Offset of Module Reference Table - public ushort ImportedNamesTableOffset; // 2A Offset of Imported Names Table - public uint NonResidentNamesTableOffset; // 2C Offset of Non-resident Names Table - public ushort MovableEntriesCount; // 30 Count of movable entries - public ushort SegmentAlignmentShiftCount; // 32 Segment alignment shift count - public ushort ResourceEntriesCount; // 34 Count of resource entries - public byte TargetOperatingSystem; // 36 Target operating system - public byte AdditionalFlags; // 37 Additional flags - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.NERESWORDS)] - public ushort[] Reserved; // 38 3 reserved words - public byte WindowsSDKRevision; // 3E Windows SDK revison number - public byte WindowsSDKVersion; // 3F Windows SDK version number - - public static IMAGE_OS2_HEADER Deserialize(Stream stream) - { - var ioh = new IMAGE_OS2_HEADER(); - - ioh.Magic = stream.ReadUInt16(); - ioh.LinkerVersion = stream.ReadByteValue(); - ioh.LinkerRevision = stream.ReadByteValue(); - ioh.EntryTableOffset = stream.ReadUInt16(); - ioh.EntryTableSize = stream.ReadUInt16(); - ioh.CrcChecksum = stream.ReadUInt32(); - ioh.Flags = stream.ReadUInt16(); - ioh.Autodata = stream.ReadUInt16(); - ioh.InitialHeapAlloc = stream.ReadUInt16(); - ioh.InitialStackAlloc = stream.ReadUInt16(); - ioh.InitialCSIPSetting = stream.ReadUInt32(); - ioh.InitialSSSPSetting = stream.ReadUInt32(); - ioh.FileSegmentCount = stream.ReadUInt16(); - ioh.ModuleReferenceTableSize = stream.ReadUInt16(); - ioh.NonResidentNameTableSize = stream.ReadUInt16(); - ioh.SegmentTableOffset = stream.ReadUInt16(); - ioh.ResourceTableOffset = stream.ReadUInt16(); - ioh.ResidentNameTableOffset = stream.ReadUInt16(); - ioh.ModuleReferenceTableOffset = stream.ReadUInt16(); - ioh.ImportedNamesTableOffset = stream.ReadUInt16(); - ioh.NonResidentNamesTableOffset = stream.ReadUInt32(); - ioh.MovableEntriesCount = stream.ReadUInt16(); - ioh.SegmentAlignmentShiftCount = stream.ReadUInt16(); - ioh.ResourceEntriesCount = stream.ReadUInt16(); - ioh.TargetOperatingSystem = stream.ReadByteValue(); - ioh.AdditionalFlags = stream.ReadByteValue(); - ioh.Reserved = new ushort[Constants.NERESWORDS]; - for (int i = 0; i < Constants.NERESWORDS; i++) - { - ioh.Reserved[i] = stream.ReadUInt16(); - } - ioh.WindowsSDKRevision = stream.ReadByteValue(); - ioh.WindowsSDKVersion = stream.ReadByteValue(); - - return ioh; - } - } -} diff --git a/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs new file mode 100644 index 00000000..c5e04499 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using BurnOutSharp.ExecutableType.Microsoft.Headers; + +namespace BurnOutSharp.ExecutableType.Microsoft +{ + /// + /// The WIN-NE executable format, designed for Windows 3.x, was the "NE", or "New Executable" format. + /// Again, a 16bit format, it alleviated the maximum size restrictions that the MZ format had. + /// + internal class NewExecutable + { + #region Headers + + /// + /// he DOS stub is a valid MZ exe. + /// This enables the develper to package both an MS-DOS and Win16 version of the program, + /// but normally just prints "This Program requires Microsoft Windows". + /// The e_lfanew field (offset 0x3C) points to the NE header. + // + public MSDOSExecutableHeader DOSStubHeader; + + /// + /// The NE header is a relatively large structure with multiple characteristics. + /// Because of the age of the format some items are unclear in meaning. + /// + public NewExecutableHeader NewExecutableHeader; + + #endregion + + // TODO: Add more and more parts of a standard NE executable, not just the header + + public static NewExecutable Deserialize(Stream stream) + { + NewExecutable nex = new NewExecutable(); + + try + { + // Attempt to read the DOS header first + nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream); stream.Seek(nex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin); + if (nex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE) + return null; + + // If the new header address is invalid for the file, it's not a NE + if (nex.DOSStubHeader.NewExeHeaderAddr >= stream.Length) + return null; + + // Then attempt to read the NE header + nex.NewExecutableHeader = NewExecutableHeader.Deserialize(stream); + if (nex.NewExecutableHeader.Magic != Constants.IMAGE_OS2_SIGNATURE) + return null; + + } + catch (Exception ex) + { + //Console.WriteLine($"Errored out on a file: {ex}"); + return null; + } + + return nex; + } + + public static NewExecutable Deserialize(byte[] content, int offset) + { + NewExecutable nex = new NewExecutable(); + + try + { + unsafe + { + // Attempt to read the DOS header first + nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, offset); offset = nex.DOSStubHeader.NewExeHeaderAddr; + if (nex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE) + return null; + + // If the new header address is invalid for the file, it's not a PE + if (nex.DOSStubHeader.NewExeHeaderAddr >= content.Length) + return null; + + // Then attempt to read the NE header + nex.NewExecutableHeader = NewExecutableHeader.Deserialize(content, offset); offset += Marshal.SizeOf(nex.NewExecutableHeader); + if (nex.NewExecutableHeader.Magic != Constants.IMAGE_OS2_SIGNATURE) + return null; + } + } + catch (Exception ex) + { + //Console.WriteLine($"Errored out on a file: {ex}"); + return null; + } + + return nex; + } + } +} \ No newline at end of file