Proto-buf serialization with Obfuscation

2020-07-27 02:18发布

问题:

I am looking for some guidance as to what is going on when using proto-buf net with obfuscation (Dotfuscator). One half of the project is a DLL and the other is an EXE elsewhere and using proto-buf NET they exchange data flawlessly. Until I obfuscate the DLL.

At that point P-BN fails without raising an exception, returning variously a 0 length byte array or a foreshortened one depending on what I have fiddled with. The class is fairly simple (VB):

<ProtoContract(Name:="DMailer")> _
Friend Class DMailer

    Private _Lic As Cert
    Private _Sys As Sys
    Private _LList As List(Of LItem)

    ..
    ..
End Class

There are 3 props all decorated with ProtoMember to get/set the constituent class objects. Snipped for brevity.

Again, it works GREAT until I obfuscate the DLL. Then, Dotfuscator renames each of these to null, apparently since they are all Friend, and that seems to choke proto-buff. If I exempt the class from renaming (just the class name, not props/members), it seems to work again. It makes sense that P-BN would only be able to act on objects with a proper name, though when asked to serialize a null named object, it seems like an exception might be in order.

On the other hand, much of the charm of PB-N is supposed to be serialization independent of .NET names working from attributes - at least as I understand it. Yet in this case it only seems to work with classes with names. I tried using the Name qualifier or argument as shown above, to no avail - it apparently doesnt do what I thought it might.

So, I am curious if:

a) ...I have basically surmised the problem correctly

b) ...There is some other attribute or flag that might facilitate serializing a null named object

c) ...if there are any other insights that would help.

If I exempt all 3 or 4 classes from Dotfuscator renaming (LList is not actually implemented yet, leaving DMailer, Cert and Sys), the DLL seems to work again - at least the output is the correct size. I can live with that, though obscured names would be better: Dotfuscator (CE) either exempts them or sets the names to Null - I cant seem to find a way to force them to be renamed.

Rather than exempt 3 or 4 classes from renaming, one alternative I am considering is to simply store the Serializer output for Cert and Sys as byte arrays or Base64 strings in DMailer instead of classes. Then have the receiver Deserialize each object individually. It is kind of nice to be able to unpack just one thing and have your toys right there as if by magic though.

(many)TIA

回答1:

Since there were a few upticks of interest, here is what looks like will work:

a) No form of reflection will be able to get the list of properties for an obfuscated type. I tried walking thru all the types to find the ones with ProtoContract on it, I could find them but the property names are all changed to a,m, b, j, g.

I also tried Me.GetType.GetProperties with the same result.

You could implement a map from the output to indicate that Employee.FirstName is now a0.j, but distributing this defeats the purpose of obfuscation.

b) What does work to a degree is to exempt the class NAME from obfuscation. Since PB-N looks for the ProtoMember attributes to get the data, you CAN obfuscate the Property/Member names, just not the CLASS/type name. If the name is something like FederalReserveLogIn, your class/type has a bullseye on it.

I have had initial success doing the following:

1) Build a simple class to store a Property Token and value. Store everything as string using ConvertFromInvariantString. Taking a tip from PBN, I used an integer for the token:

<ProtoMember(propIndex.Foo)>
Property Foo As String

An enum helps tie everything together later. Store these in a Dictionary(Of T, NameValuePair)

2) add some accessors. these can perform the type conversions for you:

  Public Sub Add(ByVal Key As T, ByVal value As Object)
        If _col.ContainsKey(Key) Then
            _col.Remove(Key)
        End If

        _col.Add(Key, New TValue(value))

    End Sub

    Public Function GetTItem(Of TT)(key As T) As TT
        If _col.ContainsKey(key) Then
            Return CType(_col(key).TValue, TT)
        Else
            Return Nothing
        End If
    End Function

T is whatever key type you wish to use. Integer results in the smallest output and still allows the subscribing code to use an Enum. But it could be String.

TT is the original type:

myFoo = props.GetTItem(Of Long)(propsEnum.Foo)

3) Expose the innerlist (dictionary) to PBN and bingo, all done.

Its also very easy to add converters for Point, Rectangle, Font, Size, Color and even bitmap.

HTH



回答2:

Interesting. I confess I have never tried this scenario, but if you can walk me through your process (or better: maybe provide a basic repro example with "run this, then this, then this: boom") I'll happily investigate.

Note: the Name on ProtoContract is mainly intended for GetProto() usage; it is not needed by the core serializer, and can be omitted to reduce your exposure. Also, protobuf-net isn't interested in fields unless those fields are decorated with the attributes, so that shouldn't be an issue.

However! there's probably a workaround here that should work now; you can pre-generate a static serialization dll; for example in a separate console exe (just as a tool; I really need to wrap this in a standalone utility!)

So if you create a console exe that references your unobfuscated library and protobuf-net.dll:

var model = RuntimeTypeModel.Create();
model.Add(typeof(DMailer), true); // true means "use the attributes etc"
// and other types needed, etc
model.Compile("MailSerializer", "MailSerializer.dll");

this should write MailSerializer.dll, which you can then reference from your main code (in addition to protobuf-net), and use:

var ser = new MailSerializer(); // our pre-genereated serializer
ser.Serialize(...); // etc

Then include MailSerializer.dll in your obfuscation payload.

(this is all v2 specific, btw)

If this doesn't work, I'll need to investigate the main issue, but I'm not an obfuscation expert so could do with your repro steps.