.Net 4.6 разрывает шифратор XOR?

В .NET 4.5 этот шифр отлично работал на 32-битной и 64-битной архитектуре. Переключение проекта на .NET 4.6 полностью разрывает этот шифр в 64-разрядной версии, а в 32-разрядной версии исправляется нечетный.

В моем методе "DecodeSkill", SkillLevel - единственная часть, которая ломается на .NET 4.6. Используемые здесь переменные считываются из сетевого потока и кодируются.

DecodeSkill (всегда возвращает правильное декодированное значение для SkillLevel)

    private void DecodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        SkillLevel = ((ushort) ((byte)SkillLevel ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort) (ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort) (ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

ExchangeShortBits

    private static uint ExchangeShortBits(uint data, int bits)
    {
        data &= 0xffff;
        return (data >> bits | data << (16 - bits)) & 65535;
    }

DecodeSkill (исправлено для .NET 4.6 32-бит, заметьте "var patch = SkillLevel" )

    private void DecodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        var patch = SkillLevel = ((ushort) ((byte)SkillLevel ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort) (ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort) (ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

Назначение переменной как SkillLevel, только в 32-битных, приведет к тому, что SkillLevel всегда будет правильным значением. Удалите этот патч, и значение всегда неверно. В 64-битном случае это всегда неверно даже с патчем.

Я пробовал использовать MethodImplOptions.NoOptimization и MethodImplOptions.NoInlining по методу декодирования, думая, что это будет иметь значение.

Любые идеи, что могло бы вызвать это?

Изменить: Меня попросили привести пример ввода, хорошего вывода и плохого вывода. Это из фактического сценария использования, значения были отправлены от клиента и правильно декодированы сервером, используя "patch" на .NET 4.6.

Input:

ObjectId = 1000001

TargetObjectId = 2778236265
PositionX = 32409
PositionY = 16267
SkillId = 28399
SkillLevel = 8481

Хороший вывод

TargetObjectId = 0
PositionX = 302
PositionY = 278
SkillId = 1115
SkillLevel = 0

Плохой вывод

TargetObjectId = 0
PositionX = 302
PositionY = 278
SkillId = 1115
SkillLevel = 34545

Изменить # 2:

Я должен включить эту часть, определенно важную роль в этом.

EncodeSkill ( Timestamp - Environment.TickCount)

     private void EncodeSkill()
    {
        SkillId = (ushort) (ExchangeShortBits(ObjectId - 0x14be, 3) ^ ObjectId ^ 0x915d);
        SkillLevel = (ushort) ((SkillLevel + 0x100*(Timestamp%0x100)) ^ 0x3721);
        Arg1 = MathUtils.BitFold32(SkillId, SkillLevel);
        TargetObjectId = ExchangeLongBits(((TargetObjectId - 0x8b90b51a) ^ ObjectId ^ 0x5f2d2463u), 19);
        PositionX = (ushort) (ExchangeShortBits((uint) PositionX - 0xdd12, 1) ^ ObjectId ^ 0x2ed6);
        PositionY = (ushort) (ExchangeShortBits((uint) PositionY - 0x76de, 5) ^ ObjectId ^ 0xb99b);
    }

BitFold32

    public static int BitFold32(int lower16, int higher16)
    {
        return (lower16) | (higher16 << 16);
    }

ExchangeLongBits

    private static uint ExchangeLongBits(uint data, int bits)
    {
        return data >> bits | data << (32 - bits);
    }

Ответ 1

Вот код, который я придумал, который, по моему мнению, аналогичен вашему фактическому сценарию:

using System;
using System.Diagnostics;

class Program
{
    static void Main(string[] args)
    {
        var dc = new Decoder();
        dc.DecodeSkill();
        Debug.Assert(dc.TargetObjectId == 0m && dc.PositionX == 302 && dc.PositionY == 278 && dc.SkillId == 1115 && dc.SkillLevel == 0);
    }
}

class Decoder
{
    public uint ObjectId = 1000001;
    public uint TargetObjectId = 2778236265;
    public ushort PositionX = 32409;
    public ushort PositionY = 16267;
    public ushort SkillId = 28399;
    public ushort SkillLevel = 8481;

    public void DecodeSkill()
    {
        SkillId = (ushort)(ExchangeShortBits((SkillId ^ ObjectId ^ 0x915d), 13) + 0x14be);
        SkillLevel = ((ushort)((byte)(SkillLevel) ^ 0x21));
        TargetObjectId = (ExchangeLongBits(TargetObjectId, 13) ^ ObjectId ^ 0x5f2d2463) + 0x8b90b51a;
        PositionX = (ushort)(ExchangeShortBits((PositionX ^ ObjectId ^ 0x2ed6), 15) + 0xdd12);
        PositionY = (ushort)(ExchangeShortBits((PositionY ^ ObjectId ^ 0xb99b), 11) + 0x76de);
    }

    private static uint ExchangeShortBits(uint data, int bits)
    {
        data &= 0xffff;
        return (data >> bits | data << (16 - bits)) & 65535;
    }

    public static int BitFold32(int lower16, int higher16)
    {
        return (lower16) | (higher16 << 16);
    }

    private static uint ExchangeLongBits(uint data, int bits)
    {
        return data >> bits | data << (32 - bits);
    }
}

Вы XORing 8481 с 33. Это 8448, что я вижу на моей машине. Предполагая, что SkillLevel является ushort, я думаю, что происходит то, что вы ожидаете, что приведение byte будет усечено SkillLevel, так что все, что осталось, это последние 8 бит, но это не поэтому, когда вы возвращаетесь на ushort, биты более высокого порядка все еще существуют.

Если вы хотите надежно усечь все цифры после нижнего 8, вам нужно погладить его так:

SkillLevel = ((ushort) ((SkillLevel & 255) ^ 0x21));

ИЗМЕНИТЬ:

У меня есть подозрение, что это имеет какое-то отношение к числовым акциям операторов. Оператор ^, применяемый к byte или ushort и int, будет поддерживать оба операнда до int, так как неявные преобразования существуют из обоих возможных типов первого операнда в int. Похоже, что происходит то, что преобразование явное из ushort в byte, которое вызывает усечение, пропускается. Теперь у вас есть только два int s, которые, когда XORed, затем усекаются до ushort, сохраняют свои младшие бит.