Search code examples
.netobfuscationserializationsmartassembly

How do I maintain deserialization compatibility across obfuscated and debug builds?


I'm trying to get the {smartassembly} .NET obfuscator to work with my system. I currently store user data in a series of serialized dictionary classes, and then deserialize those classes to get the data back. I'm already ignoring assembly version information, just because that way making life a pain. That code is adapted from MSDN:

//to avoid cross-versioning problems
public sealed class CrossVersionDeserializationBinder : SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
        Type typeToDeserialize = null;

        typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, assemblyName));

        return typeToDeserialize;
    }
}

Problem is, now my obfuscated app will ignore versioning information, but can't read data saved by the non-obfuscated app, and vice versa. We'll need to have a non-obfuscated version in order to debug the application, so this is a pretty big showstopper for us. Any way to get around this problem? Should I just not obfuscate the data classes? That seems like a pretty large security hole.


Solution

  • Perhaps consider a serializer that isn't tied to the type and field-names? For example, protobuf-net is a binary serializer, but uses numeric tags set (via an attribute) against each member. This means:

    • serialization isn't tied to the assembly version at all
    • the field name information isn't in the serialized file
    • (by the above) it won't matter whether the code is obfuscated
    • (and) the file can't be used to trivially break the obfuscation (although the data might still suggest intent unless encrypted)

    For example:

    [ProtoContract]
    public class Foo {
        [ProtoMember(1)]
        public string Bar {get;set;}
    }
    

    Here, the 1 is all that identified the member in the file. The key here is that it is contract-based, so can be deserialized later with an unrelated type:

    [ProtoContract]
    public class a12 {
        [ProtoMember(1)]
        public string a {get;set;}
    }
    

    (which is fine since obfuscation preserves metadata, IIRC).

    Contrast this to other contract-based serializers (such as XmlSerializer or DataContractSerializer) - where you would be forced to put the member-name in the attributes, which would pretty much render obfuscation pointless:

    [DataContract]
    public class a12 {
        [DataMember(Name="Bar")]
        public string a {get;set;}
    }