diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs index 427de590..19752858 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ExportAddressTableEntry.cs @@ -35,7 +35,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return eate; } - public static ExportAddressTableEntry Deserialize(byte[] content, int offset) + public static ExportAddressTableEntry Deserialize(byte[] content, ref int offset) { var eate = new ExportAddressTableEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs index 9500e18e..375bffc0 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/HintNameTableEntry.cs @@ -58,7 +58,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return hnte; } - public static HintNameTableEntry Deserialize(byte[] content, int offset) + public static HintNameTableEntry Deserialize(byte[] content, ref int offset) { var hnte = new HintNameTableEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs index c493a2ba..10c6c9a6 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportAddressTableEntry.cs @@ -66,7 +66,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return iate; } - public static ImportAddressTableEntry Deserialize(byte[] content, int offset) + public static ImportAddressTableEntry Deserialize(byte[] content, ref int offset) { var iate = new ImportAddressTableEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs index 78c3bfa6..5016afb4 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ImportDirectoryTableEntry.cs @@ -66,7 +66,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return idte; } - public static ImportDirectoryTableEntry Deserialize(byte[] content, int offset) + public static ImportDirectoryTableEntry Deserialize(byte[] content, ref int offset) { var idte = new ImportDirectoryTableEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceNameString.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceNameString.cs index f0d3a047..05543491 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceNameString.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceNameString.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Runtime.InteropServices; using System.Text; using BurnOutSharp.Tools; @@ -8,7 +7,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// /// Resource type and name strings /// - [StructLayout(LayoutKind.Sequential)] internal class NEResourceNameString { /// @@ -33,12 +31,12 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return rds; } - public static NEResourceNameString Deserialize(byte[] contents, int offset) + public static NEResourceNameString Deserialize(byte[] content, ref int offset) { var rds = new NEResourceNameString(); - rds.Length = contents[offset++]; - rds.Value = Encoding.ASCII.GetChars(contents, offset, rds.Length); + rds.Length = content[offset++]; + rds.Value = Encoding.ASCII.GetChars(content, offset, rds.Length); offset += rds.Length; return rds; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceTableEntry.cs index aa903042..de19d2aa 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/NEResourceTableEntry.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Entries @@ -8,7 +7,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// /// A table of resources for this type /// - [StructLayout(LayoutKind.Sequential)] internal class NEResourceTableEntry { /// @@ -61,16 +59,16 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return ni; } - public static NEResourceTableEntry Deserialize(byte[] contents, int offset) + public static NEResourceTableEntry Deserialize(byte[] content, ref int offset) { var ni = new NEResourceTableEntry(); - ni.Offset = BitConverter.ToUInt16(contents, offset); offset += 2; - ni.Length = BitConverter.ToUInt16(contents, offset); offset += 2; - ni.Flags = (ResourceTableEntryFlags)BitConverter.ToUInt16(contents, offset); offset += 2; - ni.ResourceID = BitConverter.ToUInt16(contents, offset); offset += 2; - ni.Handle = BitConverter.ToUInt16(contents, offset); offset += 2; - ni.Usage = BitConverter.ToUInt16(contents, offset); offset += 2; + ni.Offset = BitConverter.ToUInt16(content, offset); offset += 2; + ni.Length = BitConverter.ToUInt16(content, offset); offset += 2; + ni.Flags = (ResourceTableEntryFlags)BitConverter.ToUInt16(content, offset); offset += 2; + ni.ResourceID = BitConverter.ToUInt16(content, offset); offset += 2; + ni.Handle = BitConverter.ToUInt16(content, offset); offset += 2; + ni.Usage = BitConverter.ToUInt16(content, offset); offset += 2; return ni; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/NESegmentTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/NESegmentTableEntry.cs index 60659630..e11f6dae 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/NESegmentTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/NESegmentTableEntry.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Entries @@ -11,7 +10,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// EXE header. The first entry in the segment table is segment number 1. /// The following is the structure of a segment table entry. /// - [StructLayout(LayoutKind.Sequential)] internal class NESegmentTableEntry { /// @@ -49,7 +47,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return nste; } - public static NESegmentTableEntry Deserialize(byte[] content, int offset) + public static NESegmentTableEntry Deserialize(byte[] content, ref int offset) { var nste = new NESegmentTableEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDataEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDataEntry.cs index 67d395db..d0700794 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDataEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDataEntry.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.Tools; @@ -11,7 +10,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// /// Each Resource Data entry describes an actual unit of raw data in the Resource Data area. /// - [StructLayout(LayoutKind.Sequential)] internal class ResourceDataEntry { /// @@ -27,7 +25,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// /// A unit of resource data in the Resource Data area. /// - public string EncodedData + public string DataAsUTF8String { get { @@ -38,7 +36,9 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries try { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - return Encoding.GetEncoding(codePage).GetString(Data); + var originalEncoding = Encoding.GetEncoding(codePage); + byte[] convertedData = Encoding.Convert(originalEncoding, Encoding.UTF8, Data); + return Encoding.UTF8.GetString(convertedData); } catch (Exception ex) { @@ -85,7 +85,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return rde; } - public static ResourceDataEntry Deserialize(byte[] content, int offset, SectionHeader[] sections) + public static ResourceDataEntry Deserialize(byte[] content, ref int offset, SectionHeader[] sections) { var rde = new ResourceDataEntry(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs index 31544743..33e150d9 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryString.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using System.Text; using BurnOutSharp.Tools; @@ -11,7 +10,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// 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 { /// @@ -34,12 +32,12 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return rds; } - public static ResourceDirectoryString Deserialize(byte[] content, int offset) + public static ResourceDirectoryString Deserialize(byte[] content, ref int offset) { var rds = new ResourceDirectoryString(); rds.Length = BitConverter.ToUInt16(content, offset); offset += 2; - rds.UnicodeString = Encoding.Unicode.GetString(content, offset, rds.Length); + rds.UnicodeString = Encoding.Unicode.GetString(content, offset, rds.Length); offset += rds.Length; return rds; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs index af450330..fac05895 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceDirectoryTableEntry.cs @@ -111,7 +111,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return rdte; } - public static ResourceDirectoryTableEntry Deserialize(byte[] content, int offset, long sectionStart, SectionHeader[] sections) + public static ResourceDirectoryTableEntry Deserialize(byte[] content, ref int offset, long sectionStart, SectionHeader[] sections) { var rdte = new ResourceDirectoryTableEntry(); @@ -120,7 +120,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries { int nameAddress = (int)(rdte.NameOffset + sectionStart); if (nameAddress >= 0 && nameAddress < content.Length) - rdte.Name = ResourceDirectoryString.Deserialize(content, nameAddress); + rdte.Name = ResourceDirectoryString.Deserialize(content, ref nameAddress); } rdte.DataEntryOffset = BitConverter.ToUInt32(content, offset); offset += 4; @@ -128,13 +128,13 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries { int dataEntryAddress = (int)(rdte.DataEntryOffset + sectionStart); if (dataEntryAddress > 0 && dataEntryAddress < content.Length) - rdte.DataEntry = ResourceDataEntry.Deserialize(content, dataEntryAddress, sections); + rdte.DataEntry = ResourceDataEntry.Deserialize(content, ref dataEntryAddress, sections); } else { int subdirectoryAddress = (int)(rdte.SubdirectoryOffset + sectionStart); if (subdirectoryAddress > 0 && subdirectoryAddress < content.Length) - rdte.Subdirectory = ResourceDirectoryTable.Deserialize(content, subdirectoryAddress, sectionStart, sections); + rdte.Subdirectory = ResourceDirectoryTable.Deserialize(content, ref subdirectoryAddress, sectionStart, sections); } // TODO: Add parsing for further directory table entries in the tree diff --git a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceTypeInformationBlock.cs b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceTypeInformationBlock.cs index 3a078632..1af0ffa1 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceTypeInformationBlock.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Entries/ResourceTypeInformationBlock.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Entries @@ -8,7 +7,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries /// /// Resource type information block /// - [StructLayout(LayoutKind.Sequential)] internal class ResourceTypeInformationBlock { /// @@ -52,18 +50,18 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Entries return rtib; } - public static ResourceTypeInformationBlock Deserialize(byte[] contents, int offset) + public static ResourceTypeInformationBlock Deserialize(byte[] content, ref int offset) { var rtib = new ResourceTypeInformationBlock(); - rtib.TypeID = BitConverter.ToUInt16(contents, offset); offset += 2; - rtib.ResourceCount = BitConverter.ToUInt16(contents, offset); offset += 2; - rtib.Reserved = BitConverter.ToUInt32(contents, offset); offset += 4; + rtib.TypeID = BitConverter.ToUInt16(content, offset); offset += 2; + rtib.ResourceCount = BitConverter.ToUInt16(content, offset); offset += 2; + rtib.Reserved = BitConverter.ToUInt32(content, offset); offset += 4; rtib.ResourceTable = new NEResourceTableEntry[rtib.ResourceCount]; for (int i = 0; i < rtib.ResourceCount; i++) { - rtib.ResourceTable[i] = NEResourceTableEntry.Deserialize(contents, offset); offset += 12; + rtib.ResourceTable[i] = NEResourceTableEntry.Deserialize(content, ref offset); } return rtib; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs index 9445e7fe..daffcacf 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/CommonObjectFileFormatHeader.cs @@ -1,11 +1,9 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers { - [StructLayout(LayoutKind.Sequential)] internal class CommonObjectFileFormatHeader { /// @@ -71,7 +69,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return ifh; } - public static CommonObjectFileFormatHeader Deserialize(byte[] content, int offset) + public static CommonObjectFileFormatHeader Deserialize(byte[] content, ref int offset) { var ifh = new CommonObjectFileFormatHeader(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs index 2405fe2f..287bf67d 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/DataDirectoryHeader.cs @@ -1,11 +1,9 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers { - [StructLayout(LayoutKind.Sequential)] internal class DataDirectoryHeader { /// @@ -29,7 +27,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return ddh; } - public static DataDirectoryHeader Deserialize(byte[] content, int offset) + public static DataDirectoryHeader Deserialize(byte[] content, ref int offset) { var ddh = new DataDirectoryHeader(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs index 058dae53..259b22db 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/MSDOSExecutableHeader.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers @@ -14,7 +13,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// 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 @@ -102,7 +100,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// /// Reserved words [1C] /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES1WDS)] public ushort[] Reserved1; /// @@ -118,7 +115,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// /// Reserved words [28] /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES2WDS)] public ushort[] Reserved2; /// @@ -168,7 +164,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return idh; } - public static MSDOSExecutableHeader Deserialize(byte[] content, int offset, bool asStub = true) + public static MSDOSExecutableHeader Deserialize(byte[] content, ref int offset, bool asStub = true) { MSDOSExecutableHeader idh = new MSDOSExecutableHeader(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs index 581eca6f..53c38f62 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/NewExecutableHeader.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers @@ -10,7 +9,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// Because of the age of the format some items are unclear in meaning. /// /// http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm - [StructLayout(LayoutKind.Sequential)] internal class NewExecutableHeader { /// @@ -216,42 +214,42 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return neh; } - public static NewExecutableHeader Deserialize(byte[] contents, int offset) + public static NewExecutableHeader Deserialize(byte[] content, ref 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.ToUInt32(contents, offset); offset += 4; - 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++]; + neh.Magic = BitConverter.ToUInt16(content, offset); offset += 2; + neh.LinkerVersion = content[offset++]; + neh.LinkerRevision = content[offset++]; + neh.EntryTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.EntryTableSize = BitConverter.ToUInt16(content, offset); offset += 2; + neh.CrcChecksum = BitConverter.ToUInt32(content, offset); offset += 4; + neh.ProgramFlags = content[offset++]; + neh.ApplicationFlags = content[offset++]; + neh.Autodata = BitConverter.ToUInt16(content, offset); offset += 2; + neh.InitialHeapAlloc = BitConverter.ToUInt16(content, offset); offset += 2; + neh.InitialStackAlloc = BitConverter.ToUInt16(content, offset); offset += 2; + neh.InitialCSIPSetting = BitConverter.ToUInt32(content, offset); offset += 4; + neh.InitialSSSPSetting = BitConverter.ToUInt32(content, offset); offset += 4; + neh.FileSegmentCount = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ModuleReferenceTableSize = BitConverter.ToUInt16(content, offset); offset += 2; + neh.NonResidentNameTableSize = BitConverter.ToUInt16(content, offset); offset += 2; + neh.SegmentTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ResourceTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ResidentNameTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ModuleReferenceTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ImportedNamesTableOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.NonResidentNamesTableOffset = BitConverter.ToUInt32(content, offset); offset += 4; + neh.MovableEntriesCount = BitConverter.ToUInt16(content, offset); offset += 2; + neh.SegmentAlignmentShiftCount = BitConverter.ToUInt16(content, offset); offset += 2; + neh.ResourceEntriesCount = BitConverter.ToUInt16(content, offset); offset += 2; + neh.TargetOperatingSystem = content[offset++]; + neh.AdditionalFlags = content[offset++]; + neh.ReturnThunkOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.SegmentReferenceThunkOffset = BitConverter.ToUInt16(content, offset); offset += 2; + neh.MinCodeSwapAreaSize = BitConverter.ToUInt16(content, offset); offset += 2; + neh.WindowsSDKRevision = content[offset++]; + neh.WindowsSDKVersion = content[offset++]; return neh; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs index beefb919..838e7a0d 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers @@ -19,7 +18,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// 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 OptionalHeader { #region Standard Fields @@ -231,7 +229,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// /// Data-directory entries following the optional header /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES)] public DataDirectoryHeader[] DataDirectories; #endregion @@ -301,7 +298,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return ioh; } - public static OptionalHeader Deserialize(byte[] content, int offset) + public static OptionalHeader Deserialize(byte[] content, ref int offset) { var ioh = new OptionalHeader(); @@ -364,7 +361,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers ioh.DataDirectories = new DataDirectoryHeader[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { - ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(content, offset); offset += 8; + ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(content, ref offset); } return ioh; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs b/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs index 18eb2f97..77d9e887 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.Tools; namespace BurnOutSharp.ExecutableType.Microsoft.Headers @@ -12,7 +11,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// 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 { /// @@ -23,7 +21,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers /// 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; /// @@ -110,7 +107,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Headers return ish; } - public static SectionHeader Deserialize(byte[] content, int offset) + public static SectionHeader Deserialize(byte[] content, ref int offset) { var ish = new SectionHeader(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs index 95d78865..daf75bbe 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft.Headers; namespace BurnOutSharp.ExecutableType.Microsoft @@ -41,7 +40,8 @@ namespace BurnOutSharp.ExecutableType.Microsoft try { // Attempt to read the DOS header first - nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream); stream.Seek(nex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin); + nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream); + stream.Seek(nex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin); if (nex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE) return null; @@ -70,22 +70,20 @@ namespace BurnOutSharp.ExecutableType.Microsoft 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; + // Attempt to read the DOS header first + nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, ref 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; + // 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; - } + // Then attempt to read the NE header + nex.NewExecutableHeader = NewExecutableHeader.Deserialize(content, ref offset); + if (nex.NewExecutableHeader.Magic != Constants.IMAGE_OS2_SIGNATURE) + return null; } catch (Exception ex) { diff --git a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs index d6cee7da..e01b07ac 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.ExecutableType.Microsoft.Sections; @@ -234,59 +233,54 @@ namespace BurnOutSharp.ExecutableType.Microsoft try { - unsafe + // Attempt to read the DOS header first + pex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, ref offset); + offset = pex.DOSStubHeader.NewExeHeaderAddr; + if (pex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE) + return null; + + // If the new header address is invalid for the file, it's not a PE + if (pex.DOSStubHeader.NewExeHeaderAddr >= content.Length) + return null; + + // Then attempt to read the PE header + pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(content, ref offset); + if (pex.ImageFileHeader.Signature != Constants.IMAGE_NT_SIGNATURE) + return null; + + // If the optional header is supposed to exist, read that as well + if (pex.ImageFileHeader.SizeOfOptionalHeader > 0) + pex.OptionalHeader = OptionalHeader.Deserialize(content, ref offset); + + // Then read in the section table + pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections]; + for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++) { - // Attempt to read the DOS header first - pex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, offset); offset = pex.DOSStubHeader.NewExeHeaderAddr; - if (pex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE) - return null; + pex.SectionTable[i] = SectionHeader.Deserialize(content, ref offset); + } - // If the new header address is invalid for the file, it's not a PE - if (pex.DOSStubHeader.NewExeHeaderAddr >= content.Length) - return null; + // // Export Table + // var table = pex.GetSection(".edata", true); + // if (table != null && table.VirtualSize > 0) + // { + // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); + // pex.ExportTable = ExportDataSection.Deserialize(content, tableAddress); + // } - // Then attempt to read the PE header - pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(content, offset); offset += Marshal.SizeOf(pex.ImageFileHeader); - if (pex.ImageFileHeader.Signature != Constants.IMAGE_NT_SIGNATURE) - return null; + // // Import Table + // table = pex.GetSection(".idata", true); + // if (table != null && table.VirtualSize > 0) + // { + // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); + // pex.ImportTable = ImportDataSection.Deserialize(content, tableAddress, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0); + // } - // If the optional header is supposed to exist, read that as well - if (pex.ImageFileHeader.SizeOfOptionalHeader > 0) - { - pex.OptionalHeader = OptionalHeader.Deserialize(content, offset); - offset += pex.ImageFileHeader.SizeOfOptionalHeader; - } - - // Then read in the section table - pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections]; - for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++) - { - pex.SectionTable[i] = SectionHeader.Deserialize(content, offset); offset += 40; - } - - // // Export Table - // var table = pex.GetSection(".edata", true); - // if (table != null && table.VirtualSize > 0) - // { - // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); - // pex.ExportTable = ExportDataSection.Deserialize(content, tableAddress); - // } - - // // Import Table - // table = pex.GetSection(".idata", true); - // if (table != null && table.VirtualSize > 0) - // { - // int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); - // pex.ImportTable = ImportDataSection.Deserialize(content, tableAddress, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0); - // } - - // Resource Table - var table = pex.GetSection(".rsrc", true); - if (table != null && table.VirtualSize > 0) - { - int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); - pex.ResourceSection = ResourceSection.Deserialize(content, tableAddress, pex.SectionTable); - } + // Resource Table + var table = pex.GetSection(".rsrc", true); + if (table != null && table.VirtualSize > 0) + { + int tableAddress = (int)ConvertVirtualAddress(table.VirtualAddress, pex.SectionTable); + pex.ResourceSection = ResourceSection.Deserialize(content, ref tableAddress, pex.SectionTable); } } catch (Exception ex) diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/FixedFileInfo.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/FixedFileInfo.cs new file mode 100644 index 00000000..026cd20d --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/FixedFileInfo.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class FixedFileInfo + { + /// + /// Contains the value 0xFEEF04BD. + /// This is used with the szKey member of the VS_VERSIONINFO structure when searching a file for the VS_FIXEDFILEINFO structure. + /// + public uint Signature; + + /// + /// The binary version number of this structure. + /// The high-order word of this member contains the major version number, and the low-order word contains the minor version number. + /// + public uint StrucVersion; + + /// + /// The most significant 32 bits of the file's binary version number. + /// This member is used with dwFileVersionLS to form a 64-bit value used for numeric comparisons. + /// + public uint FileVersionMS; + + /// + /// The least significant 32 bits of the file's binary version number. + /// This member is used with dwFileVersionMS to form a 64-bit value used for numeric comparisons. + /// + public uint FileVersionLS; + + /// + /// The most significant 32 bits of the binary version number of the product with which this file was distributed. + /// This member is used with dwProductVersionLS to form a 64-bit value used for numeric comparisons. + /// + public uint ProductVersionMS; + + /// + /// The least significant 32 bits of the binary version number of the product with which this file was distributed. + /// This member is used with dwProductVersionMS to form a 64-bit value used for numeric comparisons. + /// + public uint ProductVersionLS; + + /// + /// Contains a bitmask that specifies the valid bits in dwFileFlags. + /// A bit is valid only if it was defined when the file was created. + /// + public uint FileFlagsMask; + + /// + /// Contains a bitmask that specifies the Boolean attributes of the file. This member can include one or more of the following values. + /// + /// VS_FF_DEBUG (0x00000001L) The file contains debugging information or is compiled with debugging features enabled. + /// VS_FF_INFOINFERRED (0x00000010L) The file's version structure was created dynamically; therefore, some of the members in this structure may be empty or incorrect. This flag should never be set in a file's VS_VERSIONINFO data. + /// VS_FF_PATCHED (0x00000004L) The file has been modified and is not identical to the original shipping file of the same version number. + /// VS_FF_PRERELEASE (0x00000002L) The file is a development version, not a commercially released product. + /// VS_FF_PRIVATEBUILD (0x00000008L) The file was not built using standard release procedures. If this flag is set, the StringFileInfo structure should contain a PrivateBuild entry. + /// VS_FF_SPECIALBUILD (0x00000020L) The file was built by the original company using standard release procedures but is a variation of the normal file of the same version number. If this flag is set, the StringFileInfo structure should contain a SpecialBuild entry. + /// + /// TODO: Make an enum out of this + public uint FileFlags; + + /// + /// The operating system for which this file was designed. This member can be one of the following values. + /// + /// VOS_DOS (0x00010000L) The file was designed for MS-DOS. + /// VOS_NT (0x00040000L) The file was designed for Windows NT. + /// VOS__WINDOWS16 (0x00000001L) The file was designed for 16-bit Windows. + /// VOS__WINDOWS32 (0x00000004L) The file was designed for 32-bit Windows. + /// VOS_OS216 (0x00020000L) The file was designed for 16-bit OS/2. + /// VOS_OS232 (0x00030000L) The file was designed for 32-bit OS/2. + /// VOS__PM16 (0x00000002L) The file was designed for 16-bit Presentation Manager. + /// VOS__PM32 (0x00000003L) The file was designed for 32-bit Presentation Manager. + /// VOS_UNKNOWN (0x00000000L) The operating system for which the file was designed is unknown to the system. + /// + /// An application can combine these values to indicate that the file was designed for one operating system running on another. + /// The following dwFileOS values are examples of this, but are not a complete list. + /// + /// VOS_DOS_WINDOWS16 (0x00010001L) The file was designed for 16-bit Windows running on MS-DOS. + /// VOS_DOS_WINDOWS32 (0x00010004L) The file was designed for 32-bit Windows running on MS-DOS. + /// VOS_NT_WINDOWS32 (0x00040004L) The file was designed for Windows NT. + /// VOS_OS216_PM16 (0x00020002L) The file was designed for 16-bit Presentation Manager running on 16-bit OS/2. + /// VOS_OS232_PM32 (0x00030003L) The file was designed for 32-bit Presentation Manager running on 32-bit OS/2. + /// + /// TODO: Make an enum out of this + public uint FileOS; + + /// + /// The general type of file. This member can be one of the following values. All other values are reserved. + /// + /// VFT_APP (0x00000001L) The file contains an application. + /// VFT_DLL (0x00000002L) The file contains a DLL. + /// VFT_DRV (0x00000003L) The file contains a device driver. If dwFileType is VFT_DRV, dwFileSubtype contains a more specific description of the driver. + /// VFT_FONT (0x00000004L) The file contains a font. If dwFileType is VFT_FONT, dwFileSubtype contains a more specific description of the font file. + /// VFT_STATIC_LIB (0x00000007L) The file contains a static-link library. + /// VFT_UNKNOWN (0x00000000L) The file type is unknown to the system. + /// VFT_VXD (0x00000005L) The file contains a virtual device. + /// + /// TODO: Make an enum out of this + public uint FileType; + + /// + /// The function of the file. The possible values depend on the value of dwFileType. + /// For all values of dwFileType not described in the following list, dwFileSubtype is zero. + /// + /// If dwFileType is VFT_DRV, dwFileSubtype can be one of the following values. + /// + /// VFT2_DRV_COMM (0x0000000AL) The file contains a communications driver. + /// VFT2_DRV_DISPLAY (0x00000004L) The file contains a display driver. + /// VFT2_DRV_INSTALLABLE (0x00000008L) The file contains an installable driver. + /// VFT2_DRV_KEYBOARD (0x00000002L) The file contains a keyboard driver. + /// VFT2_DRV_LANGUAGE (0x00000003L) The file contains a language driver. + /// VFT2_DRV_MOUSE (0x00000005L) The file contains a mouse driver. + /// VFT2_DRV_NETWORK (0x00000006L) The file contains a network driver. + /// VFT2_DRV_PRINTER (0x00000001L) The file contains a printer driver. + /// VFT2_DRV_SOUND (0x00000009L) The file contains a sound driver. + /// VFT2_DRV_SYSTEM (0x00000007L) The file contains a system driver. + /// VFT2_DRV_VERSIONED_PRINTER (0x0000000CL) The file contains a versioned printer driver. + /// VFT2_UNKNOWN (0x00000000L) The driver type is unknown by the system. + /// + /// If dwFileType is VFT_FONT, dwFileSubtype can be one of the following values. + /// + /// VFT2_FONT_RASTER (0x00000001L) The file contains a raster font. + /// VFT2_FONT_TRUETYPE (0x00000003L) The file contains a TrueType font. + /// VFT2_FONT_VECTOR (0x00000002L) The file contains a vector font. + /// VFT2_UNKNOWN (0x00000000L) The font type is unknown by the system. + /// + /// If dwFileType is VFT_VXD, dwFileSubtype contains the virtual device identifier included in the virtual device control block. + /// All dwFileSubtype values not listed here are reserved. + /// + /// TODO: Make an enum out of this + public uint FileSubtype; + + /// + /// The most significant 32 bits of the file's 64-bit binary creation date and time stamp. + /// + public uint FileDateMS; + + /// + /// The least significant 32 bits of the file's 64-bit binary creation date and time stamp. + /// + public uint FileDateLS; + + public static FixedFileInfo Deserialize(Stream stream) + { + FixedFileInfo ffi = new FixedFileInfo(); + + ushort temp; + while ((temp = stream.ReadUInt16()) == 0x0000); + stream.Seek(-2, SeekOrigin.Current); + + ffi.Signature = stream.ReadUInt32(); + ffi.StrucVersion = stream.ReadUInt32(); + ffi.FileVersionMS = stream.ReadUInt32(); + ffi.FileVersionLS = stream.ReadUInt32(); + ffi.ProductVersionMS = stream.ReadUInt32(); + ffi.ProductVersionLS = stream.ReadUInt32(); + ffi.FileFlagsMask = stream.ReadUInt32(); + ffi.FileFlags = stream.ReadUInt32(); + ffi.FileOS = stream.ReadUInt32(); + ffi.FileType = stream.ReadUInt32(); + ffi.FileSubtype = stream.ReadUInt32(); + ffi.FileDateMS = stream.ReadUInt32(); + ffi.FileDateLS = stream.ReadUInt32(); + + return ffi; + } + + public static FixedFileInfo Deserialize(byte[] content, ref int offset) + { + FixedFileInfo ffi = new FixedFileInfo(); + + ushort temp; + bool padded = false; + while ((temp = BitConverter.ToUInt16(content, offset)) == 0x0000) + { + offset += 2; + padded = true; + } + + if (padded) + offset -= 2; + + ffi.Signature = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.StrucVersion = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileVersionMS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileVersionLS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.ProductVersionMS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.ProductVersionLS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileFlagsMask = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileFlags = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileOS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileType = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileSubtype = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileDateMS = BitConverter.ToUInt32(content, offset); offset += 4; + ffi.FileDateLS = BitConverter.ToUInt32(content, offset); offset += 4; + + return ffi; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/LanguageCodePage.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/LanguageCodePage.cs new file mode 100644 index 00000000..5919937a --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/LanguageCodePage.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + /// + /// If you use the Var structure to list the languages your application or DLL supports instead of using multiple version resources, + /// use the Value member to contain an array of DWORD values indicating the language and code page combinations supported by this file. + /// The low-order word of each DWORD must contain a Microsoft language identifier, and the high-order word must contain the IBM code page number. + /// Either high-order or low-order word can be zero, indicating that the file is language or code page independent. + /// If the Var structure is omitted, the file will be interpreted as both language and code page independent. + /// + internal class LanguageCodePage + { + /// + /// The low-order word of each DWORD must contain a Microsoft language identifier + /// + public ushort MicrosoftLanguageIdentifier; + + /// + /// The high-order word must contain the IBM code page number + /// + public ushort IBMCodePageNumber; + + public static LanguageCodePage Deserialize(Stream stream) + { + LanguageCodePage lcp = new LanguageCodePage(); + + lcp.MicrosoftLanguageIdentifier = stream.ReadUInt16(); + lcp.IBMCodePageNumber = stream.ReadUInt16(); + + return lcp; + } + + public static LanguageCodePage Deserialize(byte[] content, ref int offset) + { + LanguageCodePage lcp = new LanguageCodePage(); + + lcp.MicrosoftLanguageIdentifier = BitConverter.ToUInt16(content, offset); offset += 2; + lcp.IBMCodePageNumber = BitConverter.ToUInt16(content, offset); offset += 2; + + return lcp; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/Resource.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/Resource.cs new file mode 100644 index 00000000..fbd65b4e --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/Resource.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class Resource + { + /// + /// The length, in bytes, of the resource structure. + /// This length does not include any padding that aligns any subsequent version resource data on a 32-bit boundary. + /// + public ushort Length; + + /// + /// The length, in bytes, of the Value member. + /// This value is zero if there is no Value member associated with the current version structure. + /// + public ushort ValueLength; + + /// + /// The type of data in the version resource. + /// This member is 1 if the version resource contains text data and 0 if the version resource contains binary data. + /// + public ushort Type; + + /// + /// A Unicode string representing the key + /// + public string Key; + + public static Resource Deserialize(Stream stream) + { + Resource r = new Resource(); + + while ((r.Length = stream.ReadUInt16()) == 0x0000); + + r.Length = stream.ReadUInt16(); + r.ValueLength = stream.ReadUInt16(); + r.Type = stream.ReadUInt16(); + r.Key = stream.ReadString(Encoding.Unicode); + + return r; + } + + public static Resource Deserialize(byte[] content, ref int offset) + { + Resource r = new Resource(); + + while ((r.Length = BitConverter.ToUInt16(content, offset)) == 0x0000) + { + offset += 2; + } + + offset += 2; + r.ValueLength = BitConverter.ToUInt16(content, offset); offset += 2; + r.Type = BitConverter.ToUInt16(content, offset); offset += 2; + + List keyChars = new List(); + while (BitConverter.ToUInt16(content, offset) != 0x0000) + { + keyChars.Add(Encoding.Unicode.GetChars(content, offset, 2)[0]); offset += 2; + } + offset += 2; + + r.Key = new string(keyChars.ToArray()); + + return r; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/StringFileInfo.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringFileInfo.cs new file mode 100644 index 00000000..b0f35128 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringFileInfo.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class StringFileInfo : Resource + { + /// + /// An array of one or more StringTable structures. + /// Each StringTable structure's szKey member indicates the appropriate language and code page for displaying the text in that StringTable structure. + /// + public StringTable Children; + + public static new StringFileInfo Deserialize(Stream stream) + { + StringFileInfo sfi = new StringFileInfo(); + + Resource resource = Resource.Deserialize(stream); + if (resource.Key != "StringFileInfo") + return null; + + sfi.Length = resource.Length; + sfi.ValueLength = resource.ValueLength; + sfi.Type = resource.Type; + sfi.Key = resource.Key; + sfi.Children = StringTable.Deserialize(stream); + + return sfi; + } + + public static new StringFileInfo Deserialize(byte[] content, ref int offset) + { + StringFileInfo sfi = new StringFileInfo(); + + Resource resource = Resource.Deserialize(content, ref offset); + if (resource.Key != "StringFileInfo") + return null; + + sfi.Length = resource.Length; + sfi.ValueLength = resource.ValueLength; + sfi.Type = resource.Type; + sfi.Key = resource.Key; + sfi.Children = StringTable.Deserialize(content, ref offset); + + return sfi; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/StringStruct.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringStruct.cs new file mode 100644 index 00000000..1f1d4033 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringStruct.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Text; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class StringStruct : Resource + { + /// + /// Typically contains a list of languages that the application or DLL supports. + /// + public string Value; + + public static new StringStruct Deserialize(Stream stream) + { + StringStruct s = new StringStruct(); + + Resource resource = Resource.Deserialize(stream); + + s.Length = resource.Length; + s.ValueLength = resource.ValueLength; + s.Type = resource.Type; + s.Key = resource.Key; + stream.Seek(stream.Position % 4 == 0 ? 0 : 4 - (stream.Position % 4), SeekOrigin.Current); + s.Value = new string(stream.ReadChars(s.ValueLength)); + + return s; + } + + public static new StringStruct Deserialize(byte[] content, ref int offset) + { + StringStruct s = new StringStruct(); + + Resource resource = Resource.Deserialize(content, ref offset); + + s.Length = resource.Length; + s.ValueLength = resource.ValueLength; + s.Type = resource.Type; + s.Key = resource.Key; + offset += offset % 4 == 0 ? 0 : 4 - (offset % 4); + s.Value = Encoding.Unicode.GetString(content, offset, s.ValueLength * 2); offset += s.ValueLength * 2; + + return s; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/StringTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringTable.cs new file mode 100644 index 00000000..3ecdc06d --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/StringTable.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.IO; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class StringTable : Resource + { + /// + /// An array of one or more String structures. + /// + public StringStruct[] Children; + + public static new StringTable Deserialize(Stream stream) + { + StringTable st = new StringTable(); + + Resource resource = Resource.Deserialize(stream); + if (resource.Key.Length != 8) + return null; + + st.Length = resource.Length; + st.ValueLength = resource.ValueLength; + st.Type = resource.Type; + st.Key = resource.Key; + + // TODO: Deserialize the Value array + stream.Seek(st.Length - 6 - (st.Key.Length * 2), SeekOrigin.Begin); + + return st; + } + + public static new StringTable Deserialize(byte[] content, ref int offset) + { + int originalPosition = offset; + StringTable st = new StringTable(); + + Resource resource = Resource.Deserialize(content, ref offset); + if (resource.Key.Length != 8) + return null; + + st.Length = resource.Length; + st.ValueLength = resource.ValueLength; + st.Type = resource.Type; + st.Key = resource.Key; + + var tempValue = new List(); + while (offset - originalPosition < st.Length) + { + tempValue.Add(StringStruct.Deserialize(content, ref offset)); + } + + st.Children = tempValue.ToArray(); + + return st; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/Var.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/Var.cs new file mode 100644 index 00000000..51caea47 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/Var.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class Var : Resource + { + /// + /// An array of one or more values that are language and code page identifier pairs. + /// + /// If you use the Var structure to list the languages your application or DLL supports instead of using multiple version resources, + /// use the Value member to contain an array of DWORD values indicating the language and code page combinations supported by this file. + /// The low-order word of each DWORD must contain a Microsoft language identifier, and the high-order word must contain the IBM code page number. + /// Either high-order or low-order word can be zero, indicating that the file is language or code page independent. + /// If the Var structure is omitted, the file will be interpreted as both language and code page independent. + /// + public LanguageCodePage[] Value; + + public static new Var Deserialize(Stream stream) + { + Var v = new Var(); + + Resource resource = Resource.Deserialize(stream); + if (resource.Key != "Translation") + return null; + + v.Length = resource.Length; + v.ValueLength = resource.ValueLength; + v.Type = resource.Type; + v.Key = resource.Key; + + // TODO: Deserialize the Value array + + return v; + } + + public static new Var Deserialize(byte[] content, ref int offset) + { + Var v = new Var(); + + Resource resource = Resource.Deserialize(content, ref offset); + if (resource.Key != "Translation") + return null; + + v.Length = resource.Length; + v.ValueLength = resource.ValueLength; + v.Type = resource.Type; + v.Key = resource.Key; + + // TODO: Deserialize the Value array + + return v; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/VarFileInfo.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/VarFileInfo.cs new file mode 100644 index 00000000..0952cae3 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/VarFileInfo.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using BurnOutSharp.Tools; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class VarFileInfo : Resource + { + /// + /// Typically contains a list of languages that the application or DLL supports. + /// + public Var Children; + + public static new VarFileInfo Deserialize(Stream stream) + { + VarFileInfo vfi = new VarFileInfo(); + + Resource resource = Resource.Deserialize(stream); + if (resource.Key != "VarFileInfo") + return null; + + vfi.Length = resource.Length; + vfi.ValueLength = resource.ValueLength; + vfi.Type = resource.Type; + vfi.Key = resource.Key; + vfi.Children = Var.Deserialize(stream); + + return vfi; + } + + public static new VarFileInfo Deserialize(byte[] content, ref int offset) + { + VarFileInfo vfi = new VarFileInfo(); + + Resource resource = Resource.Deserialize(content, ref offset); + if (resource.Key != "VarFileInfo") + return null; + + vfi.Length = resource.Length; + vfi.ValueLength = resource.ValueLength; + vfi.Type = resource.Type; + vfi.Key = resource.Key; + vfi.Children = Var.Deserialize(content, ref offset); + + return vfi; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Resources/VersionInfo.cs b/BurnOutSharp/ExecutableType/Microsoft/Resources/VersionInfo.cs new file mode 100644 index 00000000..cc6466a7 --- /dev/null +++ b/BurnOutSharp/ExecutableType/Microsoft/Resources/VersionInfo.cs @@ -0,0 +1,131 @@ +using System.IO; + +namespace BurnOutSharp.ExecutableType.Microsoft.Resources +{ + internal class VersionInfo : Resource + { + /// + /// Arbitrary data associated with this VS_VERSIONINFO structure. + /// The wValueLength member specifies the length of this member; + /// if wValueLength is zero, this member does not exist. + /// + public FixedFileInfo Value; + + /// + /// An array of zero or one StringFileInfo structures, and zero or one VarFileInfo structures + /// that are children of the current VS_VERSIONINFO structure. + /// + public StringFileInfo ChildrenStringFileInfo; + + /// + /// An array of zero or one StringFileInfo structures, and zero or one VarFileInfo structures + /// that are children of the current VS_VERSIONINFO structure. + /// + public VarFileInfo ChildrenVarFileInfo; + + public static new VersionInfo Deserialize(Stream stream) + { + long originalPosition = stream.Position; + + VersionInfo vi = new VersionInfo(); + Resource resource = Resource.Deserialize(stream); + if (resource.Key != "VS_VERSION_INFO") + return null; + + vi.Length = resource.Length; + vi.ValueLength = resource.ValueLength; + vi.Type = resource.Type; + vi.Key = resource.Key; + + if (vi.ValueLength > 0) + vi.Value = FixedFileInfo.Deserialize(stream); + + if (stream.Position - originalPosition > vi.Length) + return vi; + + long preChildOffset = stream.Position; + Resource firstChild = Resource.Deserialize(stream); + if (firstChild.Key == "StringFileInfo") + { + stream.Seek(preChildOffset, SeekOrigin.Begin); + vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(stream); + } + else if (firstChild.Key == "VarFileInfo") + { + stream.Seek(preChildOffset, SeekOrigin.Begin); + vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(stream); + } + + if (stream.Position - originalPosition > vi.Length) + return vi; + + preChildOffset = stream.Position; + Resource secondChild = Resource.Deserialize(stream); + if (secondChild.Key == "StringFileInfo") + { + stream.Seek(preChildOffset, SeekOrigin.Begin); + vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(stream); + } + else if (secondChild.Key == "VarFileInfo") + { + stream.Seek(preChildOffset, SeekOrigin.Begin); + vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(stream); + } + + return vi; + } + + public static new VersionInfo Deserialize(byte[] content, ref int offset) + { + int originalOffset = offset; + + VersionInfo vi = new VersionInfo(); + Resource resource = Resource.Deserialize(content, ref offset); + if (resource.Key != "VS_VERSION_INFO") + return null; + + vi.Length = resource.Length; + vi.ValueLength = resource.ValueLength; + vi.Type = resource.Type; + vi.Key = resource.Key; + + if (vi.ValueLength > 0) + vi.Value = FixedFileInfo.Deserialize(content, ref offset); + + if (offset - originalOffset > vi.Length) + return vi; + + int preChildOffset = offset; + Resource firstChild = Resource.Deserialize(content, ref offset); + if (firstChild.Key == "StringFileInfo") + { + offset = preChildOffset; + vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(content, ref offset); + } + else if (firstChild.Key == "VarFileInfo") + { + offset = preChildOffset; + vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(content, ref offset); + } + + if (offset - originalOffset > vi.Length) + return vi; + + + preChildOffset = offset; + Resource secondChild = Resource.Deserialize(content, ref offset); + if (secondChild.Key == "StringFileInfo") + { + offset = preChildOffset; + vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(content, ref offset); + } + else if (secondChild.Key == "VarFileInfo") + { + offset = preChildOffset; + vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(content, ref offset); + } + + return vi; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs index 197b414f..17b49350 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ExportDataSection.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft.Tables; namespace BurnOutSharp.ExecutableType.Microsoft.Sections @@ -56,18 +55,15 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections return eds; } - public static ExportDataSection Deserialize(byte[] content, int offset) + public static ExportDataSection Deserialize(byte[] content, ref 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 - } + eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(content, ref offset); + // eds.ExportAddressTable = ExportAddressTable.Deserialize(content, ref offset, count: 0); // TODO: Figure out where this count comes from + // eds.NamePointerTable = ExportNamePointerTable.Deserialize(content, ref offset, count: 0); // TODO: Figure out where this count comes from + // eds.OrdinalTable = ExportOrdinalTable.Deserialize(content, ref offset, count: 0); // TODO: Figure out where this count comes from + // eds.ExportNameTable = ExportNameTable.Deserialize(content, ref offset); // TODO: set this table based on the NamePointerTable value return eds; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs index 09fada0b..0fc65024 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ImportDataSection.cs @@ -57,28 +57,24 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections return ids; } - public static ImportDataSection Deserialize(byte[] content, int offset, bool pe32plus, int hintCount) + public static ImportDataSection Deserialize(byte[] content, ref int offset, bool pe32plus, int hintCount) { var ids = new ImportDataSection(); - ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(content, offset); offset += 20 * ids.ImportDirectoryTable.Entries.Length; + ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(content, ref offset); 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 + var tempLookupTable = ImportLookupTable.Deserialize(content, ref offset, pe32plus); + if (tempLookupTable.EntriesPE32 == null && tempLookupTable.EntriesPE32Plus == null) break; tempLookupTables.Add(tempLookupTable); } // TODO: Update the offset, if possible - ids.HintNameTable = HintNameTable.Deserialize(content, offset, hintCount); + ids.HintNameTable = HintNameTable.Deserialize(content, ref offset, hintCount); return ids; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs index eee13b54..1a05deba 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Sections/ResourceSection.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.ExecutableType.Microsoft.Tables; @@ -32,15 +31,12 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Sections return rs; } - public static ResourceSection Deserialize(byte[] content, int offset, SectionHeader[] sections) + public static ResourceSection Deserialize(byte[] content, ref int offset, SectionHeader[] sections) { var rs = new ResourceSection(); - unsafe - { - long sectionStart = offset; - rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, offset, sectionStart, sections); offset += Marshal.SizeOf(rs.ResourceDirectoryTable); - } + long sectionStart = offset; + rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, ref offset, sectionStart, sections); return rs; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs index 42509524..15822d8e 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportAddressTable.cs @@ -26,14 +26,14 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return eat; } - public static ExportAddressTable Deserialize(byte[] content, int offset, int count) + public static ExportAddressTable Deserialize(byte[] content, ref 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; + eat.Entries[i] = ExportAddressTableEntry.Deserialize(content, ref offset); } return eat; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs index 749dfd41..a0430e47 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportDirectoryTable.cs @@ -90,7 +90,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return edt; } - public static ExportDirectoryTable Deserialize(byte[] content, int offset) + public static ExportDirectoryTable Deserialize(byte[] content, ref int offset) { var edt = new ExportDirectoryTable(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs index 72956fca..47b11a98 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportNamePointerTable.cs @@ -28,7 +28,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return enpt; } - public static ExportNamePointerTable Deserialize(byte[] content, int offset, int count) + public static ExportNamePointerTable Deserialize(byte[] content, ref int offset, int count) { var enpt = new ExportNamePointerTable(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs index d33c5576..7ff9ac30 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ExportOrdinalTable.cs @@ -28,7 +28,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return edt; } - public static ExportOrdinalTable Deserialize(byte[] content, int offset, int count) + public static ExportOrdinalTable Deserialize(byte[] content, ref int offset, int count) { var edt = new ExportOrdinalTable(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs index c17f4941..6af73d39 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/HintNameTable.cs @@ -25,14 +25,14 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return hnt; } - public static HintNameTable Deserialize(byte[] content, int offset, int count) + public static HintNameTable Deserialize(byte[] content, ref 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); + hnt.Entries[i] = HintNameTableEntry.Deserialize(content, ref offset); offset += 2 + hnt.Entries[i].Name.Length + hnt.Entries[i].Pad; } diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs index 62d4dabd..cb3dbe8f 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportAddressTable.cs @@ -33,14 +33,14 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return iat; } - public static ImportAddressTable Deserialize(byte[] content, int offset) + public static ImportAddressTable Deserialize(byte[] content, ref int offset) { var iat = new ImportAddressTable(); List tempEntries = new List(); while (true) { - var entry = ImportAddressTableEntry.Deserialize(content, offset); offset += 20; + var entry = ImportAddressTableEntry.Deserialize(content, ref offset); tempEntries.Add(entry); if (entry.IsNull()) break; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs index 71f806e5..39e4844c 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportDirectoryTable.cs @@ -33,14 +33,14 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return idt; } - public static ImportDirectoryTable Deserialize(byte[] content, int offset) + public static ImportDirectoryTable Deserialize(byte[] content, ref int offset) { var idt = new ImportDirectoryTable(); List tempEntries = new List(); while (true) { - var entry = ImportDirectoryTableEntry.Deserialize(content, offset); offset += 20; + var entry = ImportDirectoryTableEntry.Deserialize(content, ref offset); tempEntries.Add(entry); if (entry.IsNull()) break; diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs index ef13cead..9430f900 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ImportLookupTable.cs @@ -58,7 +58,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return ilt; } - public static ImportLookupTable Deserialize(byte[] content, int offset, bool pe32plus) + public static ImportLookupTable Deserialize(byte[] content, ref int offset, bool pe32plus) { var ilt = new ImportLookupTable(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs index d515794b..7138cae4 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft.Entries; using BurnOutSharp.Tools; @@ -17,7 +16,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables /// resource. It also defines the location and size of the resource. /// /// http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm - [StructLayout(LayoutKind.Sequential)] internal class NEResourceTable { /// @@ -69,22 +67,19 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return nrt; } - public static NEResourceTable Deserialize(byte[] contents, int offset) + public static NEResourceTable Deserialize(byte[] content, ref int offset) { var nrt = new NEResourceTable(); - nrt.AlignmentShiftCount = BitConverter.ToUInt16(contents, offset); offset += 2; + nrt.AlignmentShiftCount = BitConverter.ToUInt16(content, offset); offset += 2; var typeInformationBlocks = new List(); while (true) { - unsafe - { - var block = ResourceTypeInformationBlock.Deserialize(contents, offset); offset += Marshal.SizeOf(block); - if (block.TypeID == 0) - break; - - typeInformationBlocks.Add(block); - } + var block = ResourceTypeInformationBlock.Deserialize(content, ref offset); + if (block.TypeID == 0) + break; + + typeInformationBlocks.Add(block); } nrt.TypeInformationBlocks = typeInformationBlocks.ToArray(); @@ -92,14 +87,11 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables var typeAndNameStrings = new List(); while (true) { - unsafe - { - var str = NEResourceNameString.Deserialize(contents, offset); offset += Marshal.SizeOf(str); - if (str.Length == 0) - break; + var str = NEResourceNameString.Deserialize(content, ref offset); + if (str.Length == 0) + break; - typeAndNameStrings.Add(str); - } + typeAndNameStrings.Add(str); } nrt.TypeAndNameStrings = typeAndNameStrings.ToArray(); diff --git a/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs index aa82fd0c..54a1d0f5 100644 --- a/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs +++ b/BurnOutSharp/ExecutableType/Microsoft/Tables/ResourceDirectoryTable.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using BurnOutSharp.ExecutableType.Microsoft.Entries; using BurnOutSharp.ExecutableType.Microsoft.Headers; using BurnOutSharp.Tools; @@ -12,7 +11,6 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables /// 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 { /// @@ -94,7 +92,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables return rdt; } - public static ResourceDirectoryTable Deserialize(byte[] content, int offset, long sectionStart, SectionHeader[] sections) + public static ResourceDirectoryTable Deserialize(byte[] content, ref int offset, long sectionStart, SectionHeader[] sections) { var rdt = new ResourceDirectoryTable(); @@ -108,13 +106,13 @@ namespace BurnOutSharp.ExecutableType.Microsoft.Tables rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries]; for (int i = 0; i < rdt.NumberOfNamedEntries; i++) { - rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sectionStart, sections); offset += 8; + rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, ref offset, sectionStart, sections); } rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries]; for (int i = 0; i < rdt.NumberOfIdEntries; i++) { - rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, offset, sectionStart, sections); offset += 8; + rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, ref offset, sectionStart, sections); } return rdt; diff --git a/BurnOutSharp/PackerType/IntelInstallationFramework.cs b/BurnOutSharp/PackerType/IntelInstallationFramework.cs index f8b50e4d..bb3a9171 100644 --- a/BurnOutSharp/PackerType/IntelInstallationFramework.cs +++ b/BurnOutSharp/PackerType/IntelInstallationFramework.cs @@ -31,7 +31,7 @@ namespace BurnOutSharp.PackerType && (name.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) || name.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase))) { - return $"Intel Installation Framework {Utilities.GetFileVersion(file)}"; + return $"Intel Installation Framework {Utilities.GetFileVersion(fileContent)}"; } name = fvinfo?.ProductName?.Trim(); @@ -39,7 +39,7 @@ namespace BurnOutSharp.PackerType && (name.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) || name.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase))) { - return $"Intel Installation Framework {Utilities.GetFileVersion(file)}"; + return $"Intel Installation Framework {Utilities.GetFileVersion(fileContent)}"; } // Get the sections from the executable, if possible diff --git a/BurnOutSharp/PackerType/MicrosoftCABSFX.cs b/BurnOutSharp/PackerType/MicrosoftCABSFX.cs index da102c37..9d473b4b 100644 --- a/BurnOutSharp/PackerType/MicrosoftCABSFX.cs +++ b/BurnOutSharp/PackerType/MicrosoftCABSFX.cs @@ -36,7 +36,7 @@ namespace BurnOutSharp.PackerType { string version = GetVersion(file, fileContent, null); if (!string.IsNullOrWhiteSpace(version)) - return $"Microsoft CAB SFX v{Utilities.GetFileVersion(file)}"; + return $"Microsoft CAB SFX v{Utilities.GetFileVersion(fileContent)}"; return "Microsoft CAB SFX"; } @@ -46,7 +46,7 @@ namespace BurnOutSharp.PackerType { string version = GetVersion(file, fileContent, null); if (!string.IsNullOrWhiteSpace(version)) - return $"Microsoft CAB SFX v{Utilities.GetFileVersion(file)}"; + return $"Microsoft CAB SFX v{Utilities.GetFileVersion(fileContent)}"; return "Microsoft CAB SFX"; } @@ -158,7 +158,7 @@ namespace BurnOutSharp.PackerType // This method of version detection is suboptimal because the version is sometimes the version of the included software, not the SFX itself. public static string GetVersion(string file, byte[] fileContent, List positions) { - string version = Utilities.GetFileVersion(file); + string version = Utilities.GetFileVersion(fileContent); if (!string.IsNullOrWhiteSpace(version)) return $"v{version}"; diff --git a/BurnOutSharp/PackerType/SetupFactory.cs b/BurnOutSharp/PackerType/SetupFactory.cs index 3bed75c9..9e8334a5 100644 --- a/BurnOutSharp/PackerType/SetupFactory.cs +++ b/BurnOutSharp/PackerType/SetupFactory.cs @@ -122,7 +122,7 @@ namespace BurnOutSharp.PackerType return version; // Then check the file version - version = Utilities.GetFileVersion(file); + version = Utilities.GetFileVersion(fileContent); if (!string.IsNullOrEmpty(version)) return version; diff --git a/BurnOutSharp/ProtectionType/ElectronicArts.cs b/BurnOutSharp/ProtectionType/ElectronicArts.cs index 8b0d8a80..c6251bb4 100644 --- a/BurnOutSharp/ProtectionType/ElectronicArts.cs +++ b/BurnOutSharp/ProtectionType/ElectronicArts.cs @@ -47,13 +47,13 @@ namespace BurnOutSharp.ProtectionType string name = fvinfo?.FileDescription?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.Contains("Registration code installer program")) - return $"EA CdKey Registration Module {Utilities.GetFileVersion(file)}"; + return $"EA CdKey Registration Module {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.Equals("EA DRM Helper", StringComparison.OrdinalIgnoreCase)) - return $"EA DRM Protection {Utilities.GetFileVersion(file)}"; + return $"EA DRM Protection {Utilities.GetFileVersion(fileContent)}"; name = fvinfo?.InternalName?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.Equals("CDCode", StringComparison.Ordinal)) - return $"EA CdKey Registration Module {Utilities.GetFileVersion(file)}"; + return $"EA CdKey Registration Module {Utilities.GetFileVersion(fileContent)}"; // Get the sections from the executable, if possible PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); diff --git a/BurnOutSharp/ProtectionType/GFWL.cs b/BurnOutSharp/ProtectionType/GFWL.cs index 2498452e..0e6655fc 100644 --- a/BurnOutSharp/ProtectionType/GFWL.cs +++ b/BurnOutSharp/ProtectionType/GFWL.cs @@ -28,9 +28,9 @@ namespace BurnOutSharp.ProtectionType string name = fvinfo?.FileDescription?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Games for Windows - LIVE Zero Day Piracy Protection", StringComparison.OrdinalIgnoreCase)) - return $"Games for Windows LIVE - Zero Day Piracy Protection Module {Utilities.GetFileVersion(file)}"; + return $"Games for Windows LIVE - Zero Day Piracy Protection Module {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Games for Windows", StringComparison.OrdinalIgnoreCase)) - return $"Games for Windows LIVE {Utilities.GetFileVersion(file)}"; + return $"Games for Windows LIVE {Utilities.GetFileVersion(fileContent)}"; // Get the sections from the executable, if possible PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); diff --git a/BurnOutSharp/ProtectionType/SolidShield.cs b/BurnOutSharp/ProtectionType/SolidShield.cs index 017e2b2b..1b75b9e5 100644 --- a/BurnOutSharp/ProtectionType/SolidShield.cs +++ b/BurnOutSharp/ProtectionType/SolidShield.cs @@ -57,17 +57,17 @@ namespace BurnOutSharp.ProtectionType string name = fvinfo?.FileDescription?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("DVM Library", StringComparison.OrdinalIgnoreCase)) - return $"SolidShield {Utilities.GetFileVersion(file)}"; + return $"SolidShield {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Solidshield Activation Library", StringComparison.OrdinalIgnoreCase)) - return $"SolidShield Core.dll {Utilities.GetFileVersion(file)}"; + return $"SolidShield Core.dll {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Activation Manager", StringComparison.OrdinalIgnoreCase)) return $"SolidShield Activation Manager Module {GetFileVersion(file, fileContent, null)}"; name = fvinfo?.ProductName?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Solidshield Activation Library", StringComparison.OrdinalIgnoreCase)) - return $"SolidShield Core.dll {Utilities.GetFileVersion(file)}"; + return $"SolidShield Core.dll {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Solidshield Library", StringComparison.OrdinalIgnoreCase)) - return $"SolidShield Core.dll {Utilities.GetFileVersion(file)}"; + return $"SolidShield Core.dll {Utilities.GetFileVersion(fileContent)}"; else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Activation Manager", StringComparison.OrdinalIgnoreCase)) return $"SolidShield Activation Manager Module {GetFileVersion(file, fileContent, null)}"; @@ -224,7 +224,7 @@ namespace BurnOutSharp.ProtectionType { string companyName = Utilities.GetFileVersionInfo(file)?.CompanyName.ToLowerInvariant(); if (!string.IsNullOrWhiteSpace(companyName) && (companyName.Contains("solidshield") || companyName.Contains("tages"))) - return Utilities.GetFileVersion(file); + return Utilities.GetFileVersion(fileContent); return null; } diff --git a/BurnOutSharp/ProtectionType/StarForce.cs b/BurnOutSharp/ProtectionType/StarForce.cs index 6dedb3c8..9464bdfb 100644 --- a/BurnOutSharp/ProtectionType/StarForce.cs +++ b/BurnOutSharp/ProtectionType/StarForce.cs @@ -28,7 +28,7 @@ namespace BurnOutSharp.ProtectionType string name = fvinfo?.LegalCopyright?.Trim(); if (!string.IsNullOrWhiteSpace(name) && name.Contains("Protection Technology")) - return $"StarForce {Utilities.GetFileVersion(file)}"; + return $"StarForce {Utilities.GetFileVersion(fileContent)}"; // TODO: Find what fvinfo field actually maps to this name = fvinfo?.FileDescription?.Trim(); @@ -155,7 +155,7 @@ namespace BurnOutSharp.ProtectionType public static string GetVersion(string file, byte[] fileContent, List positions) { - return $"{Utilities.GetFileVersion(file)} ({fileContent.Skip(positions[1] + 22).TakeWhile(c => c != 0x00)})"; + return $"{Utilities.GetFileVersion(fileContent)} ({fileContent.Skip(positions[1] + 22).TakeWhile(c => c != 0x00)})"; } } } diff --git a/BurnOutSharp/Tools/Extensions.cs b/BurnOutSharp/Tools/Extensions.cs index 1163e384..2b0d93fc 100644 --- a/BurnOutSharp/Tools/Extensions.cs +++ b/BurnOutSharp/Tools/Extensions.cs @@ -1,11 +1,84 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; +using BurnOutSharp.Matching; namespace BurnOutSharp.Tools { - internal static class BufferExtensions + internal static class Extensions { + // TODO: Add extensions for BitConverter.ToX(); offset += x; + #region Byte Arrays + + /// + /// Find all positions of one array in another, if possible, if possible + /// + public static List FindAllPositions(this byte[] stack, byte?[] needle, int start = 0, int end = -1) + { + // Get the outgoing list + List positions = new List(); + + // Initialize the loop variables + bool found = true; + int lastPosition = start; + var matcher = new ContentMatch(needle, end: end); + + // Loop over and get all positions + while (found) + { + matcher.Start = lastPosition; + (found, lastPosition) = matcher.Match(stack, false); + if (found) + positions.Add(lastPosition); + } + + return positions; + } + + /// + /// Find the first position of one array in another, if possible + /// + public static bool FirstPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) + { + var matcher = new ContentMatch(needle, start, end); + (bool found, int foundPosition) = matcher.Match(stack, false); + position = foundPosition; + return found; + } + + /// + /// Find the last position of one array in another, if possible + /// + public static bool LastPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) + { + var matcher = new ContentMatch(needle, start, end); + (bool found, int foundPosition) = matcher.Match(stack, true); + position = foundPosition; + return found; + } + + /// + /// See if a byte array starts with another + /// + public static bool StartsWith(this byte[] stack, byte?[] needle) + { + return stack.FirstPosition(needle, out int _, start: 0, end: 1); + } + + /// + /// See if a byte array ends with another + /// + public static bool EndsWith(this byte[] stack, byte?[] needle) + { + return stack.FirstPosition(needle, out int _, start: stack.Length - needle.Length); + } + + #endregion + + #region Streams + /// /// Read a byte from the stream /// @@ -109,6 +182,32 @@ namespace BurnOutSharp.Tools byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); return BitConverter.ToUInt64(buffer, 0); - } + } + + /// + /// Read a null-terminated string from the stream + /// + public static string ReadString(this Stream stream) => stream.ReadString(Encoding.Default); + + /// + /// Read a null-terminated string from the stream + /// + public static string ReadString(this Stream stream, Encoding encoding) + { + byte[] nullTerminator = encoding.GetBytes(new char[] { '\0' }); + int charWidth = nullTerminator.Length; + + List tempBuffer = new List(); + + byte[] buffer = new byte[charWidth]; + while (stream.Read(buffer, 0, charWidth) != 0 && buffer.SequenceEqual(nullTerminator)) + { + tempBuffer.AddRange(buffer); + } + + return encoding.GetString(tempBuffer.ToArray()); + } + + #endregion } } \ No newline at end of file diff --git a/BurnOutSharp/Tools/Utilities.cs b/BurnOutSharp/Tools/Utilities.cs index 109fb2ec..81516090 100644 --- a/BurnOutSharp/Tools/Utilities.cs +++ b/BurnOutSharp/Tools/Utilities.cs @@ -6,6 +6,8 @@ using System.IO; using System.Linq; using System.Xml; using BurnOutSharp.ExecutableType.Microsoft; +using BurnOutSharp.ExecutableType.Microsoft.Entries; +using BurnOutSharp.ExecutableType.Microsoft.Resources; using BurnOutSharp.ExecutableType.Microsoft.Sections; using BurnOutSharp.ExecutableType.Microsoft.Tables; using BurnOutSharp.Matching; @@ -180,73 +182,6 @@ namespace BurnOutSharp.Tools #endregion - #region Byte Arrays - - /// - /// Find all positions of one array in another, if possible, if possible - /// - public static List FindAllPositions(this byte[] stack, byte?[] needle, int start = 0, int end = -1) - { - // Get the outgoing list - List positions = new List(); - - // Initialize the loop variables - bool found = true; - int lastPosition = start; - var matcher = new ContentMatch(needle, end: end); - - // Loop over and get all positions - while (found) - { - matcher.Start = lastPosition; - (found, lastPosition) = matcher.Match(stack, false); - if (found) - positions.Add(lastPosition); - } - - return positions; - } - - /// - /// Find the first position of one array in another, if possible - /// - public static bool FirstPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) - { - var matcher = new ContentMatch(needle, start, end); - (bool found, int foundPosition) = matcher.Match(stack, false); - position = foundPosition; - return found; - } - - /// - /// Find the last position of one array in another, if possible - /// - public static bool LastPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) - { - var matcher = new ContentMatch(needle, start, end); - (bool found, int foundPosition) = matcher.Match(stack, true); - position = foundPosition; - return found; - } - - /// - /// See if a byte array starts with another - /// - public static bool StartsWith(this byte[] stack, byte?[] needle) - { - return stack.FirstPosition(needle, out int _, start: 0, end: 1); - } - - /// - /// See if a byte array ends with another - /// - public static bool EndsWith(this byte[] stack, byte?[] needle) - { - return stack.FirstPosition(needle, out int _, start: stack.Length - needle.Length); - } - - #endregion - #region Protection /// @@ -269,6 +204,36 @@ namespace BurnOutSharp.Tools } } + /// + /// Get the file version info object related to file contents, if possible + /// + /// Byte array representing the file contents + /// FileVersionInfo object on success, null on error + public static VersionInfo GetVersionInfo(byte[] fileContent) + { + if (fileContent == null || !fileContent.Any()) + return null; + + // If we don't have a PE executable, just return null + PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0); + var resourceSection = pex?.ResourceSection; + if (resourceSection == null) + return null; + + var resource = FindResourceInSection(resourceSection, dataContains: "V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0"); + + try + { + int index = 0; + return VersionInfo.Deserialize(resource.Data, ref index); + } + catch (Exception ex) + { + // Console.WriteLine(ex); + return null; + } + } + /// /// Get the file version as reported by the filesystem /// @@ -284,6 +249,29 @@ namespace BurnOutSharp.Tools else return fvinfo.ProductVersion.Replace(", ", "."); } + + /// + /// Get the file version as reported by the filesystem + /// + /// Byte array representing the file contents + /// Version string, null on error + /// TODO: Add override that takes an existing PE + public static string GetFileVersion(byte[] fileContent) + { + var resourceStrings = GetVersionInfo(fileContent)?.ChildrenStringFileInfo?.Children?.Children; + if (resourceStrings == null) + return null; + + var fileVersion = resourceStrings.FirstOrDefault(s => s.Key == "FileVersion"); + if (!string.IsNullOrWhiteSpace(fileVersion?.Value)) + return fileVersion.Value.Replace(", ", "."); + + var productVersion = resourceStrings.FirstOrDefault(s => s.Key == "ProductVersion"); + if (!string.IsNullOrWhiteSpace(productVersion?.Value)) + return productVersion.Value.Replace(", ", "."); + + return null; + } /// /// Wrapper for GetFileVersion for use in content matching @@ -292,7 +280,7 @@ namespace BurnOutSharp.Tools /// Byte array representing the file contents /// Last matched positions in the contents /// Version string, null on error - public static string GetFileVersion(string file, byte[] fileContent, List positions) => GetFileVersion(file); + public static string GetFileVersion(string file, byte[] fileContent, List positions) => GetFileVersion(fileContent); /// /// Wrapper for GetFileVersion for use in path matching @@ -388,7 +376,7 @@ namespace BurnOutSharp.Tools /// String to use if checking for data starting with a string /// String to use if checking for data contains a string /// Full encoded resource data, null on error - private static string FindResourceInSection(ResourceSection rs, string dataStart = null, string dataContains = null) + private static ResourceDataEntry FindResourceInSection(ResourceSection rs, string dataStart = null, string dataContains = null) { if (rs == null) return null; @@ -403,7 +391,7 @@ namespace BurnOutSharp.Tools /// String to use if checking for data starting with a string /// String to use if checking for data contains a string /// Full encoded resource data, null on error - private static string FindResourceInTable(ResourceDirectoryTable rdt, string dataStart, string dataContains) + private static ResourceDataEntry FindResourceInTable(ResourceDirectoryTable rdt, string dataStart, string dataContains) { if (rdt == null) return null; @@ -412,15 +400,15 @@ namespace BurnOutSharp.Tools { if (rdte.IsResourceDataEntry() && rdte.DataEntry != null) { - if (dataStart != null && rdte.DataEntry.EncodedData.StartsWith(dataStart)) - return rdte.DataEntry.EncodedData; - else if (dataContains != null && rdte.DataEntry.EncodedData.Contains(dataContains)) - return rdte.DataEntry.EncodedData; + if (dataStart != null && rdte.DataEntry.DataAsUTF8String.StartsWith(dataStart)) + return rdte.DataEntry; + else if (dataContains != null && rdte.DataEntry.DataAsUTF8String.Contains(dataContains)) + return rdte.DataEntry; } else { - string manifest = FindResourceInTable(rdte.Subdirectory, dataStart, dataContains); - if (!string.IsNullOrWhiteSpace(manifest)) + var manifest = FindResourceInTable(rdte.Subdirectory, dataStart, dataContains); + if (manifest != null) return manifest; } } @@ -429,15 +417,15 @@ namespace BurnOutSharp.Tools { if (rdte.IsResourceDataEntry() && rdte.DataEntry != null) { - if (dataStart != null && rdte.DataEntry.EncodedData.StartsWith(dataStart)) - return rdte.DataEntry.EncodedData; - else if (dataContains != null && rdte.DataEntry.EncodedData.Contains(dataContains)) - return rdte.DataEntry.EncodedData; + if (dataStart != null && rdte.DataEntry.DataAsUTF8String.StartsWith(dataStart)) + return rdte.DataEntry; + else if (dataContains != null && rdte.DataEntry.DataAsUTF8String.Contains(dataContains)) + return rdte.DataEntry; } else { - string manifest = FindResourceInTable(rdte.Subdirectory, dataStart, dataContains); - if (!string.IsNullOrWhiteSpace(manifest)) + var manifest = FindResourceInTable(rdte.Subdirectory, dataStart, dataContains); + if (manifest != null) return manifest; } } @@ -450,7 +438,7 @@ namespace BurnOutSharp.Tools /// /// ResourceSection from the executable /// Full assembly manifest, null on error - private static string FindAssemblyManifest(ResourceSection rs) => FindResourceInSection(rs, dataStart: " FindResourceInSection(rs, dataStart: " /// Get the assembly identity node from an embedded manifest