Dominik Antal Dominik Antal - 1 month ago 10
C# Question

Binary deserialization without object definition

I'm trying to read a binary serialized object, I don't have the object definition/source for it. I took a peak into the file and saw property names, so I manually recreated the object (let's call it

SomeDataFormat
).

I ended up with this :

public class SomeDataFormat // 16 field
{
public string Name{ get; set; }
public int Country{ get; set; }
public string UserEmail{ get; set; }
public bool IsCaptchaDisplayed{ get; set; }
public bool IsForgotPasswordCaptchaDisplayed{ get; set; }
public bool IsSaveChecked{ get; set; }
public string SessionId{ get; set; }
public int SelectedLanguage{ get; set; }
public int SelectedUiCulture{ get; set; }
public int SecurityImageRefId{ get; set; }
public int LogOnId{ get; set; }
public bool BetaLogOn{ get; set; }
public int Amount{ get; set; }
public int CurrencyTo{ get; set; }
public int Delivery{ get; set; }
public bool displaySSN{ get; set; }
}


Now I'm able to deserialize it like this :

BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Full; // original uses this
formatter.TypeFormat = FormatterTypeStyle.TypesWhenNeeded; // this reduces size
FileStream readStream = new FileStream("data.dat", FileMode.Open);
SomeDataFormat data = (SomeDataFormat) formatter.Deserialize(readStream);


First suspicious thing is that only the 2 string (
SessionId
&
UserEmail
) has value in the deserialized data object. The other properties are null or just 0. This might be intended, but still, I suspect that something has gone wrone during the deserialization.

The second suspicious thing is if I reserialize this object, I end up with different file sizes. Original (695 bytes). Reserialized object is 698 bytes. So there is 3bytes difference. I should get the same file size as the original.

Taking a look at the original, and the new (reserialized) file:

The originally serialized file: (zoom)
enter image description here
The reserialized file: (zoom)
enter image description here

As you can see, after the header section, the data appears to be in different order. For example, you can see that the email, and the sessionID is not at the same place.

UPDATE: Will warned me that the byte coming after the "PublicKeyToken=null" is also different. (03 <-> 05)


  • Q1: Why are the values are in different order in the two files?

  • Q2: Why is there extra 3 bytes compared the 2 serialized objects?

  • Q3: What am I missing? How could I do this?



Any help is appreciated.




Kind of related questions:
1 2 3

Answer

Why are the values are in different order in the two files?

That is because member order is not based on the declaration ordering. http://msdn.microsoft.com/en-us/library/424c79hc.aspx

The GetMembers method does not return members in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which members are returned, because that order varies.

.

Why is there extra 3 bytes compared the 2 serialized objects?

First the TypeFormat 'TypesWhenNeeded' should actually be 'TypesAlways'. That is why there are so many differences. For example the 05 after '=null' becoming 03 is due to that.

Second you don't have the correct types. Looking at BinaryFormatter in ILSpy and the hex dump reveals that the members you marked as 'int' are actually 'string'.

public class SomeDataFormat // 16 field
{
    public string Name { get; set; }
    public string Country { get; set; } 
    public string UserEmail{ get; set; }
    public bool IsCaptchaDisplayed{ get; set; }
    public bool IsForgotPasswordCaptchaDisplayed{ get; set; }
    public bool IsSaveChecked{ get; set; }
    public string SessionId{ get; set; } 
    public string SelectedLanguage{ get; set; } 
    public string SelectedUiCulture{ get; set; } 
    public string SecurityImageRefId{ get; set; } 
    public string LogOnId{ get; set; } 
    public bool BetaLogOn{ get; set; } 
    public string Amount{ get; set; }
    public string CurrencyTo{ get; set; }
    public string Delivery{ get; set; } 
    public bool displaySSN{ get; set; }
}

What am I missing? How could I do this?

I don't see a way to do it with the given BinaryFormatter. You could decompile/reverse the way BinaryFormatter works.