.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
, сохраняют свои младшие бит.