Обратный инжиниринг String.GetHashCode
Поведение String.GetHashCode зависит от архитектуры программы. Таким образом, он вернет одно значение в x86 и одно значение в x64. У меня есть тестовое приложение, которое должно работать в x86, и оно должно предсказать вывод хеш-кода из приложения, которое должно работать в x64.
Ниже приведена разборка реализации String.GetHashCode из mscorwks.
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));
}
}
Кто-нибудь может портировать эту функцию на безопасную реализацию??
4 ответа
Хеш-коды не предназначены для повторения на разных платформах или даже для нескольких запусков одной и той же программы в одной и той же системе. Вы идете не в ту сторону. Если вы не измените курс, ваш путь будет трудным, и однажды он может закончиться слезами.
Какую реальную проблему вы хотите решить? Можно ли написать свою собственную хеш-функцию, либо как метод расширения, либо как GetHashCode
реализация класса-обертки и использовать его вместо этого?
Во-первых, Джон прав; это дурацкое поручение. Внутренние отладочные сборки фреймворка, который мы используем, чтобы "съесть свою собачью еду", ежедневно меняют алгоритм хэширования, чтобы не допустить людей к созданию систем - даже систем тестирования - которые полагаются на ненадежные детали реализации, которые задокументированы как подлежащие изменению в любое время.
Вместо того, чтобы закреплять эмуляцию системы, которая задокументирована как непригодная для эмуляции, я рекомендую сделать шаг назад и спросить себя, почему вы пытаетесь сделать что-то столь опасное. Это действительно требование?
Во-вторых, Stackru - это сайт технических вопросов и ответов, а не сайт "делай мою работу для меня бесплатно". Если вы одержимы этой опасной вещью и вам нужен кто-то, кто может переписать небезопасный код в эквивалентный безопасный код, тогда я рекомендую вам нанять того, кто может сделать это для вас.
Хотя все предупреждения, приведенные здесь, действительны, они не отвечают на вопрос. У меня была ситуация, в которой GetHashCode(), к сожалению, уже использовался в качестве постоянного значения в рабочей среде, и у меня не было выбора, кроме как повторно реализовать его, используя стандартный 32-разрядный алгоритм x86 (little-endian).NET 2.0. Я перекодировал без небезопасных, как показано ниже, и это, кажется, работает. Надеюсь, это кому-нибудь поможет.
// 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);
}
Следующее точно воспроизводит значение по умолчанию String
хэш-коды на .NET 4.7 (и, возможно, раньше). Это хеш-код, заданный:
- По умолчанию на
String
пример:"abc".GetHashCode()
StringComparer.Ordinal.GetHashCode("abc")
- Различный
String
методы, которые принимаютStringComparison.Ordinal
перечисление.System.Globalization.CompareInfo.GetStringComparer(CompareOptions.Ordinal)
При тестировании сборок выпусков с полной JIT-оптимизацией эти версии скромно опережают встроенный код.NET, а также прошли тщательное модульное тестирование на точную эквивалентность с .NET
поведение. Обратите внимание, что существуют отдельные версии для x86 и x64. Ваша программа, как правило, должна включать в себя оба; ниже соответствующих списков кодов находится система вызова, которая выбирает соответствующую версию во время выполнения.
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);
}