逆向工程String.GetHashCode

String.GetHashCode的行为取决于程序架构。 因此它将在x86中返回一个值,在x64上返回一个值。 我有一个必须在x86中运行的测试应用程序,它必须预测必须在x64上运行的应用程序的哈希码输出。

下面是mscorwks中String.GetHashCode实现的反汇编。

public override unsafe int GetHashCode() { fixed (char* text1 = ((char*) this)) { char* chPtr1 = text1; int num1 = 0x15051505; int num2 = num1; int* numPtr1 = (int*) chPtr1; for (int num3 = this.Length; num3 > 0; num3 -= 4) { num1 = (((num1 <≫ 0x1b)) ^ numPtr1[0]; if (num3 <= 2) { break; } num2 = (((num2 <> 0x1b)) ^ numPtr1[1]; numPtr1 += 2; } return (num1 + (num2 * 0x5d588b65)); } } 

任何人都可以将此function移植到安全的实现吗?

哈希代码不能跨平台重复,甚至不能在同一系统上多次运行同一程序。 你走错了路。 如果你不改变路线,那么你的道路将很艰难,有一天它可能会以泪水结束。

你想要解决的真正问题是什么? 是否可以编写自己的哈希函数,作为扩展方法或包装类的GetHashCode实现,并使用它?

首先,乔恩是正确的; 这是一个傻瓜的差事。 我们用来“吃我们自己的dogfood”的框架的内部调试版本每天都会改变哈希算法,以防止人们构建系统 – 甚至是测试系统 – 依赖于不可靠的实现细节,这些细节被记录为可能会发生变化随时。

我的建议是退后一步,问问自己为什么要尝试做一些危险的事情,而不是将系统的仿真记录为不适合仿真的系统。 这真的是一个要求吗?

其次,StackOverflow是一个技术问答网站,而不是“为我免费工作”网站。 如果你一心想做这个危险的事情,你需要有人可以将不安全的代码重写成等效的安全代码,那么我建议你聘请能为你做这件事的人。

虽然这里给出的所有警告都是有效的,但他们没有回答这个问题。 我有一种情况,其中不幸的是,GetHashCode()已被用于生产中的持久化值,我别无选择,只能使用默认的.NET 2.0 32位x86(little-endian)算法重新实现。 我重新编码没有不安全,如下所示,这似乎是有效的。 希望这有助于某人。

 // The GetStringHashCode() extension method is equivalent to the Microsoft .NET Framework 2.0 // String.GetHashCode() method executed on 32 bit systems. public static int GetStringHashCode(this string value) { int hash1 = (5381 << 16) + 5381; int hash2 = hash1; int len = value.Length; int intval; int c0, c1; int i = 0; while (len > 0) { c0 = (int)value[i]; c1 = (int)value[i + 1]; intval = c0 | (c1 << 16); hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ intval; if (len <= 2) { break; } i += 2; c0 = (int)value[i]; c1 = len > 3 ? (int)value[i + 1] : 0; intval = c0 | (c1 << 16); hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ intval; len -= 4; i += 2; } return hash1 + (hash2 * 1566083941); } 

以下内容完全重现了.NET 4.7上的默认String哈希码 (可能更早)。 这是由下面给出的哈希码:

  • String实例上的默认值: "abc".GetHashCode()
  • StringComparer.Ordinal.GetHashCode("abc")
  • 采用StringComparison.Ordinal枚举的各种String方法。
  • System.Globalization.CompareInfo.GetStringComparer(CompareOptions.Ordinal)

通过完整的JIT优化测试发布版本,这些版本谦虚地优于内置的.NET代码,并且还经过严格的unit testing,以确保与.NET行为完全等效。 请注意, x86x64有不同的版本。 您的计划通常应包括两者; 在相应的代码清单下面是一个呼叫线束,它在运行时选择适当的版本。

x86 – (.NET以32位模式运行)

 static unsafe int GetHashCode_x86_NET(int* p, int c) { int h1, h2 = h1 = 0x15051505; while (c > 2) { h1 = ((h1 << 5) + h1 + (h1 >> 27)) ^ *p++; h2 = ((h2 << 5) + h2 + (h2 >> 27)) ^ *p++; c -= 4; } if (c > 0) h1 = ((h1 << 5) + h1 + (h1 >> 27)) ^ *p++; return h1 + (h2 * 0x5d588b65); } 

x64 – (.NET以64位模式运行)

 static unsafe int GetHashCode_x64_NET(Char* p) { int h1, h2 = h1 = 5381; while (*p != 0) { h1 = ((h1 << 5) + h1) ^ *p++; if (*p == 0) break; h2 = ((h2 << 5) + h2) ^ *p++; } return h1 + (h2 * 0x5d588b65); } 

调用任一平台(x86 / x64)的线束/扩展方法:

 readonly static int _hash_sz = IntPtr.Size == 4 ? 0x2d2816fe : 0x162a16fe; public static unsafe int GetStringHashCode(this String s) { /// Note: x64 string hash ignores remainder after embedded '\0'char (unlike x86) if (s.Length == 0 || (IntPtr.Size == 8 && s[0] == '\0')) return _hash_sz; fixed (char* p = s) return IntPtr.Size == 4 ? GetHashCode_x86_NET((int*)p, s.Length) : GetHashCode_x64_NET(p); }