二进制流' 226'不包含有效的BinaryHeader - C#

时间:2018-05-24 08:38:07

标签: c# encryption serialization binaryformatter

我有一个典型的二进制流' x'不包含有效的BinaryHeader',但我在网上看到的所有问题似乎都无法解决我的具体情况。

问题详情

该程序的一部分是最基本的术语,它将类数据写入二进制文件。这99.9%的时间完美无缺,但我们最近发现了一种让数据损坏的模糊方法。

因此,序列化的类是名为&#39; RecordEntry&#39; 的公共类,标有[Serializable]属性。该类继承自接口。 &#39; RecordEntry&#39; 包含一堆包含信息的变量,但导致我们出现问题的变量是一个名为&#39; EntryField&#39; <的类的数组/ em>的。这是一个非常简单的类,它包含3个字符串,也标记为[Serializable]

如果&#39; EntryField&#39; 类的数组(让我们称之为&#39; EntryFieldArray&#39; ),则长度为4,并且每个&#39; EntryField&#39; 中的第一个字符串长度为13,每个字符串中的第三个字符串的长度为1,然后在尝试反序列化时,我收到错误标题。可能有一些更复杂的方法来重现同样的崩溃,但这是我现在发现的那种。

序列化代码

首先,&#39; RecordEntry&#39; 类转换为字节:

public static byte[] ToBytes(this object obj)
{
    using (var stream = new MemoryStream())
    {
        IFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, obj);
        return stream.ToArray();
    }
}

然后将该字节数组作为数据块添加到文件中。

为了读取加密文件,首先在定义的块中读回字节,然后使用以下方法解密:

internal static T ObjectFromBytes<T>(this byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        IFormatter formatter = new BinaryFormatter();
        return (T)formatter.Deserialize(stream);
    }
}

这里是跟踪错误的堆栈跟踪的重要部分:

&#13;
&#13;
System.Runtime.Serialization.SerializationException : Binary stream '226' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization.
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
   at <ProjectName>.<ProjectFile>.ObjectFromBytes[T](Byte[] bytes)
&#13;
&#13;
&#13;

我似乎无法追溯问题,而且我与之联系的一些开发人员表示,这可能是微软序列化代码的一个问题。关于什么可能导致像这样的问题的任何想法?

1 个答案:

答案 0 :(得分:0)

您写的是加密...也许加密的方式存在一些问题......您可以做的最好的事情就是对序列化数据添加一些检查。

// https://en.wikipedia.org/wiki/Jenkins_hash_function
public static uint JenkinsOneAtATimeHash(byte[] key, int start, int count)
{
    int i = start;
    int end = start + count;
    uint hash = 0;
    while (i != end)
    {
        hash += key[i++];
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash;
}

public static byte[] ToBytes(this object obj)
{
    using (var stream = new MemoryStream())
    {
        IFormatter formatter = new BinaryFormatter();

        // We will prepend the length of the serialized object, we leave some space
        stream.Position += sizeof(int);

        formatter.Serialize(stream, obj);

        // We append a Jenkins hash of the serialized object
        uint hash = JenkinsOneAtATimeHash(stream.GetBuffer(), 4, (int)stream.Length - sizeof(int));

        byte[] buffer = BitConverter.GetBytes(hash);
        stream.Write(buffer, 0, buffer.Length);

        // We prepend the length of the serialized object (max 2gb)
        buffer = BitConverter.GetBytes((int)stream.Length - sizeof(int) - sizeof(int));
        stream.Position = 0;
        stream.Write(buffer, 0, buffer.Length);

        return stream.ToArray();
    }
}

internal static T ObjectFromBytes<T>(this byte[] bytes)
{
    if (bytes.Length < sizeof(int) + sizeof(int))
    {
        throw new Exception(string.Format("Serialized length: {0} < {1}", bytes.Length, sizeof(int) + sizeof(int)));
    }

    int length = BitConverter.ToInt32(bytes, 0);

    if (length != bytes.Length - sizeof(int) - sizeof(int))
    {
        throw new Exception(string.Format("Serialized length should be {0}, is {1} (+ sizeof(int) * 2)", length, bytes.Length - sizeof(int) - sizeof(int)));
    }

    uint hash = BitConverter.ToUInt32(bytes, bytes.Length - 4);

    uint hash2 = JenkinsOneAtATimeHash(bytes, sizeof(int), bytes.Length - sizeof(int) - sizeof(int));

    if (hash != hash2)
    {
        throw new Exception("Wrong hash!");
    }

    using (var stream = new MemoryStream(bytes, sizeof(int), bytes.Length - sizeof(int) - sizeof(int)))
    {
        IFormatter formatter = new BinaryFormatter();
        return (T)formatter.Deserialize(stream);
    }
}

我已经改变了序列化和反序列化方法。现在我在前面添加对象的长度并附加simple hash。它不是一个非常强大的哈希值,但它应该足以检测数据的变化。在反序列化时,我会对流的长度和散列进行一些检查。