From 27a7bbf5d181b935c35880125e0910c470f60892 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Fri, 2 Mar 2018 22:49:19 +0000 Subject: [PATCH] Add support for NE OS/2 resource table format and resource types. --- libexeinfo/NE/Consts.cs | 44 ++++++++++-- libexeinfo/NE/Enums.cs | 149 +++++++++++++++++++++++++++------------ libexeinfo/NE/NE.cs | 87 ++++++++++++++++++++--- libexeinfo/NE/Structs.cs | 11 ++- 4 files changed, 228 insertions(+), 63 deletions(-) diff --git a/libexeinfo/NE/Consts.cs b/libexeinfo/NE/Consts.cs index 3a13055..7aada08 100644 --- a/libexeinfo/NE/Consts.cs +++ b/libexeinfo/NE/Consts.cs @@ -41,6 +41,12 @@ namespace libexeinfo /// const string STRING_FILE_INFO = "StringFileInfo"; + const ushort SEGMENT_TYPE_MASK = 0x07; + const ushort SEGMENT_FLAGS_MASK = 0x3F8; + const ushort SEGMENT_DISCARD_MASK = 0xF000; + const ushort SEGMENT_IOPRVL_MASK = 0xC00; + const ushort KNOWN_RSRC_FLAGS = 0x1070; + /// /// Gets the name of a resource type according to its identifier /// @@ -80,10 +86,38 @@ namespace libexeinfo } } - const ushort SEGMENT_TYPE_MASK = 0x07; - const ushort SEGMENT_FLAGS_MASK = 0x3F8; - const ushort SEGMENT_DISCARD_MASK = 0xF000; - const ushort SEGMENT_IOPRVL_MASK = 0xC00; - const ushort KNOWN_RSRC_FLAGS = 0x1070; + /// + /// Gets the name of a resource type according to its identifier + /// + /// The resource type name. + /// Resource type identifier. + public static string ResourceIdToNameOs2(ushort id) + { + switch(id) + { + case (int)Os2ResourceTypes.RT_POINTER: return "RT_POINTER"; + case (int)Os2ResourceTypes.RT_BITMAP: return "RT_BITMAP"; + case (int)Os2ResourceTypes.RT_MENU: return "RT_MENU"; + case (int)Os2ResourceTypes.RT_DIALOG: return "RT_DIALOG"; + case (int)Os2ResourceTypes.RT_STRING: return "RT_STRING"; + case (int)Os2ResourceTypes.RT_FONTDIR: return "RT_FONTDIR"; + case (int)Os2ResourceTypes.RT_FONT: return "RT_FONT"; + case (int)Os2ResourceTypes.RT_ACCELTABLE: return "RT_ACCELTABLE"; + case (int)Os2ResourceTypes.RT_RCDATA: return "RT_RCDATA"; + case (int)Os2ResourceTypes.RT_MESSAGE: return "RT_MESSAGE"; + case (int)Os2ResourceTypes.RT_DLGINCLUDE: return "RT_DLGINCLUDE"; + case (int)Os2ResourceTypes.RT_VKEYTBL: return "RT_VKEYTBL"; + case (int)Os2ResourceTypes.RT_KEYTBL: return "RT_KEYTBL"; + case (int)Os2ResourceTypes.RT_CHARTBL: return "RT_CHARTBL"; + case (int)Os2ResourceTypes.RT_DISPLAYINFO: return "RT_DISPLAYINFO"; + case (int)Os2ResourceTypes.RT_FKASHORT: return "RT_FKASHORT"; + case (int)Os2ResourceTypes.RT_FKALONG: return "RT_FKALONG"; + case (int)Os2ResourceTypes.RT_HELPTABLE: return "RT_HELPTABLE"; + case (int)Os2ResourceTypes.RT_HELPSUBTABLE: return "RT_HELPSUBTABLE"; + case (int)Os2ResourceTypes.RT_FDDIR: return "RT_FDDIR"; + case (int)Os2ResourceTypes.RT_FD: return "RT_FD"; + default: return $"{id}"; + } + } } } \ No newline at end of file diff --git a/libexeinfo/NE/Enums.cs b/libexeinfo/NE/Enums.cs index 412d9b5..5a98e2c 100644 --- a/libexeinfo/NE/Enums.cs +++ b/libexeinfo/NE/Enums.cs @@ -55,6 +55,55 @@ namespace libexeinfo GangloadArea = 1 << 3 } + /// + /// Resource types. + /// + public enum Os2ResourceTypes : ushort + { + /// mouse pointer shape + RT_POINTER = 1, + /// bitmap + RT_BITMAP = 2, + /// menu template + RT_MENU = 3, + /// dialog template + RT_DIALOG = 4, + /// string tables + RT_STRING = 5, + /// font directory + RT_FONTDIR = 6, + /// font + RT_FONT = 7, + /// accelerator tables + RT_ACCELTABLE = 8, + /// binary data + RT_RCDATA = 9, + /// error msg tables + RT_MESSAGE = 10, + /// dialog include file name + RT_DLGINCLUDE = 11, + /// key to vkey tables + RT_VKEYTBL = 12, + /// key to UGL tables + RT_KEYTBL = 13, + /// glyph to character tables + RT_CHARTBL = 14, + /// screen display information + RT_DISPLAYINFO = 15, + /// function key area short form + RT_FKASHORT = 16, + /// function key area long form + RT_FKALONG = 17, + /// Help table for IPFC + RT_HELPTABLE = 18, + /// Help subtable for IPFC + RT_HELPSUBTABLE = 19, + /// DBCS uniq/font driver directory + RT_FDDIR = 20, + /// DBCS uniq/font driver + RT_FD = 21 + } + /// /// Program flags. /// @@ -78,9 +127,9 @@ namespace libexeinfo [Flags] public enum ResourceFlags : ushort { - Moveable = 0x10, - Pure = 0x20, - Preload = 0x40, + Moveable = 0x10, + Pure = 0x20, + Preload = 0x40, Discardable = 0x1000 } @@ -118,6 +167,57 @@ namespace libexeinfo RT_NEW = 0x2000 } + [Flags] + public enum SegmentFlags : ushort + { + /// + /// Segment data is iterated + /// + Iterated = 0x08, + /// + /// Segment is not fixed + /// + Moveable = 0x10, + /// + /// Segment can be shared + /// + Shared = 0x20, + /// + /// Segment will be preloaded; read-only if this is a data segment + /// + Preload = 0x40, + /// + /// Code segment is execute only; data segment is read-only + /// + Eronly = 0x80, + /// + /// Segment has relocation records + /// + Relocinfo = 0x100, + /// + /// Segment is conforming + /// + Conform = 0x200, + /// + /// Discardable + /// + Discardable = 0x1000, + /// + /// 32-bit code segment + /// + Code32 = 0x2000, + /// + /// Length of segment and minimum allocation size are in units of segment sector size + /// + Huge = 0x4000 + } + + public enum SegmentType : ushort + { + Code = 0, + Data = 1 + } + /// /// Target operating system. /// @@ -217,48 +317,5 @@ namespace libexeinfo VFT_UNKNOWN = 0x00000000, VFT_VXD = 0x00000005 } - - [Flags] - public enum SegmentFlags : ushort - { - /// - /// Segment data is iterated - /// - Iterated = 0x08, - /// - /// Segment is not fixed - /// - Moveable = 0x10, - /// - /// Segment can be shared - /// - Shared = 0x20, - /// - /// Segment will be preloaded; read-only if this is a data segment - /// - Preload = 0x40, - /// - /// Code segment is execute only; data segment is read-only - /// - Eronly = 0x80, - /// - /// Segment has relocation records - /// - Relocinfo = 0x100, - /// - /// Segment is conforming - /// - Conform = 0x200, - /// - /// Length of segment and minimum allocation size are in units of segment sector size - /// - Huge = 0x4000 - } - - public enum SegmentType : ushort - { - Code = 0, - Data = 1 - } } } \ No newline at end of file diff --git a/libexeinfo/NE/NE.cs b/libexeinfo/NE/NE.cs index 513cf31..08ceae7 100644 --- a/libexeinfo/NE/NE.cs +++ b/libexeinfo/NE/NE.cs @@ -216,19 +216,86 @@ namespace libexeinfo if(Header.imported_names_offset >= Header.resource_table_offset && Header.imported_names_offset <= resourceUpperLimit) resourceUpperLimit = Header.imported_names_offset; - if(Header.resource_table_offset < resourceUpperLimit && Header.resource_table_offset != 0 && - (Header.target_os == TargetOS.Windows || Header.target_os == TargetOS.Win32)) - { - Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset, - resourceUpperLimit); + if(Header.resource_table_offset < resourceUpperLimit && Header.resource_table_offset != 0) + if(Header.target_os == TargetOS.Windows || Header.target_os == TargetOS.Win32) + { + Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset, + resourceUpperLimit); - for(int t = 0; t < Resources.types.Length; t++) - Resources.types[t].resources = Resources.types[t].resources.OrderBy(r => r.name).ToArray(); + for(int t = 0; t < Resources.types.Length; t++) + Resources.types[t].resources = Resources.types[t].resources.OrderBy(r => r.name).ToArray(); - Resources.types = Resources.types.OrderBy(t => t.name).ToArray(); + Resources.types = Resources.types.OrderBy(t => t.name).ToArray(); - Versions = GetVersions().ToArray(); - } + Versions = GetVersions().ToArray(); + } + else if(Header.target_os == TargetOS.OS2 && segments != null && Header.resource_entries > 0) + { + BaseStream.Position = BaseExecutable.Header.new_offset + Header.resource_table_offset; + buffer = new byte[Header.resource_entries * 4]; + BaseStream.Read(buffer, 0, buffer.Length); + Os2ResourceTableEntry[] entries = new Os2ResourceTableEntry[Header.resource_entries]; + for(int i = 0; i < entries.Length; i++) + { + entries[i].etype = BitConverter.ToUInt16(buffer, i * 4 + 0); + entries[i].ename = BitConverter.ToUInt16(buffer, i * 4 + 2); + } + + SegmentEntry[] resourceSegments = new SegmentEntry[Header.resource_entries]; + Array.Copy(segments, Header.segment_count - Header.resource_entries, resourceSegments, 0, + Header.resource_entries); + SegmentEntry[] realSegments = new SegmentEntry[Header.segment_count - Header.resource_entries]; + Array.Copy(segments, 0, realSegments, 0, realSegments.Length); + segments = realSegments; + + SortedDictionary> os2resources = + new SortedDictionary>(); + + for(int i = 0; i < entries.Length; i++) + { + os2resources.TryGetValue(entries[i].etype, out List thisResourceType); + + if(thisResourceType == null) thisResourceType = new List(); + + Resource thisResource = new Resource + { + id = entries[i].ename, + name = $"{entries[i].ename}", + flags = (ResourceFlags)resourceSegments[i].dwFlags, + dataOffset = (uint)(resourceSegments[i].dwLogicalSectorOffset * 16), + length = resourceSegments[i].dwSegmentLength + }; + + if(thisResource.length == 0) + thisResource.length = 65536; + if(thisResource.dataOffset == 0) + thisResource.dataOffset = 65536; + if((resourceSegments[i].dwFlags & (ushort)SegmentFlags.Huge) == (ushort)SegmentFlags.Huge) + thisResource.length *= 16; + thisResource.data = new byte[thisResource.length]; + BaseStream.Position = thisResource.dataOffset; + BaseStream.Read(thisResource.data, 0, thisResource.data.Length); + + thisResourceType.Add(thisResource); + os2resources.Remove(entries[i].etype); + os2resources.Add(entries[i].etype, thisResourceType); + } + + if(os2resources.Count > 0) + { + Resources = new ResourceTable(); + int counter = 0; + Resources.types = new ResourceType[os2resources.Count]; + foreach(KeyValuePair> kvp in os2resources) + { + Resources.types[counter].count = (ushort)kvp.Value.Count; + Resources.types[counter].id = kvp.Key; + Resources.types[counter].name = ResourceIdToNameOs2(kvp.Key); + Resources.types[counter].resources = kvp.Value.OrderBy(r => r.id).ToArray(); + counter++; + } + } + } resourceUpperLimit = ushort.MaxValue; diff --git a/libexeinfo/NE/Structs.cs b/libexeinfo/NE/Structs.cs index f4f9a6b..15c037d 100644 --- a/libexeinfo/NE/Structs.cs +++ b/libexeinfo/NE/Structs.cs @@ -98,8 +98,8 @@ namespace libexeinfo /// public struct Resource { - public ushort dataOffset; - public ushort length; + public uint dataOffset; + public uint length; public ResourceFlags flags; public ushort id; public uint reserved; @@ -176,5 +176,12 @@ namespace libexeinfo /// public ushort dwMinimumAllocation; } + + [StructLayout(LayoutKind.Sequential)] + public struct Os2ResourceTableEntry + { + public ushort etype; + public ushort ename; + } } } \ No newline at end of file