.Net 4.6 打破 XOR 密码模式?

.Net 4.6 breaks XOR cipher pattern?

在 .NET 4.5 中,此密码在 32 位和 64 位体系结构上完美运行。将项目切换到 .NET 4.6 会在 64 位中完全破解此密码,而在 32 位中有一个 odd 补丁来解决这个问题。

在我的方法 "DecodeSkill" 中,SkillLevel 是唯一在 .NET 4.6 上失效的部分。 此处使用的变量是从网络流中读取并进行编码的。

DecodeSkill(始终 returns 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);
    }

仅在 32 位中将变量指定为 SkillLevel,将使 SkillLevel 始终为正确值。去掉这个补丁,值总是不对。在 64 位中,即使有补丁,这也总是不正确的。

我已经尝试在解码方法上使用 MethodImplOptions.NoOptimization 和 MethodImplOptions.NoInlining,认为它会有所作为。

知道什么会导致这种情况吗?

编辑: 我被要求举一个输入、好的输出和坏的输出的例子。 这是来自实际使用场景,值是从客户端发送的,并由服务器使用 .NET 4.6 上的 "patch" 正确解码。

输入:

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:

我应该包括这部分,这绝对是一个重要的部分。

编码技能(时间戳是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);
    }

这是我想出的代码,我认为它类似于您的实际场景:

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

您正在对 8481 与 33 进行异或运算。这就是 8448,这是我在我的机器上看到的。假设,SkillLevel 是一个 ushort,我想发生的事情是你期望对 byte 的转换截断 SkillLevel,这样剩下的就是最后 8 位,但这并没有发生,所以当你转换回 ushort 时,高阶位仍然存在。

如果您想可靠地截断小 8 位之后的所有数字,您需要像这样对其进行位掩码:

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

编辑:

我怀疑这与运营商的数字促销活动有关。 ^ 运算符在应用于 byte ushortint 时,会将两个操作数提升为 int,因为存在从第一个操作数的两种可能类型到 int 的隐式转换。似乎正在发生的事情是 explicitushortbyte 的转换被跳过,这会导致截断。现在你只有两个 ints,当它们进行异或运算时,然后截断回 ushort,保留它们的高阶位。