From abd532981afe10241c0c56f8b2e89d79cad71342 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 7 Mar 2018 15:55:18 +0000 Subject: [PATCH] Add support for GEOS v1 and v2 executables (resource format is unknown). --- exeinfo/Program.cs | 74 +++++---- exeinfogui/MainForm.xeto.cs | 33 ++-- libexeinfo/Geos/Consts.cs | 46 ++++++ libexeinfo/Geos/Enums.cs | 187 +++++++++++++++++++++ libexeinfo/Geos/Geos.cs | 228 ++++++++++++++++++++++++++ libexeinfo/Geos/Info.cs | 110 +++++++++++++ libexeinfo/Geos/Structs.cs | 308 +++++++++++++++++++++++++++++++++++ libexeinfo/libexeinfo.csproj | 5 + 8 files changed, 940 insertions(+), 51 deletions(-) create mode 100644 libexeinfo/Geos/Consts.cs create mode 100644 libexeinfo/Geos/Enums.cs create mode 100644 libexeinfo/Geos/Geos.cs create mode 100644 libexeinfo/Geos/Info.cs create mode 100644 libexeinfo/Geos/Structs.cs diff --git a/exeinfo/Program.cs b/exeinfo/Program.cs index bcb5db3..f426e7b 100644 --- a/exeinfo/Program.cs +++ b/exeinfo/Program.cs @@ -52,6 +52,7 @@ namespace exeinfo IExecutable lxExe = new LX(args[0]); IExecutable coffExe = new COFF(args[0]); IExecutable peExe = new PE(args[0]); + IExecutable geosExe = new Geos(args[0]); if(neExe.Recognized) { @@ -71,8 +72,8 @@ namespace exeinfo 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)); + Console.WriteLine("\t\tFile flags: {0}", vers.FileFlags); + Console.WriteLine("\t\tFile OS: {0}", NE.Version.OsToString(vers.FileOS)); foreach(KeyValuePair> strByLang in vers.StringsByLanguage) { @@ -140,10 +141,10 @@ namespace exeinfo { recognized = true; Console.Write(mzExe.Information); - if(((MZ)mzExe).ResourceStream != null || ((MZ)mzExe).ResourceHeader.rsh_vrsn != 0 && - ((MZ)mzExe).ResourceHeader.rsh_vrsn != 1 && - ((MZ)mzExe).ResourceHeader.rsh_vrsn != 4 && - ((MZ)mzExe).ResourceHeader.rsh_vrsn != 5) + if(((MZ)mzExe).ResourceStream != null || ((MZ)mzExe).ResourceHeader.rsh_vrsn != 0 && + ((MZ)mzExe).ResourceHeader.rsh_vrsn != 1 && + ((MZ)mzExe).ResourceHeader.rsh_vrsn != 4 && + ((MZ)mzExe).ResourceHeader.rsh_vrsn != 5) PrintGemResources(((MZ)mzExe).ResourceHeader, ((MZ)mzExe).ResourceObjectRoots, ((MZ)mzExe).ResourceExtension, ((MZ)mzExe).GemColorIcons); @@ -158,10 +159,10 @@ namespace exeinfo { recognized = true; Console.Write(stExe.Information); - if(((AtariST)stExe).ResourceStream != null || ((AtariST)stExe).ResourceHeader.rsh_vrsn != 0 && - ((AtariST)stExe).ResourceHeader.rsh_vrsn != 1 && - ((AtariST)stExe).ResourceHeader.rsh_vrsn != 4 && - ((AtariST)stExe).ResourceHeader.rsh_vrsn != 5) + if(((AtariST)stExe).ResourceStream != null || ((AtariST)stExe).ResourceHeader.rsh_vrsn != 0 && + ((AtariST)stExe).ResourceHeader.rsh_vrsn != 1 && + ((AtariST)stExe).ResourceHeader.rsh_vrsn != 4 && + ((AtariST)stExe).ResourceHeader.rsh_vrsn != 5) PrintGemResources(((AtariST)stExe).ResourceHeader, ((AtariST)stExe).ResourceObjectRoots, ((AtariST)stExe).ResourceExtension, ((AtariST)stExe).GemColorIcons); @@ -184,12 +185,25 @@ namespace exeinfo } } + if(geosExe.Recognized) + { + recognized = true; + Console.Write(geosExe.Information); + + if(geosExe.Strings != null && geosExe.Strings.Any()) + { + Console.WriteLine("\tStrings:"); + foreach(string str in geosExe.Strings) Console.WriteLine("\t\t{0}", str); + } + } + if(!recognized) Console.WriteLine("Executable format not recognized"); } - static void PrintGemResources(GEM.MagiCResourceHeader resourceHeader, IReadOnlyList roots, - GEM.GemResourceExtension resourceExtension, - GEM.ColorIcon[] colorIcons) + static void PrintGemResources(GEM.MagiCResourceHeader resourceHeader, + IReadOnlyList roots, + GEM.GemResourceExtension resourceExtension, + GEM.ColorIcon[] colorIcons) { Console.WriteLine("\t\tGEM Resources:"); Console.WriteLine("\t\t\t{0} OBJECTs start at {1}", resourceHeader.rsh_nobs, resourceHeader.rsh_object); @@ -234,23 +248,19 @@ namespace exeinfo case GEM.ObjectTypes.G_IBOX: Console.WriteLine("{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode," + " coordinates ({8},{9}) size {10}x{11}", node.type, node.flags, node.state, - (GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12), - (GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8), - (GEM.ObjectColors)((node.data & 0xFFFF & GEM.InsideColorMask) >> 8), + (GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12), + (GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8), + (GEM.ObjectColors)((node.data & 0xFFFF & GEM.InsideColorMask) >> 8), (GEM.ObjectFillPattern)((node.data & 0xFFFF & GEM.FillPatternMask) >> 4), - (node.data & 0xFFFF & GEM.TransparentColor) != 0 - ? "transparent" - : "replace", + (node.data & 0xFFFF & GEM.TransparentColor) != 0 ? "transparent" : "replace", node.x, node.y, node.width, node.height); break; case GEM.ObjectTypes.G_BOXCHAR: sbyte thickness = (sbyte)((node.data & 0xFF0000) >> 16); if(thickness < 0) thickStr = $"{thickness * -1} pixels outward thickness"; - else if(thickness > 0) - thickStr = $"{thickness} pixels inward thickness"; - else - thickStr = "no thickness"; + else if(thickness > 0) thickStr = $"{thickness} pixels inward thickness"; + else thickStr = "no thickness"; char character = Claunia.Encoding.Encoding.AtariSTEncoding.GetString(new[] @@ -262,12 +272,10 @@ namespace exeinfo "{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode, {8}," + " '{9}' character, coordinates ({10},{11}) size {12}x{13}", node.type, node.flags, node.state, (GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12), - (GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8), - (GEM.ObjectColors)((node.data & 0xFFFF & GEM.InsideColorMask) >> 8), - (GEM.ObjectFillPattern)((node.data & 0xFFFF & GEM.FillPatternMask) >> 4), - (node.data & 0xFFFF & GEM.TransparentColor) != 0 - ? "transparent" - : "replace", + (GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8), + (GEM.ObjectColors)((node.data & 0xFFFF & GEM.InsideColorMask) >> 8), + (GEM.ObjectFillPattern)((node.data & 0xFFFF & GEM.FillPatternMask) >> 4), + (node.data & 0xFFFF & GEM.TransparentColor) != 0 ? "transparent" : "replace", thickStr, character, node.x, node.y, node.width, node.height); break; case GEM.ObjectTypes.G_BUTTON: @@ -283,11 +291,9 @@ namespace exeinfo if(node.TedInfo == null) goto default; if(node.TedInfo.Thickness < 0) - thickStr = $"{node.TedInfo.Thickness * -1} pixels outward thickness"; - else if(node.TedInfo.Thickness > 0) - thickStr = $"{node.TedInfo.Thickness} pixels inward thickness"; - else - thickStr = "no thickness"; + thickStr = $"{node.TedInfo.Thickness * -1} pixels outward thickness"; + else if(node.TedInfo.Thickness > 0) thickStr = $"{node.TedInfo.Thickness} pixels inward thickness"; + else thickStr = "no thickness"; Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, font {7}, {8}-justified," + " {9}, {10} border, {11} text, {12} interior, {13} fill, {14} mode," + " text: \"{15}\", validation: \"{16}\", template: \"{17}\"", node.type, node.flags, node.state, node.x, node.y, node.width, node.height, diff --git a/exeinfogui/MainForm.xeto.cs b/exeinfogui/MainForm.xeto.cs index 09f9cc9..8e9bcbe 100644 --- a/exeinfogui/MainForm.xeto.cs +++ b/exeinfogui/MainForm.xeto.cs @@ -40,23 +40,23 @@ namespace exeinfogui Label lblSubsystem; TabGemResources tabGemResources; TabControl tabMain; + TabNeResources tabNeResources; + TabPageSegments tabSegments; TabPageStrings tabStrings; TextBox txtFile; TextArea txtInformation; TextBox txtOs; TextBox txtSubsystem; TextBox txtType; - TabPageSegments tabSegments; - TabNeResources tabNeResources; public MainForm() { XamlReader.Load(this); - tabSegments = new TabPageSegments {Visible = false}; + tabSegments = new TabPageSegments {Visible = false}; tabStrings = new TabPageStrings {Visible = false}; tabGemResources = new TabGemResources {Visible = false}; - tabNeResources = new TabNeResources {Visible = false}; + tabNeResources = new TabNeResources {Visible = false}; tabMain.Pages.Add(tabSegments); tabMain.Pages.Add(tabStrings); tabMain.Pages.Add(tabGemResources); @@ -78,11 +78,11 @@ namespace exeinfogui if(dlgOpen.ShowDialog(this) != DialogResult.Ok) return; - txtFile.Text = dlgOpen.FileName; + txtFile.Text = dlgOpen.FileName; txtInformation.Text = ""; - txtOs.Text = ""; - txtSubsystem.Text = ""; - txtType.Text = ""; + txtOs.Text = ""; + txtSubsystem.Text = ""; + txtType.Text = ""; IExecutable mzExe = new MZ(dlgOpen.FileName); IExecutable neExe = new libexeinfo.NE(dlgOpen.FileName); @@ -90,6 +90,7 @@ namespace exeinfogui IExecutable lxExe = new LX(dlgOpen.FileName); IExecutable coffExe = new COFF(dlgOpen.FileName); IExecutable peExe = new PE(dlgOpen.FileName); + IExecutable geosExe = new Geos(dlgOpen.FileName); IExecutable recognizedExe = null; if(mzExe.Recognized) @@ -107,14 +108,13 @@ namespace exeinfogui recognizedExe = neExe; if(((libexeinfo.NE)neExe).Resources.types != null && ((libexeinfo.NE)neExe).Resources.types.Any()) { - tabNeResources.Update(((libexeinfo.NE)neExe).Resources.types, ((libexeinfo.NE)neExe).Header.target_os); + tabNeResources.Update(((libexeinfo.NE)neExe).Resources.types, + ((libexeinfo.NE)neExe).Header.target_os); tabNeResources.Visible = true; } } - else if(lxExe.Recognized) - recognizedExe = lxExe; - else if(peExe.Recognized) - recognizedExe = peExe; + else if(lxExe.Recognized) recognizedExe = lxExe; + else if(peExe.Recognized) recognizedExe = peExe; else if(stExe.Recognized) { recognizedExe = stExe; @@ -124,10 +124,9 @@ namespace exeinfogui tabGemResources.Visible = true; } } - else if(coffExe.Recognized) - recognizedExe = coffExe; - else - txtType.Text = "Format not recognized"; + else if(coffExe.Recognized) recognizedExe = coffExe; + else if(geosExe.Recognized) recognizedExe = geosExe; + else txtType.Text = "Format not recognized"; if(recognizedExe == null) return; diff --git a/libexeinfo/Geos/Consts.cs b/libexeinfo/Geos/Consts.cs new file mode 100644 index 0000000..9368509 --- /dev/null +++ b/libexeinfo/Geos/Consts.cs @@ -0,0 +1,46 @@ +// +// Consts.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.namespace libexeinfo.Geos + +namespace libexeinfo +{ + public partial class Geos + { + /// GEOS file identification "magic" + const uint GEOS_ID = 0x53CF45C7; + /// GEOS2 file identification "magic" + const uint GEOS2_ID = 0x53C145C7; + + const int GEOS_TOKENLEN = 4; + /// Length of filename + const int GEOS_LONGNAME = 36; + /// Length of user file info + const int GEOS_INFO = 100; + /// Length of internal filename + const int GEOS_FNAME = 8; + /// Length of internal extension + const int GEOS_FEXT = 4; + } +} \ No newline at end of file diff --git a/libexeinfo/Geos/Enums.cs b/libexeinfo/Geos/Enums.cs new file mode 100644 index 0000000..7bbddba --- /dev/null +++ b/libexeinfo/Geos/Enums.cs @@ -0,0 +1,187 @@ +// +// Enums.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.namespace libexeinfo.Geos + +using System; + +namespace libexeinfo +{ + public partial class Geos + { + [Flags] + enum Attributes : ushort + { + GA_PROCESS = 0x8000, + GA_LIBRARY = 0x4000, + GA_DRIVER = 0x2000, + GA_KEEP_FILE_OPEN = 0x1000, + GA_SYSTEM = 0x0800, + GA_MULTI_LAUNCHABLE = 0x0400, + GA_APPLICATION = 0x0200, + GA_DRIVER_INITIALIZED = 0x0100, + GA_LIBRARY_INITIALIZED = 0x0080, + GA_GEODE_INITIALIZED = 0x0040, + GA_USES_COPROC = 0x0020, + GA_REQUIRES_COPROC = 0x0010, + GA_HAS_GENERAL_CONSUMER_MODE = 0x0008, + GA_ENTRY_POINTS_IN_C = 0x0004 + } + + enum FileType2 : ushort + { + /// + /// The file is not a GEOS file. + /// + GFT_NOT_GEOS_FILE = 0, + /// + /// The file is executable. + /// + GFT_EXECUTABLE = 1, + /// + /// The file is a VM file. + /// + GFT_VM = 2, + /// + /// The file is a GEOS byte file. + /// + GFT_DATA = 3, + /// + /// The file is a GEOS directory. + /// + GFT_DIRECTORY = 4, + /// + /// The file is a symbolic link. + /// + GFT_LINK = 5 + } + + enum ApplicationType : ushort + { + Application = 1, + Library = 2, + Driver = 3 + } + + enum FileType : ushort + { + /// + /// The file is executable. + /// + GFT_EXECUTABLE = 0, + /// + /// The file is a VM file. + /// + GFT_VM = 1 + } + + // These are defined in GEOS SDK but itself says they stopped using numerical IDs and should use ASCII ones. + enum ManufacturerId : ushort + { + GeoWorks = 0, + App = 1, + Palm = 2, + Wizard = 3, + CreativeLabs = 4, + DosLauncher = 5, + AmericaOnline = 6, + Intuit = 7, + Sdk = 8, + Agd = 9, + Generic = 10, + Tbd = 11, + Socket = 12 + } + + [Flags] + enum SegmentFlags : ushort + { + /// + /// The block will not move from its place in the global heap until it is freed. + /// + HF_FIXED = 0x80, + /// + /// The block may be locked by threads belonging to geodes other than the block's owner. + /// + HF_SHARABLE = 0x40, + /// + /// The block may be discarded when unlocked. + /// + HF_DISCARDABLE = 0x20, + /// + /// The block may be swapped to extended/expanded memory or to the disk swap space when it is unlocked. + /// + HF_SWAPABLE = 0x10, + /// + /// The block contains a local memory heap. + /// + HF_LMEM = 0x08, + /// + /// The memory manager turns this bit on when it discards a block. + /// + HF_DISCARDED = 0x02, + /// + /// The memory manager turns this bit on when it swaps a block to extended/expanded memory or to the disk swap space. + /// + HF_SWAPPED = 0x01, + HF_STATIC = HF_DISCARDABLE | HF_SWAPABLE, + HF_DYNAMIC = HF_SWAPABLE, + /// + /// The memory manager should initialize the block to null bytes. + /// + HAF_ZERO_INIT = 0x8000, + /// + /// The memory manager should lock the block after allocating it. + /// + HAF_LOCK = 0x4000, + /// + /// The memory manager should not return errors. If it cannot allocate block, GEOS will tell the user that there is no + /// memory available and crash. + /// + HAF_NO_ERR = 0x2000, + /// + /// If both and are set, this block will be run by the + /// application's UI thread. + /// + HAF_UI = 0x1000, + /// + /// The block's data will not be modified. + /// + HAF_READ_ONLY = 0x0800, + /// + /// This block will be an object block. + /// + HAF_OBJECT_RESOURCE = 0x0400, + /// + /// This block contains executable code. + /// + HAF_CODE = 0x0200, + /// + /// If the block contains code, the code may be run by a less privileged entity. If the block contains data, the data + /// may be accessed or altered by a less privileged entity. + /// + HAF_CONFORMING = 0x0100 + } + } +} \ No newline at end of file diff --git a/libexeinfo/Geos/Geos.cs b/libexeinfo/Geos/Geos.cs new file mode 100644 index 0000000..bac3052 --- /dev/null +++ b/libexeinfo/Geos/Geos.cs @@ -0,0 +1,228 @@ +// +// Geos.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.namespace libexeinfo.Geos + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace libexeinfo +{ + public partial class Geos : IExecutable + { + ApplicationHeader applicationHeader; + ApplicationHeaderV2 applicationHeader2; + Export[] exports; + GeodeHeader header; + GeodeHeaderV2 header2; + Import[] imports; + bool isNewHeader; + SegmentDescriptor[] segments; + + /// + /// Initializes a new instance of the class. + /// + /// Executable path. + public Geos(string path) + { + BaseStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Stream containing the executable. + public Geos(Stream stream) + { + BaseStream = stream; + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Byte array containing the executable. + public Geos(byte[] data) + { + BaseStream = new MemoryStream(data); + Initialize(); + } + + public bool Recognized { get; private set; } + public string Type => + isNewHeader ? "GEOS executable v2" : "GEOS executable"; + public Stream BaseStream { get; } + public bool IsBigEndian => false; + public IEnumerable Architectures => new[] {Architecture.I86}; + public OperatingSystem RequiredOperatingSystem { get; private set; } + public IEnumerable Strings { get; private set; } + public IEnumerable Segments { get; private set; } + + // TODO: GEOS character set + void Initialize() + { + Recognized = false; + if(BaseStream == null) return; + + BaseStream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(GeodeHeaderV2))]; + BaseStream.Read(buffer, 0, buffer.Length); + header = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + header2 = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + + Recognized = header.magic == GEOS_ID && header.type == FileType.GFT_EXECUTABLE || + header2.magic == GEOS2_ID && header2.type == FileType2.GFT_EXECUTABLE; + + if(!Recognized) return; + + isNewHeader = header2.magic == GEOS2_ID; + RequiredOperatingSystem = new OperatingSystem {Name = "GEOS", MajorVersion = isNewHeader ? 2 : 1}; + + List strings = new List + { + StringHandlers.CToString(isNewHeader ? header2.name : header.name), + StringHandlers.CToString(isNewHeader ? header2.copyright : header.copyright), + StringHandlers.CToString(isNewHeader ? header2.info : header.info) + }; + + uint segmentBase = 0; + + if(isNewHeader) + { + BaseStream.Position = Marshal.SizeOf(typeof(GeodeHeaderV2)); + buffer = new byte[Marshal.SizeOf(typeof(ApplicationHeaderV2))]; + segmentBase = (uint)Marshal.SizeOf(typeof(GeodeHeaderV2)); + BaseStream.Read(buffer, 0, buffer.Length); + applicationHeader2 = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + imports = new Import[applicationHeader2.imports]; + exports = new Export[applicationHeader2.exports]; + segments = new SegmentDescriptor[applicationHeader2.segments]; + strings.Add($"{StringHandlers.CToString(applicationHeader2.name).Trim()}.{StringHandlers.CToString(applicationHeader2.extension).Trim()}"); + } + else + { + BaseStream.Position = Marshal.SizeOf(typeof(GeodeHeader)); + buffer = new byte[Marshal.SizeOf(typeof(ApplicationHeader))]; + BaseStream.Read(buffer, 0, buffer.Length); + applicationHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + imports = new Import[applicationHeader.imports]; + exports = new Export[applicationHeader.exports]; + segments = new SegmentDescriptor[applicationHeader.segments]; + strings.Add($"{StringHandlers.CToString(applicationHeader.name).Trim()}.{StringHandlers.CToString(applicationHeader.extension).Trim()}"); + } + + buffer = new byte[Marshal.SizeOf(typeof(Import))]; + for(int i = 0; i < imports.Length; i++) + { + BaseStream.Read(buffer, 0, buffer.Length); + imports[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + strings.Add(StringHandlers.CToString(imports[i].name).Trim()); + } + + buffer = new byte[Marshal.SizeOf(typeof(Export))]; + for(int i = 0; i < exports.Length; i++) + { + BaseStream.Read(buffer, 0, buffer.Length); + exports[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + } + + if(segments.Length > 0) + { + buffer = new byte[Marshal.SizeOf(typeof(SegmentDescriptor)) * segments.Length]; + BaseStream.Read(buffer, 0, buffer.Length); + Segment[] mySegments = new Segment[segments.Length]; + + for(int i = 0; i < segments.Length; i++) + { + segments[i].length = BitConverter.ToUInt16(buffer, 2 * i); + segments[i].offset = + BitConverter.ToUInt32(buffer, 2 * segments.Length + 4 * i) + segmentBase; + segments[i].relocs_length = BitConverter.ToUInt16(buffer, 6 * segments.Length + 2 * i); + segments[i].flags = + (SegmentFlags)BitConverter.ToUInt16(buffer, 8 * segments.Length + 2 * i); + + mySegments[i] = new Segment + { + Flags = $"{segments[i].flags}", + Offset = segments[i].offset, + Size = segments[i].length + }; + + if(i == 1) mySegments[i].Name = ".idata"; + else if(segments[i].flags.HasFlag(SegmentFlags.HAF_CODE)) mySegments[i].Name = ".text"; + else if(segments[i].flags.HasFlag(SegmentFlags.HAF_OBJECT_RESOURCE)) mySegments[i].Name = ".rsrc"; + else if(segments[i].flags.HasFlag(SegmentFlags.HAF_ZERO_INIT)) mySegments[i].Name = ".bss"; + else if(segments[i].flags.HasFlag(SegmentFlags.HAF_READ_ONLY)) mySegments[i].Name = ".rodata"; + else mySegments[i].Name = ".data"; + } + + Segments = mySegments; + } + + strings.Remove(""); + strings.Remove(null); + Strings = strings; + } + + /// + /// Identifies if the specified executable is a Microsoft/IBM Linear EXecutable + /// + /// true if the specified executable is a Microsoft/IBM Linear EXecutable, false otherwise. + /// Executable path. + public static bool Identify(string path) + { + FileStream baseStream = File.Open(path, FileMode.Open, FileAccess.Read); + + baseStream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(GeodeHeaderV2))]; + baseStream.Read(buffer, 0, buffer.Length); + GeodeHeader header = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + GeodeHeaderV2 header2 = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + + return header.magic == GEOS_ID && header.type == FileType.GFT_EXECUTABLE || + header2.magic == GEOS2_ID && header2.type == FileType2.GFT_EXECUTABLE; + } + + /// + /// Identifies if the specified executable is a Microsoft/IBM Linear EXecutable + /// + /// true if the specified executable is a Microsoft/IBM Linear EXecutable, false otherwise. + /// Stream containing the executable. + public static bool Identify(FileStream stream) + { + FileStream baseStream = stream; + baseStream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[Marshal.SizeOf(typeof(GeodeHeaderV2))]; + baseStream.Read(buffer, 0, buffer.Length); + GeodeHeader header = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + GeodeHeaderV2 header2 = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + + return header.magic == GEOS_ID && header.type == FileType.GFT_EXECUTABLE || + header2.magic == GEOS2_ID && header2.type == FileType2.GFT_EXECUTABLE; + } + } +} \ No newline at end of file diff --git a/libexeinfo/Geos/Info.cs b/libexeinfo/Geos/Info.cs new file mode 100644 index 0000000..c234fe6 --- /dev/null +++ b/libexeinfo/Geos/Info.cs @@ -0,0 +1,110 @@ +// +// Info.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.namespace libexeinfo.Geos + +using System.Text; + +namespace libexeinfo +{ + public partial class Geos + { + public string Information + { + get + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("GEOS executable{0}", isNewHeader ? " v2" : "").AppendLine(); + + sb.AppendFormat("\tClass: {0}", isNewHeader ? $"{header2.type}" : $"{header.type}").AppendLine(); + sb.AppendFormat("\tType: {0}", isNewHeader ? applicationHeader2.type : applicationHeader.type) + .AppendLine(); + sb.AppendFormat("\tAttributes: {0}", + isNewHeader ? applicationHeader2.attributes : applicationHeader.attributes) + .AppendLine(); + sb.AppendFormat("\tName: {0}", StringHandlers.CToString(isNewHeader ? header2.name : header.name)) + .AppendLine(); + sb.AppendFormat("\tInternal name: \"{0}.{1}\"", + isNewHeader + ? StringHandlers.CToString(applicationHeader2.name).Trim() + : StringHandlers.CToString(applicationHeader.name).Trim(), + isNewHeader + ? StringHandlers.CToString(applicationHeader2.extension).Trim() + : StringHandlers.CToString(applicationHeader.extension).Trim()).AppendLine(); + sb.AppendFormat("\tVersion: {0}", + isNewHeader + ? $"{header2.release.major}.{header2.release.minor}.{header2.release.change}.{header2.release.engineering}" + : $"{header.release.major}.{header.release.minor}.{header.release.change}.{header.release.engineering}") + .AppendLine(); + sb.AppendFormat("\tCopyright string: {0}", + StringHandlers.CToString(isNewHeader ? header2.copyright : header.copyright)) + .AppendLine(); + sb.AppendFormat("\tInformational string: {0}", + StringHandlers.CToString(isNewHeader ? header2.info : header.info)).AppendLine(); + sb.AppendFormat("\tProtocol: {0}", + isNewHeader + ? $"{header2.protocol.major}.{header2.protocol.minor}" + : $"{header.protocol.major}.{header.protocol.minor}").AppendLine(); + sb.AppendFormat("\tApplication token: \"{0}\" id {1}", + isNewHeader + ? StringHandlers.CToString(header2.application.str) + : StringHandlers.CToString(header.creator.str), + isNewHeader ? header2.application.manufacturer : header.creator.manufacturer) + .AppendLine(); + sb.AppendFormat("\tToken: \"{0}\" id {1}", + isNewHeader + ? StringHandlers.CToString(header2.token.str) + : StringHandlers.CToString(header.token.str), + isNewHeader ? header2.token.manufacturer : header.token.manufacturer).AppendLine(); + + sb.AppendFormat("\tSegments: {0}", + isNewHeader ? applicationHeader2.segments : applicationHeader.segments).AppendLine(); + sb.AppendFormat("\tImported libraries: {0}", + isNewHeader ? applicationHeader2.imports : applicationHeader.imports).AppendLine(); + sb.AppendFormat("\tExported entry points: {0}", + isNewHeader ? applicationHeader2.exports : applicationHeader.exports).AppendLine(); + + sb.AppendFormat("\t{0} imports:", imports.Length).AppendLine(); + for(int i = 0; i < imports.Length; i++) + sb.AppendFormat("\t\tImport \"{0}\", attributes {1}, protocol {2}.{3}", + StringHandlers.CToString(imports[i].name).Trim(), imports[i].attributes, + imports[i].protocol.major, imports[i].protocol.minor).AppendLine(); + + sb.AppendFormat("\t{0} exports:", exports.Length).AppendLine(); + for(int i = 0; i < exports.Length; i++) + sb.AppendFormat("\t\tExported entry point in segment {0} offset {1}", exports[i].segment, + exports[i].offset).AppendLine(); + + sb.AppendFormat("\t{0} segments:", segments.Length).AppendLine(); + for(int i = 0; i < segments.Length; i++) + sb + .AppendFormat("\t\tSegment {0} starts at {1} runs for {2} bytes with flags {3} has {4} relocations", + i, segments[i].offset, segments[i].length, segments[i].flags, + segments[i].relocs_length / 4).AppendLine(); + + return sb.ToString(); + } + } + } +} \ No newline at end of file diff --git a/libexeinfo/Geos/Structs.cs b/libexeinfo/Geos/Structs.cs new file mode 100644 index 0000000..fa163bf --- /dev/null +++ b/libexeinfo/Geos/Structs.cs @@ -0,0 +1,308 @@ +// +// Structs.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.namespace libexeinfo.Geos + +using System.Runtime.InteropServices; + +namespace libexeinfo +{ + // Thanks to Marcus Gröber for structures + // TODO: Format of resource segments + public partial class Geos + { + /// ID for file types/icons + [StructLayout(LayoutKind.Sequential)] + struct Token + { + /// 4 byte string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_TOKENLEN)] + public byte[] str; + /// Manufacturer ID + public ManufacturerId manufacturer; + } + + /// Protocol/version number + [StructLayout(LayoutKind.Sequential)] + struct Protocol + { + /// Protocol + public ushort major; + /// Sub revision + public ushort minor; + } + + [StructLayout(LayoutKind.Sequential)] + struct Release + { + public ushort major; + public ushort minor; + public ushort change; + public ushort engineering; + } + + // Followed by followed by geosliblist, followed by exportlist, followed by segmentlist + [StructLayout(LayoutKind.Sequential)] + struct GeodeHeader + { + /// GEOS id magic: C7 45 CF 53 + public uint magic; + /// + /// + /// + public FileType type; + /// Flags ??? (always seen 0000h) + public ushort flags; + /// Release + public Release release; + /// Protocol/version + public Protocol protocol; + /// File type/icon + public Token token; + /// Tokenof creator application + public Token creator; + /// Long filename + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_LONGNAME)] + public byte[] name; + /// User file info + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_INFO)] + public byte[] info; + /// Copyright notice + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] copyright; + } + + /// Additional geode file header + [StructLayout(LayoutKind.Sequential)] + struct ApplicationHeader + { + /// This is actually not part of the app header, but for historical reasons, I keep it in... :-) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] unknown; + /// Attributes + public Attributes _attr; + /// Application type + public ApplicationType _type; + /// Expected kernel protocol + public Protocol kernel_protocol; + /// Number of program segments + public ushort _numseg; + /// Number of included libraries + public ushort _numlib; + /// Number of exported locations + public ushort _numexp; + /// Default stack size + public ushort stack_size; + /// If application: segment/offset of ??? + public ushort app_off; + public ushort app_seg; + /// If application: item of resource with application token + public ushort tokenres_item; + /// If application: segment of resource with application token + public ushort tokenres_segment; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown2; + /// Attributes + public Attributes attributes; + /// Application type + public ApplicationType type; + /// Release + public Release release; + /// Protocol/version + public Protocol protocol; + /// Possibly header checksum (???) + public ushort crc; + /// Internal filename (blank padded) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_FNAME)] + public byte[] name; + /// Internal extension (blank padded) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_FEXT)] + public byte[] extension; + /// File type/icon + Token token; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown3; + /// If driver: entry location + public ushort entry_off; + public ushort entry_seg; + /// If library: init location (?) + public ushort init_off; + public ushort init_seg; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown4; + /// Number of exported locations + public ushort exports; + /// Number of included libraries + public ushort imports; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown5; + /// Number of program segments + public ushort segments; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] unknown6; + } + + /// Base type of "exported" array + [StructLayout(LayoutKind.Sequential)] + struct Export + { + /// Routine entry location + public ushort offset; + /// Routine entry location + public ushort segment; + } + + /// Base type of library array + [StructLayout(LayoutKind.Sequential)] + struct Import + { + /// library name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_FNAME)] + public byte[] name; + /// library type + public Attributes attributes; + /// required lib protocol/version + public Protocol protocol; + } + + /// GEOS2 standard header + [StructLayout(LayoutKind.Sequential)] + struct GeodeHeaderV2 + { + /// GEOS2 id magic: C7 45 CF 53 + public uint magic; + /// Long filename + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_LONGNAME)] + public byte[] name; + /// + /// + /// + public FileType2 type; + /// Flags + public ushort flags; + /// Release + public Release release; + /// Protocol/version + public Protocol protocol; + /// File type/icon + public Token token; + /// Token of creator application + public Token application; + /// User file info + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_INFO)] + public byte[] info; + /// Copyright notice + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] copyright; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] unknown; + /// Creation date in DOS format + public byte create_date; + /// Creation time in DOS format + public byte create_time; + /// Password, encrypted as hex string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] password; + /// not yet decoded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 44)] + public byte[] unknown2; + } + + /// Additional geode file header + [StructLayout(LayoutKind.Sequential)] + struct ApplicationHeaderV2 + { + /// Attributes + public Attributes _attr; + /// Application type + public ApplicationType _type; + /// Expected kernel protocol + public Protocol kernel_protocol; + /// Number of program segments + public ushort _numseg; + /// Number of included libraries + public ushort _numlib; + /// Number of exported locations + public ushort _numexp; + /// Default stack size + public ushort stack_size; + /// If application: segment/offset of ??? + public ushort app_off; + public ushort app_seg; + /// If application: item of resource with application token + public ushort tokenres_item; + /// If application: segment of resource with application token + public ushort tokenres_segment; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown; + /// Attributes + public Attributes attributes; + /// Application type + public ApplicationType type; + /// Release" + public Release release; + /// Protocol/version + public Protocol protocol; + /// Possibly header checksum (???) + public ushort crc; + /// Internal filename (blank padded) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_FNAME)] + public byte[] name; + /// Internal extension (blank padded) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = GEOS_FEXT)] + public byte[] extension; + /// File type/icon + public Token token; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown2; + /// If driver: entry location + public ushort entry_off; + public ushort entry_seg; + /// If library: init location (?) + public ushort init_off; + public ushort init_seg; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown3; + /// Number of exported locations + public ushort exports; + /// Number of included libraries + public ushort imports; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] unknown4; + /// Number of program segments + public ushort segments; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] unknown5; + } + + [StructLayout(LayoutKind.Sequential)] + struct SegmentDescriptor + { + public ushort length; + public uint offset; + public ushort relocs_length; + public SegmentFlags flags; + } + } +} \ No newline at end of file diff --git a/libexeinfo/libexeinfo.csproj b/libexeinfo/libexeinfo.csproj index c2f3912..3d94e79 100644 --- a/libexeinfo/libexeinfo.csproj +++ b/libexeinfo/libexeinfo.csproj @@ -55,6 +55,11 @@ + + + + +