簡體   English   中英

逆向工程String.GetHashCode

[英]Reverse Engineering 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 << 5) + num1) + (num1 >≫ 0x1b)) ^ numPtr1[0];
                  if (num3 <= 2)
                  {
                        break;
                  }
                  num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr1[1];
                  numPtr1 += 2;
            }
            return (num1 + (num2 * 0x5d588b65));
      }
}

任何人都可以將此功能移植到安全的實現?

哈希代碼不能跨平台重復,甚至不能在同一系統上多次運行同一程序。 你走錯了路。 如果你不改變路線,那么你的道路將很艱難,有一天它可能會以淚水結束。

你想要解決的真正問題是什么? 是否可以編寫自己的哈希函數,作為擴展方法或包裝類的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代碼,並且還經過嚴格的單元測試,以確保與.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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM