反转哈希(发现冲突)

时间:2018-07-31 18:45:27

标签: c++ hash brute-force

我正在尝试查找以下哈希的哈希冲突:

inline DWORD jenkins_one_at_a_time_hash(const char* value) {
    size_t len = strlen(value);
    unsigned int hash, i;
    for (hash = i = 0; i < len; ++i)
    {
        hash += tolower(value[i]);
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

我对算法了解不多,我知道哈希不能被反转,但是我只需要一个返回特定哈希值的输入字符串即可(不必是原始哈希值)。

我目前正在对输入字符串进行暴力破解,但我想知道是否可以使用除随机字符串调用该函数之外的其他方法来使暴力破解更快(我只需要一个与哈希匹配的字符串)

1 个答案:

答案 0 :(得分:1)

有几种方法可以使蛮力更快。

  1. 字符串的计算和循环可以递增。
  2. 它可以并行制作。例如,一个内核可以检查以“ a”开头的字符串,而另一个以“ b”开头的字符串,依此类推。此任务高度可并行化,并且在1,000个处理器上运行它将使其速度提高大约1,000倍。
  3. 预先预先计算所有2 32 = 4,294,967,296个逆转可能性,并在需要时简单地获取逆向詹金斯。

循环(上面列表中的选项1)可以通过以下方式完成:

using text_buf_t = std::array<char, 8>;
// std::string, unlike char[], has no '\0' as the last element.
const std::string CHARS = " !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`{|}~";

uint32_t step(char ch, uint32_t prev=0)
{
  auto hash = prev;
  hash += ch;
  hash += (hash << 10);
  hash ^= (hash >> 6);
  return hash;
}

const uint32_t finalize(uint32_t hash)
{
  hash += (hash << 3);
  hash ^= (hash >> 11);
  hash += (hash << 15);
  return hash;
}

bool brute_reverse(uint32_t target_hash, text_buf_t & buf,
                   unsigned length,
                   unsigned depth = 0, uint32_t temp_hash = 0)
{
  if (depth == length) 
  {
    auto hash = finalize(temp_hash);
    return hash == target_hash;
  }

  for (auto ch: CHARS) 
  {
    buf[depth] = ch;
    if (brute_reverse(target_hash, buf, length,
                      depth+1, step(ch, temp_hash)))
    {
      return true;
    }
  }
  return false;
}
std::string brute_reverse(uint32_t target_hash)
{
  text_buf_t buf{};
  for (int length = 0 ; length < 8 ; ++length)
  {
    if (brute_reverse(target_hash, buf, length))
      return buf.data();
  }
  return "";
}

现在,情况有多严重? brute_reverse(2380561940)运行将近10分钟才能返回" /o9c*y"。这是最难以逆转的值。在1,000个内核上运行它将使运行时间达到10毫秒。因此,使用并行化听起来很合理。

如果经常需要进行冲销,那么冲销表听起来似乎是个好主意。为了检查反向表的可行性,必须考虑其内存占用量。 2 32 哈希中的任何一个都需要反转的最长字符串有7个字符。字符串可以压缩为5个字节,因为每个字符的限制范围为69种。

整个表占用5 * 4GB == 20 GB。对于拥有32 GB RAM的用户,反向操作可以驻留在RAM中,并允许每秒数百万次哈希反转。我的旧计算机只有4 GB,因此该表位于磁盘上。从表中的随机位置获取一个值大约需要10毫秒,这与在1,000个内核上运行的蛮力相当。

如果计算机的RAM不足,则可以使用map-reduce算法的变体创建反向表。该算法像蛮力算法一样循环所有字符串,并将字符串,哈希对存储在32个1.2GB文件中(每个文件包含具有不同高位的哈希值)。生成所有组合后,reduce阶段会将每个文件转换为hash-> string的映射。任何时候都不需要超过600 MB的RAM。

这里有一些冲突统计信息(问题标题为“ 反向哈希(发现冲突)”)。

所有2 32 个反向哈希字符串的长度

len: entries    (% of total)
----------------------------
0:   1          (2.3e-08%)
1:   69         (1.6e-06%)
2:   4761       ( 0.00011%)
3:   328488     ( 0.0076%)
4:   21848661   ( 0.51%)
5:   1170033315 (27.24%)
6:   3102751727 (72.24%)
7:   274        (6.4e-06%)

创建表的运行时间:在老化的笔记本电脑上,不到2个小时。由于高碰撞率,它必须计算134,039,399,920个字符串的哈希值。每个哈希都遇到31.2085次(平均 30次冲突),直到计算出最大字符串" /o9c*y"为止。

下图显示了有多少个碰撞(重复1 =碰撞): enter image description here 该表显示,大约有2.5 * 10 8 个不同的哈希值,冲突率为26。由于很难读取Y轴上的低值,因此让我们看一下同一图形的对数刻度: 有364个哈希值只计算了一次(没有冲突),但这只是2 32 哈希值中的一小部分。另一方面,有86个不同的字符串创建了一个哈希。这是Jenkins哈希的蛮力反转期间的最高碰撞率。

注意:

  1. 如果没有将其标记为黑客工具的危险,我会在github上放置用于创建反向哈希表的代码。
  2. 要计算图中的碰撞率,我必须运行算法4次,并每次收集不同哈希范围的数据。在内存不足的笔记本电脑上,占用2 GB以上RAM的任务非常慢。