将 C 移植到 C# 时棘手的整数溢出

Tricky integer overflow when porting C to C#

我正在从反汇编的 x86 二进制文件中移植以下 C 代码:

(unsigned __int16) ~ (_WORD) crc32 ^ length * ~crc32;

这是我在 C# 中移植的代码:

(uint) (~(ushort) crc32) ^ length * ~crc32)

下面是 table 和一些计算值。

Expected   | Actual (C#)
-----------+------------
0x1082B9CB | 0x6082B9CB
0x30389AF7 | 0x20389AF7
0xD0EF1CD6 | 0xE0EF1CD6

如您所见,最后三个字节是正确的。只是第一个字节稍微移动了一点,但我看不出它与另一个字节有何不同。

关于如何修复我的 C# 代码以提供预期输出的任何想法?


示例值(C# 语法):

uint crc32 = 0x7601A9C5;
int length = 17;

它们的计算结果应为 0xD0EF1CD6,但应给出 0xE0EF1CD6


编辑: 我可能忘了提一件重要的事情。输出以十六进制表示法表示为基础原始字节。

工作代码示例:https://ideone.com/Z8eONJ

Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(checksum)));

问题出在 CRC32 实现上。看起来我反汇编的二进制文件没有否定 return 值,这在大多数 CRC32 实现中通常都是这种情况。

public static class Crc32
{
    private static readonly uint[] _table =
    {
        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
        0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
        ...
    };

    public static uint ComputeChecksum(byte[] bytes)
    {
        var crc = 0xffffffff;
        for (var i = 0; i < bytes.Length; i++)
        {
            var t = bytes[i];
            var index = (byte) ((crc & 0xff) ^ t);
            crc = (crc >> 8) ^ _table[index];
        }
        return ~crc; // or maybe return crc;
    }
}

因此,代码:

(uint) (~(ushort) crc32) ^ length * ~crc32)

简化为:

(uint) ((ushort) crc32) ^ length * crc32)

现在它是 return 的预期输出。 我花了比我愿意承认的时间更长的时间...