mardi 6 octobre 2020

Get properties from object in c# - Originally from base64 string in legacy system with no access to the original binaries

I have a peculiar situation. In a legacy system no longer used we have base64 values stored that we now need to access.

By converting the base64 value to a string I can see that the base64 value contains my properties needed like this.

enter image description here

The problem is that I can't deserialize neither the byte array or the string to a anonymous type object or dynamic. This is because I don't have access to the binaries that this object is using. In this example it is shown as ConsoleApp2.

First try:

public static object FromByteArray(byte[] data)
{
    BinaryFormatter bf = new BinaryFormatter();
    using (MemoryStream ms = new MemoryStream(data))
    {
        object obj = bf.Deserialize(ms);
        return obj;
    }
}

Source:

https://stackoverflow.com/a/33022788/3850405

System.Runtime.Serialization.SerializationException: 'Unable to find assembly 'ConsoleApp2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.'

enter image description here

Given that you normally can access properties from a plain Object Class I tried to set every assembly to System.Object with a SerializationBinder.

object o = new { A = "1", B = 2 };

enter image description here

public static object FromByteArray(byte[] data)
    {
        BinaryFormatter bf = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream(data))
        {
            bf.Binder = new PreMergeToMergedDeserializationBinder();
            object obj = bf.Deserialize(ms);
            return obj;
        }
    }
}

sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        var systemObjectAssembly = "System.Object, System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";

        return Type.GetType(systemObjectAssembly);
    }
}

Source:

https://stackoverflow.com/a/9012089/3850405

This prevents any runtime errors but everything that shows up looks like an empty object:

enter image description here

If I try to list properties using the code below it is of course empty as well.

Type myType = myObject.GetType();
List<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());

enter image description here

What can I do to deserialize this base64 string and access the properties? Preferably I would not like to create a complete class hierarchy since the original object is quite large.

Runnable example program:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            object o = new { A = "1", B = 2 };

            var base64String = "AAEAAAD/////AQAAAAAAAAAMAgAAAEJDb25zb2xlQXBwMiwgVmVyc2lvbj0xLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAABhDb25zb2xlQXBwMi5FeGFtcGxlTW9kZWwCAAAAHDxNeVByb3BlcnR5QT5rX19CYWNraW5nRmllbGQcPE15UHJvcGVydHlCPmtfX0JhY2tpbmdGaWVsZAEACAIAAAAGAwAAAAtNeVRlc3RWYWx1ZXsAAAAL";

            byte[] byteArray = Convert.FromBase64String(base64String);
            string objectInfo = System.Text.Encoding.UTF8.GetString(byteArray);

            var myObject = FromByteArray(byteArray);

            Type myType = myObject.GetType();
            List<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
        }

        public static object FromByteArray(byte[] data)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream(data))
            {
                bf.Binder = new PreMergeToMergedDeserializationBinder();
                object obj = bf.Deserialize(ms);
                return obj;
            }
        }
    }

    sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            var systemObjectAssembly = "System.Object, System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";

            return Type.GetType(systemObjectAssembly);
        }
    }
}

Guess how the original object could have been stored in the first place:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApp2
{
    [Serializable]
    public class ExampleModel
    {
        public string MyPropertyA { get; set; }

        public int MyPropertyB { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var t = new ExampleModel();
            t.MyPropertyA = "MyTestValue";
            t.MyPropertyB = 123;

            var byteArray = ToByteArray<ExampleModel>(t);

            var base64String = Convert.ToBase64String(byteArray);
        }

        public static byte[] ToByteArray<T>(T obj)
        {
            if (obj == null)
                return null;
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, obj);
                return ms.ToArray();
            }
        }
    }
}




Aucun commentaire:

Enregistrer un commentaire