diff --git a/exeinfo/Program.cs b/exeinfo/Program.cs index a33a306..e1908e1 100644 --- a/exeinfo/Program.cs +++ b/exeinfo/Program.cs @@ -36,11 +36,9 @@ namespace exeinfo { class MainClass { - static libexeinfo.NE.Header neHdr; - public static void Main(string[] args) { - if(args.Length != 1) + if (args.Length != 1) { Console.WriteLine("exeinfo version 0.1 © 2017 Natalia Portillo"); Console.WriteLine("Usage: exeinfo file.exe"); @@ -52,83 +50,65 @@ namespace exeinfo bool recognized = false; MZ mzExe = new MZ(exeFs); + NE neExe = new NE(exeFs); - if(mzExe.IsMZ) + if (mzExe.IsMZ) { recognized = true; Console.Write(mzExe.GetInfo()); + } - if (mzExe.Header.new_offset < exeFs.Length) + if (neExe.IsNE) + { + recognized = true; + Console.Write(neExe.GetInfo()); + foreach (NE.Version vers in neExe.Versions) { - exeFs.Seek(mzExe.Header.new_offset, SeekOrigin.Begin); + Console.WriteLine("\tVersion resource {0}:", vers.Name); + Console.WriteLine("\t\tFile version: {0}", vers.FileVersion); + Console.WriteLine("\t\tProduct version: {0}", vers.ProductVersion); + Console.WriteLine("\t\tFile type: {0}", NE.Version.TypeToString(vers.FileType)); + if (vers.FileType == NE.VersionFileType.VFT_DRV) + Console.WriteLine("\t\tFile subtype: {0} driver", NE.Version.DriverToString(vers.FileSubtype)); + else if (vers.FileType == NE.VersionFileType.VFT_DRV) + Console.WriteLine("\t\tFile subtype: {0} font", NE.Version.FontToString(vers.FileSubtype)); + else if (vers.FileSubtype > 0) + Console.WriteLine("\t\tFile subtype: {0}", (uint)vers.FileSubtype); + Console.WriteLine("\t\tFile flags: {0}", vers.FileFlags); + Console.WriteLine("\t\tFile OS: {0}", NE.Version.OsToString(vers.FileOS)); - byte[] buffer = new byte[Marshal.SizeOf(typeof(libexeinfo.NE.Header))]; - exeFs.Read(buffer, 0, buffer.Length); - IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); - Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); - neHdr = (libexeinfo.NE.Header)Marshal.PtrToStructure(hdrPtr, typeof(libexeinfo.NE.Header)); - Marshal.FreeHGlobal(hdrPtr); - - if (neHdr.signature == libexeinfo.NE.Signature) + foreach (KeyValuePair> strByLang in vers.StringsByLanguage) { - Console.Write(libexeinfo.NE.GetInfo(neHdr)); - libexeinfo.NE.ResourceTable resources = libexeinfo.NE.GetResources(exeFs, mzExe.Header.new_offset, neHdr.resource_table_offset); - foreach(libexeinfo.NE.ResourceType type in resources.types) + string cultureName; + string encodingName; + + try { - if((type.id & 0x7FFF) == (int)libexeinfo.NE.ResourceTypes.RT_VERSION) - { - foreach(libexeinfo.NE.Resource resource in type.resources) - { - libexeinfo.NE.Version vers = new libexeinfo.NE.Version(resource.data); - Console.WriteLine("\tVersion resource {0}:", resource.name); - Console.WriteLine("\t\tFile version: {0}", vers.FileVersion); - Console.WriteLine("\t\tProduct version: {0}", vers.ProductVersion); - Console.WriteLine("\t\tFile type: {0}", libexeinfo.NE.Version.TypeToString(vers.FileType)); - if(vers.FileType == libexeinfo.NE.VersionFileType.VFT_DRV) - Console.WriteLine("\t\tFile subtype: {0} driver", libexeinfo.NE.Version.DriverToString(vers.FileSubtype)); - else if (vers.FileType == libexeinfo.NE.VersionFileType.VFT_DRV) - Console.WriteLine("\t\tFile subtype: {0} font", libexeinfo.NE.Version.FontToString(vers.FileSubtype)); - else if(vers.FileSubtype > 0) - Console.WriteLine("\t\tFile subtype: {0}", (uint)vers.FileSubtype); - Console.WriteLine("\t\tFile flags: {0}", vers.FileFlags); - Console.WriteLine("\t\tFile OS: {0}", libexeinfo.NE.Version.OsToString(vers.FileOS)); - - foreach (KeyValuePair> strByLang in vers.StringsByLanguage) - { - string cultureName; - string encodingName; - - try - { - cultureName = new CultureInfo(Convert.ToInt32(strByLang.Key.Substring(0, 4), 16)).DisplayName; - } - catch - { - cultureName = string.Format("unsupported culture 0x{0:X4}", Convert.ToInt32(strByLang.Key.Substring(0, 4), 16)); - } - - try - { - encodingName = Encoding.GetEncoding(Convert.ToInt32(strByLang.Key.Substring(4), 16)).EncodingName; - } - catch - { - encodingName = string.Format("unsupported encoding 0x{0:X4}", Convert.ToInt32(strByLang.Key.Substring(4), 16)); - } - - Console.WriteLine("\t\tStrings for {0} in codepage {1}:", cultureName, encodingName); - foreach(KeyValuePair strings in strByLang.Value) - Console.WriteLine("\t\t\t{0}: {1}", strings.Key, strings.Value); - } - } - } + cultureName = new CultureInfo(Convert.ToInt32(strByLang.Key.Substring(0, 4), 16)).DisplayName; } + catch + { + cultureName = string.Format("unsupported culture 0x{0:X4}", Convert.ToInt32(strByLang.Key.Substring(0, 4), 16)); + } + + try + { + encodingName = Encoding.GetEncoding(Convert.ToInt32(strByLang.Key.Substring(4), 16)).EncodingName; + } + catch + { + encodingName = string.Format("unsupported encoding 0x{0:X4}", Convert.ToInt32(strByLang.Key.Substring(4), 16)); + } + + Console.WriteLine("\t\tStrings for {0} in codepage {1}:", cultureName, encodingName); + foreach (KeyValuePair strings in strByLang.Value) + Console.WriteLine("\t\t\t{0}: {1}", strings.Key, strings.Value); } - } + } } if (!recognized) - Console.WriteLine("Executalbe format not recognized"); - } + Console.WriteLine("Executable format not recognized"); + } } } diff --git a/libexeinfo/NE/Info.cs b/libexeinfo/NE/Info.cs index 502b2f5..7eda989 100644 --- a/libexeinfo/NE/Info.cs +++ b/libexeinfo/NE/Info.cs @@ -33,7 +33,7 @@ namespace libexeinfo { public partial class NE { - public static string GetInfo(Header header) + public static string GetInfo(NEHeader header) { StringBuilder sb = new StringBuilder(); sb.AppendLine("New Executable (NE):"); @@ -158,15 +158,20 @@ namespace libexeinfo return sb.ToString(); } - public static ResourceTable GetResources(FileStream exeFs, uint neStart, ushort tableOff) + public string GetInfo() + { + return GetInfo(Header); + } + + public static ResourceTable GetResources(FileStream stream, uint neStart, ushort tableOff) { - long oldPosition = exeFs.Position; + long oldPosition = stream.Position; byte[] DW = new byte[2]; byte[] DD = new byte[4]; - exeFs.Position = neStart + tableOff; + stream.Position = neStart + tableOff; ResourceTable table = new ResourceTable(); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); table.alignment_shift = BitConverter.ToUInt16(DW, 0); List types = new List(); @@ -174,29 +179,29 @@ namespace libexeinfo while (true) { ResourceType type = new ResourceType(); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.id = BitConverter.ToUInt16(DW, 0); if (type.id == 0) break; - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.count = BitConverter.ToUInt16(DW, 0); - exeFs.Read(DD, 0, 4); + stream.Read(DD, 0, 4); type.reserved = BitConverter.ToUInt32(DD, 0); type.resources = new Resource[type.count]; for (int i = 0; i < type.count; i++) { type.resources[i] = new Resource(); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.resources[i].dataOffset = BitConverter.ToUInt16(DW, 0); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.resources[i].length = BitConverter.ToUInt16(DW, 0); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.resources[i].flags = (ResourceFlags)BitConverter.ToUInt16(DW, 0); - exeFs.Read(DW, 0, 2); + stream.Read(DW, 0, 2); type.resources[i].id = BitConverter.ToUInt16(DW, 0); - exeFs.Read(DD, 0, 4); + stream.Read(DD, 0, 4); type.resources[i].reserved = BitConverter.ToUInt32(DD, 0); } @@ -211,10 +216,10 @@ namespace libexeinfo { byte len; byte[] str; - exeFs.Position = neStart + tableOff + table.types[t].id; - len = (byte)exeFs.ReadByte(); + stream.Position = neStart + tableOff + table.types[t].id; + len = (byte)stream.ReadByte(); str = new byte[len]; - exeFs.Read(str, 0, len); + stream.Read(str, 0, len); table.types[t].name = Encoding.ASCII.GetString(str); } else @@ -226,22 +231,22 @@ namespace libexeinfo { byte len; byte[] str; - exeFs.Position = neStart + tableOff + table.types[t].resources[r].id; - len = (byte)exeFs.ReadByte(); + stream.Position = neStart + tableOff + table.types[t].resources[r].id; + len = (byte)stream.ReadByte(); str = new byte[len]; - exeFs.Read(str, 0, len); + stream.Read(str, 0, len); table.types[t].resources[r].name = Encoding.ASCII.GetString(str); } else table.types[t].resources[r].name = string.Format("{0}", table.types[t].resources[r].id & 0x7FFF); table.types[t].resources[r].data = new byte[table.types[t].resources[r].length * (1 << table.alignment_shift)]; - exeFs.Position = table.types[t].resources[r].dataOffset * (1 << table.alignment_shift); - exeFs.Read(table.types[t].resources[r].data, 0, table.types[t].resources[r].data.Length); + stream.Position = table.types[t].resources[r].dataOffset * (1 << table.alignment_shift); + stream.Read(table.types[t].resources[r].data, 0, table.types[t].resources[r].data.Length); } } - exeFs.Position = oldPosition; + stream.Position = oldPosition; return table; } diff --git a/libexeinfo/NE/NE.cs b/libexeinfo/NE/NE.cs new file mode 100644 index 0000000..9d92895 --- /dev/null +++ b/libexeinfo/NE/NE.cs @@ -0,0 +1,136 @@ +// +// NE.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2017 Copyright © Claunia.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace libexeinfo +{ + public partial class NE + { + public readonly FileStream BaseStream; + public readonly NEHeader Header; + public readonly bool IsNE; + public readonly MZ BaseExecutable; + public readonly ResourceTable Resources; + public readonly Version[] Versions; + + public NE(string path) + { + IsNE = false; + BaseStream = File.Open(path, FileMode.Open, FileAccess.Read); + BaseExecutable = new MZ(BaseStream); + if(BaseExecutable.IsMZ) + { + if(BaseExecutable.Header.new_offset < BaseStream.Length) + { + BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(NEHeader))]; + BaseStream.Read(buffer, 0, buffer.Length); + IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); + Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader)); + Marshal.FreeHGlobal(hdrPtr); + IsNE = Header.signature == Signature; + Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset); + Versions = GetVersions().ToArray(); + } + } + } + + public NE(FileStream stream) + { + IsNE = false; + BaseStream = stream; + BaseExecutable = new MZ(BaseStream); + if (BaseExecutable.IsMZ) + { + if (BaseExecutable.Header.new_offset < BaseStream.Length) + { + BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(NEHeader))]; + BaseStream.Read(buffer, 0, buffer.Length); + IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); + Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader)); + Marshal.FreeHGlobal(hdrPtr); + IsNE = Header.signature == Signature; + Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset); + Versions = GetVersions().ToArray(); + } + } + } + + public bool Identify() + { + return IsNE; + } + + public static bool Identify(string path) + { + FileStream BaseStream = File.Open(path, FileMode.Open, FileAccess.Read); + MZ BaseExecutable = new MZ(BaseStream); + if (BaseExecutable.IsMZ) + { + if (BaseExecutable.Header.new_offset < BaseStream.Length) + { + BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(NEHeader))]; + BaseStream.Read(buffer, 0, buffer.Length); + IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); + NEHeader Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader)); + Marshal.FreeHGlobal(hdrPtr); + return Header.signature == Signature; + } + } + + return false; + } + + public static bool Identify(FileStream stream) + { + FileStream BaseStream = stream; + MZ BaseExecutable = new MZ(BaseStream); + if (BaseExecutable.IsMZ) + { + if (BaseExecutable.Header.new_offset < BaseStream.Length) + { + BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(NEHeader))]; + BaseStream.Read(buffer, 0, buffer.Length); + IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); + NEHeader Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader)); + Marshal.FreeHGlobal(hdrPtr); + return Header.signature == Signature; + } + } + + return false; + } + } +} diff --git a/libexeinfo/NE/Structs.cs b/libexeinfo/NE/Structs.cs index 46d9fd1..4df9307 100644 --- a/libexeinfo/NE/Structs.cs +++ b/libexeinfo/NE/Structs.cs @@ -32,7 +32,7 @@ namespace libexeinfo public partial class NE { [StructLayout(LayoutKind.Sequential/*, Pack = 2*/)] - public struct Header + public struct NEHeader { public ushort signature; public byte linker_major; diff --git a/libexeinfo/NE/Version.cs b/libexeinfo/NE/Version.cs index ce84ef3..5d41d95 100644 --- a/libexeinfo/NE/Version.cs +++ b/libexeinfo/NE/Version.cs @@ -34,6 +34,25 @@ namespace libexeinfo { public partial class NE { + public List GetVersions() + { + List versions = new List(); + + foreach (ResourceType type in Resources.types) + { + if ((type.id & 0x7FFF) == (int)ResourceTypes.RT_VERSION) + { + foreach (Resource resource in type.resources) + { + Version vers = new Version(resource.data, resource.name); + versions.Add(vers); + } + } + } + + return versions; + } + public class Version { public Dictionary> StringsByLanguage { get; } @@ -45,6 +64,7 @@ namespace libexeinfo VersionFileType fileType; VersionFileSubtype fileSubtype; DateTime fileDate; + string name; public string FileVersion { @@ -102,11 +122,18 @@ namespace libexeinfo } } - public Version(byte[] data) + public string Name + { + get { return name; } + } + + public Version(byte[] data, string resourceName = null) { if (data == null || data.Length < 5) return; + name = resourceName; + StringsByLanguage = new Dictionary>(); VersionNode root = GetNode(data, 0, out int rootLength); diff --git a/libexeinfo/libexeinfo.csproj b/libexeinfo/libexeinfo.csproj index e7507bd..cc3cb1e 100644 --- a/libexeinfo/libexeinfo.csproj +++ b/libexeinfo/libexeinfo.csproj @@ -50,6 +50,7 @@ +