【问题标题】:Reversing a hash function反转哈希函数
【发布时间】:2011-05-30 05:48:48
【问题描述】:

我有以下散列函数,我正在尝试用我的方法来反转它,以便我可以从散列值中找到键。

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

代码在 C# 中,但我认为它很清楚。

我知道对于一个散列值,可能有多个键,但我的目的不是找到它们,只要一个满足散列函数就足够了。

编辑:

函数接受的字符串仅由数字 0 到 9 以及字符“*”和“#”组成,因此 Unhash 函数也必须遵守此标准。

有什么想法吗?谢谢。

【问题讨论】:

  • 输入的字符串长度有限制吗?
  • 不是作业,我想限制不要超过十几个字符

标签: c# algorithm hash


【解决方案1】:

这应该反转操作:

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 和 '*' 和 '#'它的输入,见上面的编辑:)
  • +1。它对我有用。这是一个测试(需要 C# 4.0)-> Parallel.For(uint.MinValue, uint.MaxValue, currentValue =&gt; { Debug.Assert(Hash(Unhash((uint)currentValue)) == currentValue, "Test failed: " + currentValue.ToString()); });
  • @Merlyn Morgan-Graham 是的,解码是正确的,但我需要该值仅包含数字 0 到 9 以及 '*' 和 '#' 因为输入的唯一可能性是在电话上
  • @martani_net:关于您是否需要 Unhash 函数来满足其输出的这些标准,或者仅仅是您输入 Hash 的字符串会满足它们,您的问题是模棱两可的。请注意,您仍然可以在任何一种解决方案上运行我的测试。
  • @Merlyn Morgan-Graham 对,我刚刚纠正了这个问题,unhash 函数也必须满足这些标准
【解决方案2】:

字符 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% 的信心认为这应该只需要进行微小的更改。

【讨论】:

    【解决方案3】:

    如果 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);
    

    【讨论】:

      【解决方案4】:

      哈希函数被设计成难以或不可能逆转,因此得名(可视化肉+土豆被磨碎)

      【讨论】:

      • 现在每次用字典都会觉得饿!
      • 如果这是“hash”这个词的真正词源,因为它与计算机科学有关(我不知道,因为我没有学术背景)......好吧,这太棒了
      【解决方案5】:

      我将首先将result = ((result &lt;&lt; 5) + result) + s[i]; 执行的每个步骤写在单独的行上。这将使解决问题变得容易得多。然后你所要做的就是每行的相反(也以相反的顺序)。

      【讨论】:

      • 我做到了,但问题是你没有最后一个字符开头!
      • 你永远不会,所以你必须跳过它。
      猜你喜欢
      • 2020-07-04
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 2017-11-04
      • 2015-04-23
      • 1970-01-01
      相关资源
      最近更新 更多