我有一个典型的二进制流' x'不包含有效的BinaryHeader',但我在网上看到的所有问题似乎都无法解决我的具体情况。
问题详情
该程序的一部分是最基本的术语,它将类数据写入二进制文件。这99.9%的时间完美无缺,但我们最近发现了一种让数据损坏的模糊方法。
因此,序列化的类是名为' RecordEntry' 的公共类,标有[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);
}
}
这里是跟踪错误的堆栈跟踪的重要部分:
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;
我似乎无法追溯问题,而且我与之联系的一些开发人员表示,这可能是微软序列化代码的一个问题。关于什么可能导致像这样的问题的任何想法?
答案 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。它不是一个非常强大的哈希值,但它应该足以检测数据的变化。在反序列化时,我会对流的长度和散列进行一些检查。