Двоичный поток «226» не содержит допустимый BinaryHeader - C # - PullRequest
0 голосов
/ 24 мая 2018

У меня типичный «Бинарный поток» x не содержит действительного BinaryHeader », но все вопросы, на которые я смотрел онлайн, похоже, не решают мою конкретную ситуацию.

Сведения о выпуске

Часть программы, о которой идет речь, в своих основных терминах записывает данные класса в двоичный файл.Это работает безупречно в 99,9% случаев, но недавно мы обнаружили неясный способ повредить данные.

Итак, сериализуемый класс - это публичный класс с именем 'RecordEntry' отмечен атрибутом [Serializable].Класс наследуется от интерфейса. 'RecordEntry' содержит набор переменных с информацией, но та, которая вызывает у нас проблему, представляет собой массив класса с именем 'EntryField' .Это чрезвычайно простой класс, который содержит 3 строки и также помечен как [Serializable].

Если массив 'EntryField' классов (давайте назовем его 'EntryFieldArray' ), имеет длину 4, и первая строка в каждом 'EntryField' имеет длину 13, а третья строка в каждом имеет длину 1, затем при попытке десериализации Iполучите ошибку в заголовке.Возможно, есть еще несколько неясных способов воспроизвести тот же сбой, но я нашел его именно сейчас.

Сериализационный код

Во-первых, Класс 'RecordEntry' преобразуется в байты:

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)

Кажется, я не могу отследить эту проблему, и некоторые разработчики, с которыми я связывался, сказали, что это может быть проблема с кодом сериализации Microsoft.Любые идеи о том, что может вызвать такую ​​проблему?

1 Ответ

0 голосов
/ 24 мая 2018

Вы пишете о шифровании ... Возможно, есть какая-то проблема с тем, как выполняется шифрование ... Лучшее, что вы можете сделать, это добавить несколько проверок к вашим сериализованным данным.

// 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);
    }
}

I 'Мы изменили методы сериализации и десериализации.Теперь я добавляю длину объекта и добавляю простой хеш .Это не очень сильный хеш, но этого должно быть достаточно, чтобы обнаружить изменения в данных.После десериализации я проверяю длину потока и хеш.

...