From 4be7b3e1c541ebafb1b7822b40dbb81526a38d4d Mon Sep 17 00:00:00 2001 From: ElektroKill Date: Sun, 3 Sep 2023 21:55:15 +0200 Subject: [PATCH] Add support for deserializing images stored in newer formats --- .../Resources/SerializedImageUtilities.cs | 83 +++++++++++++++---- .../SerializedImageResourceElementNode.cs | 6 +- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs index de4d0add50..a1675056b2 100644 --- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs +++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Text; using dnlib.DotNet; using dnlib.DotNet.Resources; @@ -36,34 +37,84 @@ public static class SerializedImageUtilities { /// Serialized data /// Updated with the image data /// - public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) { + public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) => + GetImageData(module, typeName, serializedData, SerializationFormat.BinaryFormatter, out imageData); + + /// + /// Gets the image data + /// + /// Module + /// Name of type + /// Serialized data + /// Format of serialized data + /// Updated with the image data + /// + public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, SerializationFormat format, [NotNullWhen(true)] out byte[]? imageData) { imageData = null; + if (CouldBeBitmap(module, typeName)) { - var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData); - // Bitmap loops over every item looking for "Data" (case insensitive) - foreach (var v in dict.Values) { - var d = v.Value as byte[]; - if (d is null) - continue; - if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) { - imageData = d; - return true; + if (format == SerializationFormat.BinaryFormatter) { + var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData); + // Bitmap loops over every item looking for "Data" (case insensitive) + foreach (var v in dict.Values) { + var d = v.Value as byte[]; + if (d is null) + continue; + if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) { + imageData = d; + return true; + } } + return false; + } + if (format == SerializationFormat.ActivatorStream) { + imageData = serializedData; + return true; + } + if (format == SerializationFormat.TypeConverterByteArray) { + imageData = GetBitmapData(serializedData) ?? serializedData; + return true; } - return false; } if (CouldBeIcon(module, typeName)) { - var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData); - if (!dict.TryGetValue("IconData", out var info)) - return false; - imageData = info.Value as byte[]; - return imageData is not null; + if (format == SerializationFormat.BinaryFormatter) { + var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData); + if (!dict.TryGetValue("IconData", out var info)) + return false; + imageData = info.Value as byte[]; + return imageData is not null; + } + if (format == SerializationFormat.ActivatorStream || format == SerializationFormat.TypeConverterByteArray) { + imageData = serializedData; + return true; + } } return false; } + static byte[]? GetBitmapData(byte[] rawData) { + // Based on ImageConverter.GetBitmapStream + // See https://github.com/dotnet/winforms/blob/main/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs + if (rawData.Length <= 18) + return null; + + short sig = (short)(rawData[0] | rawData[1] << 8); + if (sig != 0x1C15) + return null; + + short headerSize = (short)(rawData[2] | rawData[3] << 8); + if (rawData.Length <= headerSize + 18) + return null; + if (Encoding.ASCII.GetString(rawData, headerSize + 12, 6) != "PBrush") + return null; + + var newData = new byte[rawData.Length - 78]; + Buffer.BlockCopy(rawData, 78, newData, 0, newData.Length); + return newData; + } + static bool CouldBeBitmap(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingBitmap); static bool CouldBeIcon(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingIcon); diff --git a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs index d17a7ef783..045ad294e8 100644 --- a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs +++ b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs @@ -42,7 +42,7 @@ sealed class SerializedImageResourceElementNodeProvider : IResourceNodeProvider if (serializedData is null) return null; - if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, out var imageData)) + if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, serializedData.Format, out var imageData)) return new SerializedImageResourceElementNodeImpl(treeNodeGroup, resourceElement, imageData); return null; @@ -94,7 +94,7 @@ protected override IEnumerable GetDeserializedData() { return res; var binData = (BinaryResourceData)newResElem.ResourceData; - if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData)) + if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData)) return dnSpy_Resources.NewDataIsNotAnImage; try { @@ -111,7 +111,7 @@ public override void UpdateData(ResourceElement newResElem) { base.UpdateData(newResElem); var binData = (BinaryResourceData)newResElem.ResourceData; - SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData); + SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData); Debug2.Assert(imageData is not null); InitializeImageData(imageData); }