Реверсирование хэш-функции

У меня есть следующая хэш-функция, и я пытаюсь получить свой способ отменить ее, чтобы я мог найти ключ из хэшированного значения.

uint Hash(string s)
{
    uint result = 0;
    for (int i = 0; i < s.Length; i++)
    {
        result = ((result << 5) + result) + s[i];
    }
    return result;
}

код в C#, но я предполагаю, что это понятно.

Я знаю, что для одного хэшированного значения может быть более одного ключа, но мое намерение состоит не в том, чтобы найти их все, достаточно только одного, который удовлетворяет хэш-функции.

EDIT:

строка, которую принимает функция, формируется только от цифр 0 до 9 и символов " * "и"#", следовательно, функция Unhash также должна соблюдать эти критерии.

какие идеи? Спасибо.

5 ответов


грубая сила должна работать, если uint составляет 32 бита. Попробуйте хотя бы 2^32 строки, и одна из них, вероятно, будет хэшировать до того же значения. Должно занять всего несколько минут на современном ПК.

у вас есть 12 возможных символов, а 12^9-около 2^32, поэтому, если вы попробуете 9 символьных строк, вы, скорее всего, найдете свой целевой хэш. Я сделаю 10 символьных строк на всякий случай.

(простая рекурсивная реализация на C++, не знаю C# так хорошо)

#define NUM_VALID_CHARS 12
#define STRING_LENGTH 10
const char valid_chars[NUM_VALID_CHARS] = {'0', ..., '#' ,'*'};

void unhash(uint hash_value, char *string, int nchars) {
  if (nchars == STRING_LENGTH) {
    string[STRING_LENGTH] = 0;
    if (Hash(string) == hash_value) { printf("%s\n", string); }
  } else {
    for (int i = 0; i < NUM_VALID_CHARS; i++) {
      string[nchars] = valid_chars[i];
      unhash(hash_value, string, nchars + 1);
    }
  }
}

затем вызвать это:

char string[STRING_LENGTH + 1];
unhash(hash_value, string, 0);

Это должно отменить операции:

string Unhash(uint hash)
{
    List<char> s = new List<char>();
    while (hash != 0)
    {
        s.Add((char)(hash % 33));
        hash /= 33;
    }
    s.Reverse();
    return new string(s.ToArray());
}

Это должно возвращать строку, которая дает тот же хэш, что и исходная строка, но очень маловероятно, что это будет та же самая строка.


символы 0-9,*,# имеют значения ASCII 48-57, 42, 35 или двоичные: 00110000 ... 00111001, 00101010, 00100011

первые 5 бит этих значений отличаются, а 6-й бит всегда равен 1. Это означает, что вы можете вывести свой последний символ в цикле, взяв текущий хэш:

uint lastChar = hash & 0x1F - ((hash >> 5) - 1) & 0x1F + 0x20;

(если это не сработает, я не знаю, кто это написал)

теперь откат хэша,

hash = (hash - lastChar) / 33;

и повторите цикл, пока хэш не станет нулевым. У меня нет C# , но Я на 70% уверен, что это должно работать только с незначительными изменениями.


хэш-функции разработаны, чтобы быть трудно или невозможно обратить вспять, следовательно, имя (визуализировать мясо + картофель измельчается)


Я бы начал с написания каждого шага, который result = ((result << 5) + result) + s[i]; на отдельной строке. Это сделает решение намного проще. Тогда все, что вам нужно сделать, это противоположность каждой линии (в обратном порядке).