在.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,以为这会有所作为。
有什么想法会导致这种情况吗?
编辑:
我被要求举一个输入,输出好和输出差的例子。
这是从实际使用情况中得出的,值是从客户端发送的,并由服务器使用.NET 4.6上的“修补程序”正确解码。
输入:
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(时间戳是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
或ushort
和int
时,会将两个操作数都升级为int
,因为存在从第一个操作数到int
的两种可能类型的隐式转换。似乎正在发生的事情是跳过了从ushort
到byte
的显式转换,该转换会导致截断,因此被跳过了。现在您只有两个int
,对它们进行XOR运算后将其截断为ushort
,保留它们的高阶位。