Add decoding of BeOS resources section. (It's the same on ELF and PEFF).

Thanks to @waddlesplash for pointing me to the structures.
This commit is contained in:
2018-03-10 16:14:52 +00:00
parent 61521fe4d3
commit 0b669a62bf
5 changed files with 504 additions and 1 deletions

85
libexeinfo/BeOS/Consts.cs Normal file
View File

@@ -0,0 +1,85 @@
//
// Consts.cs
//
// Author:
// Natalia Portillo <claunia@claunia.com>
//
// 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";
}
}

View File

@@ -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<ResourcesHeader>(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<ResourceIndexSectionHeader>(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<ResourceIndexEntry>(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<ResourceInfo>(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<string, List<Resource>> rezzes = new Dictionary<string, List<Resource>>();
for(int i = 0; i < header.resource_count; i++)
{
rezzes.TryGetValue(types[i], out List<Resource> thisRezzes);
if(thisRezzes == null) thisRezzes = new List<Resource>();
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<ResourceTypeBlock> result = new List<ResourceTypeBlock>();
foreach(KeyValuePair<string, List<Resource>> 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<ResourcesHeader>(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<ResourceIndexSectionHeader>(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<ResourceIndexEntry>(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<ResourceInfo>(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<string, List<Resource>> rezzes = new Dictionary<string, List<Resource>>();
for(int i = 0; i < header.resource_count; i++)
{
rezzes.TryGetValue(types[i], out List<Resource> thisRezzes);
if(thisRezzes == null) thisRezzes = new List<Resource>();
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<ResourceTypeBlock> result = new List<ResourceTypeBlock>();
foreach(KeyValuePair<string, List<Resource>> kvp in rezzes)
{
ResourceTypeBlock block = new ResourceTypeBlock {type = kvp.Key, resources = kvp.Value.ToArray()};
result.Add(block);
}
return result.ToArray();
}
}
}

205
libexeinfo/BeOS/Structs.cs Normal file
View File

@@ -0,0 +1,205 @@
//
// Structs.cs
//
// Author:
// Natalia Portillo <claunia@claunia.com>
//
// 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
{
/// <summary>
/// Header of resource data
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct ResourcesHeader
{
/// <summary>
/// <see cref="Consts.RESOURCES_HEADER_MAGIC" />
/// </summary>
public uint magic;
/// <summary>
/// How many resources are present
/// </summary>
public uint resource_count;
/// <summary>
/// Offset to <see cref="ResourceIndexSectionHeader" />
/// </summary>
public uint index_section_offset;
/// <summary>
/// Size of the admin section
/// </summary>
public uint admin_section_size;
/// <summary>
/// Padding
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)]
public uint[] pad;
}
/// <summary>
/// Header of the resource index, followed by as many <see cref="ResourceIndexEntry" /> as resources are in the file
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct ResourceIndexSectionHeader
{
/// <summary>
/// Offset to itself
/// </summary>
public uint index_section_offset;
/// <summary>
/// Size in bytes of the whole index section
/// </summary>
public uint index_section_size;
/// <summary>
/// Unused
/// </summary>
public uint unused_data1;
/// <summary>
/// Offset to an unknown section
/// </summary>
public uint unknown_section_offset;
/// <summary>
/// Size in bytes of the unknown section
/// </summary>
public uint unknown_section_size;
/// <summary>
/// Unused
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
public uint[] unused_data2;
/// <summary>
/// Offset to <see cref="ResourceInfoBlock" /> table
/// </summary>
public uint info_table_offset;
/// <summary>
/// Sizee of <see cref="ResourceInfoBlock" /> table
/// </summary>
public uint info_table_size;
/// <summary>
/// Unused
/// </summary>
public uint unused_data3;
}
/// <summary>
/// A resource index entry
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct ResourceIndexEntry
{
/// <summary>
/// Offset to resource
/// </summary>
public uint offset;
/// <summary>
/// Size of resource
/// </summary>
public uint size;
/// <summary>
/// Padding
/// </summary>
public uint pad;
}
/// <summary>
/// Resource information, followed by <see cref="name_size" /> ASCII characters with the name
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 2)]
struct ResourceInfo
{
/// <summary>
/// Resource ID
/// </summary>
public int id;
/// <summary>
/// Resource index, not related to index table
/// </summary>
public int index;
/// <summary>
/// Size of name following this structure
/// </summary>
public ushort name_size;
}
/// <summary>
/// Separator between resource types
/// </summary>
[StructLayout(LayoutKind.Sequential)]
struct ResourceInfoSeparator
{
/// <summary>
/// Must be 0xFFFFFFFF
/// </summary>
public uint value1;
/// <summary>
/// Must be 0xFFFFFFFF
/// </summary>
public uint value2;
}
/// <summary>
/// Resource info block, followed by <see cref="ResourceInfoSeparator" /> when the <see cref="info" /> array ends
/// </summary>
struct ResourceInfoBlock
{
/// <summary>
/// OSType code
/// </summary>
public uint type;
/// <summary>
/// Array of <see cref="ResourceInfo" />. Unless a <see cref="ResourceInfoSeparator" /> is found.
/// </summary>
public ResourceInfo[] info;
}
/// <summary>
/// End of the resource info table
/// </summary>
struct ResourceInfoTableEnd
{
/// <summary>
/// Resource info table checksum
/// </summary>
public uint checksum;
/// <summary>
/// Must be 0
/// </summary>
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;
}
}

View File

@@ -54,6 +54,7 @@ namespace libexeinfo
public Version[] Versions;
public ResourceNode WindowsResourcesRoot;
WindowsHeader64 winHeader;
public BeOS.ResourceTypeBlock[] beosResources;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.PE" /> 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
{

View File

@@ -50,6 +50,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AtariST\Enums.cs" />
<Compile Include="BeOS\Consts.cs" />
<Compile Include="BeOS\Resources.cs" />
<Compile Include="BeOS\Structs.cs" />
<Compile Include="Enums.cs" />
<Compile Include="GEM\Enums.cs" />
<Compile Include="GEM\Resources.cs" />