From 0b669a62bf10d2f9abbadf90c30f97b7593a8979 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 10 Mar 2018 16:14:52 +0000 Subject: [PATCH] Add decoding of BeOS resources section. (It's the same on ELF and PEFF). Thanks to @waddlesplash for pointing me to the structures. --- libexeinfo/BeOS/Consts.cs | 85 +++++++++++++++ libexeinfo/BeOS/Resources.cs | 206 +++++++++++++++++++++++++++++++++++ libexeinfo/BeOS/Structs.cs | 205 ++++++++++++++++++++++++++++++++++ libexeinfo/PE/PE.cs | 6 +- libexeinfo/libexeinfo.csproj | 3 + 5 files changed, 504 insertions(+), 1 deletion(-) create mode 100644 libexeinfo/BeOS/Consts.cs create mode 100644 libexeinfo/BeOS/Resources.cs create mode 100644 libexeinfo/BeOS/Structs.cs diff --git a/libexeinfo/BeOS/Consts.cs b/libexeinfo/BeOS/Consts.cs new file mode 100644 index 0000000..2bde376 --- /dev/null +++ b/libexeinfo/BeOS/Consts.cs @@ -0,0 +1,85 @@ +// +// Consts.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2017-2018 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.BeOS +{ + static class Consts + { + internal const int RESOURCES_HEADER_MAGIC = 0x444F1000; + + // String enums not supported in C# *sad* + public const string B_AFFINE_TRANSFORM_TYPE = "AMTX"; + public const string B_ALIGNMENT_TYPE = "ALGN"; + public const string B_ANY_TYPE = "ANYT"; + public const string B_ATOM_TYPE = "ATOM"; + public const string B_ATOMREF_TYPE = "ATMR"; + public const string B_BOOL_TYPE = "BOOL"; + public const string B_CHAR_TYPE = "CHAR"; + public const string B_COLOR_8_BIT_TYPE = "CLRB"; + public const string B_DOUBLE_TYPE = "DBLE"; + public const string B_FLOAT_TYPE = "FLOT"; + public const string B_GRAYSCALE_8_BIT_TYPE = "GRYB"; + public const string B_INT16_TYPE = "SHRT"; + public const string B_INT32_TYPE = "LONG"; + public const string B_INT64_TYPE = "LLNG"; + public const string B_INT8_TYPE = "BYTE"; + public const string B_LARGE_ICON_TYPE = "ICON"; + public const string B_MEDIA_PARAMETER_GROUP_TYPE = "BMCG"; + public const string B_MEDIA_PARAMETER_TYPE = "BMCT"; + public const string B_MEDIA_PARAMETER_WEB_TYPE = "BMCW"; + public const string B_MESSAGE_TYPE = "MSGG"; + public const string B_MESSENGER_TYPE = "MSNG"; + public const string B_MIME_TYPE = "MIME"; + public const string B_MINI_ICON_TYPE = "MICN"; + public const string B_MONOCHROME_1_BIT_TYPE = "MNOB"; + public const string B_OBJECT_TYPE = "OPTR"; + public const string B_OFF_T_TYPE = "OFFT"; + public const string B_PATTERN_TYPE = "PATN"; + public const string B_POINTER_TYPE = "PNTR"; + public const string B_POINT_TYPE = "BPNT"; + public const string B_PROPERTY_INFO_TYPE = "SCTD"; + public const string B_RAW_TYPE = "RAWT"; + public const string B_RECT_TYPE = "RECT"; + public const string B_REF_TYPE = "RREF"; + public const string B_RGB_32_BIT_TYPE = "RGBB"; + public const string B_RGB_COLOR_TYPE = "RGBC"; + public const string B_SIZE_TYPE = "SIZE"; + public const string B_SIZE_T_TYPE = "SIZT"; + public const string B_SSIZE_T_TYPE = "SSZT"; + public const string B_STRING_TYPE = "CSTR"; + public const string B_STRING_LIST_TYPE = "STRL"; + public const string B_TIME_TYPE = "TIME"; + public const string B_UINT16_TYPE = "USHT"; + public const string B_UINT32_TYPE = "ULNG"; + public const string B_UINT64_TYPE = "ULLG"; + public const string B_UINT8_TYPE = "UBYT"; + public const string B_VECTOR_ICON_TYPE = "VICN"; + public const string B_XATTR_TYPE = "XATR"; + public const string B_NETWORK_ADDRESS_TYPE = "NWAD"; + public const string B_MIME_STRING_TYPE = "MIMS"; + public const string B_ASCII_TYPE = "TEXT"; + } +} \ No newline at end of file diff --git a/libexeinfo/BeOS/Resources.cs b/libexeinfo/BeOS/Resources.cs new file mode 100644 index 0000000..5838e44 --- /dev/null +++ b/libexeinfo/BeOS/Resources.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace libexeinfo.BeOS +{ + public static class Resources + { + public static ResourceTypeBlock[] Decode(byte[] data, bool bigEndian = false) + { + return bigEndian ? ResourceEdoced(data) : ResourceDecode(data); + } + + static ResourceTypeBlock[] ResourceDecode(byte[] data) + { + uint pos = 0; + + byte[] buffer = new byte[Marshal.SizeOf(typeof(ResourcesHeader))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + ResourcesHeader header = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + + if(header.magic != Consts.RESOURCES_HEADER_MAGIC) return null; + + ResourceIndexEntry[] indexes = new ResourceIndexEntry[header.resource_count]; + pos = header.index_section_offset; + buffer = new byte[Marshal.SizeOf(typeof(ResourceIndexSectionHeader))]; + if(pos + buffer.Length > data.Length) return null; + + Array.Copy(data, pos, buffer, 0, buffer.Length); + ResourceIndexSectionHeader indexHeader = + BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + pos += (uint)buffer.Length; + + for(int i = 0; i < header.resource_count; i++) + { + buffer = new byte[Marshal.SizeOf(typeof(ResourceIndexEntry))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + indexes[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + pos += (uint)buffer.Length; + } + + pos = indexHeader.info_table_offset; + string[] types = new string[header.resource_count]; + bool terminated = true; + string currentType = null; + string[] names = new string[header.resource_count]; + ResourceInfo[] infos = new ResourceInfo[header.resource_count]; + + for(int i = 0; i < header.resource_count; i++) + { + if(terminated) + { + buffer = new byte[4]; + Array.Copy(data, pos, buffer, 0, 4); + currentType = Encoding.ASCII.GetString(buffer.Reverse().ToArray()); + terminated = false; + pos += 4; + } + + buffer = new byte[Marshal.SizeOf(typeof(ResourceInfo))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + infos[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); + pos += (uint)buffer.Length; + buffer = new byte[infos[i].name_size - 1]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + names[i] = Encoding.ASCII.GetString(buffer); + pos += (uint)(buffer.Length + 1); + types[i] = currentType; + + if(BitConverter.ToInt32(data, (int)pos) != -1 || + BitConverter.ToInt32(data, (int)pos + 4) != -1) continue; + + terminated = true; + pos += 8; + } + + Dictionary> rezzes = new Dictionary>(); + for(int i = 0; i < header.resource_count; i++) + { + rezzes.TryGetValue(types[i], out List thisRezzes); + + if(thisRezzes == null) thisRezzes = new List(); + + Resource rez = new Resource + { + id = infos[i].id, + index = infos[i].index, + name = names[i], + data = new byte[indexes[i].size] + }; + + Array.Copy(data, indexes[i].offset, rez.data, 0, rez.data.Length); + + thisRezzes.Add(rez); + rezzes.Remove(types[i]); + rezzes.Add(types[i], thisRezzes); + } + + List result = new List(); + foreach(KeyValuePair> kvp in rezzes) + { + ResourceTypeBlock block = new ResourceTypeBlock {type = kvp.Key, resources = kvp.Value.ToArray()}; + result.Add(block); + } + + return result.ToArray(); + } + + static ResourceTypeBlock[] ResourceEdoced(byte[] data) + { + uint pos = 0; + + byte[] buffer = new byte[Marshal.SizeOf(typeof(ResourcesHeader))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + ResourcesHeader header = BigEndianMarshal.ByteArrayToStructureBigEndian(buffer); + + if(header.magic != Consts.RESOURCES_HEADER_MAGIC) return null; + + ResourceIndexEntry[] indexes = new ResourceIndexEntry[header.resource_count]; + pos = header.index_section_offset; + buffer = new byte[Marshal.SizeOf(typeof(ResourceIndexSectionHeader))]; + if(pos + buffer.Length > data.Length) return null; + + Array.Copy(data, pos, buffer, 0, buffer.Length); + ResourceIndexSectionHeader indexHeader = + BigEndianMarshal.ByteArrayToStructureBigEndian(buffer); + pos += (uint)buffer.Length; + + for(int i = 0; i < header.resource_count; i++) + { + buffer = new byte[Marshal.SizeOf(typeof(ResourceIndexEntry))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + indexes[i] = BigEndianMarshal.ByteArrayToStructureBigEndian(buffer); + pos += (uint)buffer.Length; + } + + pos = indexHeader.info_table_offset; + string[] types = new string[header.resource_count]; + bool terminated = true; + string currentType = null; + string[] names = new string[header.resource_count]; + ResourceInfo[] infos = new ResourceInfo[header.resource_count]; + + for(int i = 0; i < header.resource_count; i++) + { + if(terminated) + { + buffer = new byte[4]; + Array.Copy(data, pos, buffer, 0, 4); + currentType = Encoding.ASCII.GetString(buffer.ToArray()); + terminated = false; + pos += 4; + } + + buffer = new byte[Marshal.SizeOf(typeof(ResourceInfo))]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + infos[i] = BigEndianMarshal.ByteArrayToStructureBigEndian(buffer); + pos += (uint)buffer.Length; + buffer = new byte[infos[i].name_size - 1]; + Array.Copy(data, pos, buffer, 0, buffer.Length); + names[i] = Encoding.ASCII.GetString(buffer); + pos += (uint)(buffer.Length + 1); + types[i] = currentType; + + if(BitConverter.ToInt32(data, (int)pos) != -1 || + BitConverter.ToInt32(data, (int)pos + 4) != -1) continue; + + terminated = true; + pos += 8; + } + + Dictionary> rezzes = new Dictionary>(); + for(int i = 0; i < header.resource_count; i++) + { + rezzes.TryGetValue(types[i], out List thisRezzes); + + if(thisRezzes == null) thisRezzes = new List(); + + Resource rez = new Resource + { + id = infos[i].id, + index = infos[i].index, + name = names[i], + data = new byte[indexes[i].size] + }; + + Array.Copy(data, indexes[i].offset, rez.data, 0, rez.data.Length); + + thisRezzes.Add(rez); + rezzes.Remove(types[i]); + rezzes.Add(types[i], thisRezzes); + } + + List result = new List(); + foreach(KeyValuePair> kvp in rezzes) + { + ResourceTypeBlock block = new ResourceTypeBlock {type = kvp.Key, resources = kvp.Value.ToArray()}; + result.Add(block); + } + + return result.ToArray(); + } + } +} \ No newline at end of file diff --git a/libexeinfo/BeOS/Structs.cs b/libexeinfo/BeOS/Structs.cs new file mode 100644 index 0000000..ade27f3 --- /dev/null +++ b/libexeinfo/BeOS/Structs.cs @@ -0,0 +1,205 @@ +// +// Structs.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2017-2018 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.Runtime.InteropServices; + +// Structures thanks to Ingo Weinhold and Haiku +namespace libexeinfo.BeOS +{ + /// + /// Header of resource data + /// + [StructLayout(LayoutKind.Sequential)] + struct ResourcesHeader + { + /// + /// + /// + public uint magic; + /// + /// How many resources are present + /// + public uint resource_count; + /// + /// Offset to + /// + public uint index_section_offset; + /// + /// Size of the admin section + /// + public uint admin_section_size; + /// + /// Padding + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public uint[] pad; + } + + /// + /// Header of the resource index, followed by as many as resources are in the file + /// + [StructLayout(LayoutKind.Sequential)] + struct ResourceIndexSectionHeader + { + /// + /// Offset to itself + /// + public uint index_section_offset; + /// + /// Size in bytes of the whole index section + /// + public uint index_section_size; + /// + /// Unused + /// + public uint unused_data1; + /// + /// Offset to an unknown section + /// + public uint unknown_section_offset; + /// + /// Size in bytes of the unknown section + /// + public uint unknown_section_size; + /// + /// Unused + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)] + public uint[] unused_data2; + /// + /// Offset to table + /// + public uint info_table_offset; + /// + /// Sizee of table + /// + public uint info_table_size; + /// + /// Unused + /// + public uint unused_data3; + } + + /// + /// A resource index entry + /// + [StructLayout(LayoutKind.Sequential)] + struct ResourceIndexEntry + { + /// + /// Offset to resource + /// + public uint offset; + /// + /// Size of resource + /// + public uint size; + /// + /// Padding + /// + public uint pad; + } + + /// + /// Resource information, followed by ASCII characters with the name + /// + [StructLayout(LayoutKind.Sequential, Pack = 2)] + struct ResourceInfo + { + /// + /// Resource ID + /// + public int id; + /// + /// Resource index, not related to index table + /// + public int index; + /// + /// Size of name following this structure + /// + public ushort name_size; + } + + /// + /// Separator between resource types + /// + [StructLayout(LayoutKind.Sequential)] + struct ResourceInfoSeparator + { + /// + /// Must be 0xFFFFFFFF + /// + public uint value1; + /// + /// Must be 0xFFFFFFFF + /// + public uint value2; + } + + /// + /// Resource info block, followed by when the array ends + /// + struct ResourceInfoBlock + { + /// + /// OSType code + /// + public uint type; + /// + /// Array of . Unless a is found. + /// + public ResourceInfo[] info; + } + + /// + /// End of the resource info table + /// + struct ResourceInfoTableEnd + { + /// + /// Resource info table checksum + /// + public uint checksum; + /// + /// Must be 0 + /// + public uint terminator; + } + + public class ResourceTypeBlock + { + public Resource[] resources; + public string type; + } + + public class Resource + { + public byte[] data; + public int id; + public int index; + public string name; + } +} \ No newline at end of file diff --git a/libexeinfo/PE/PE.cs b/libexeinfo/PE/PE.cs index 39a9e00..debbe2d 100644 --- a/libexeinfo/PE/PE.cs +++ b/libexeinfo/PE/PE.cs @@ -54,6 +54,7 @@ namespace libexeinfo public Version[] Versions; public ResourceNode WindowsResourcesRoot; WindowsHeader64 winHeader; + public BeOS.ResourceTypeBlock[] beosResources; /// /// Initializes a new instance of the class. @@ -385,7 +386,10 @@ namespace libexeinfo rsrc.virtualSize = rsrc.sizeOfRawData; newSectionHeaders.Add(".rsrc", rsrc); - // TODO: Decode BeOS resource format + buffer = new byte[rsrc.sizeOfRawData]; + BaseStream.Position = rsrc.pointerToRawData; + BaseStream.Read(buffer, 0, buffer.Length); + beosResources = BeOS.Resources.Decode(buffer); } else { diff --git a/libexeinfo/libexeinfo.csproj b/libexeinfo/libexeinfo.csproj index 01c33c8..931afcb 100644 --- a/libexeinfo/libexeinfo.csproj +++ b/libexeinfo/libexeinfo.csproj @@ -50,6 +50,9 @@ + + +