C 反向詹金斯';一次一个散列

C 反向詹金斯';一次一个散列,c,hash,cryptography,C,Hash,Cryptography,如何获取与返回的哈希匹配的任何可能的字符串值 我不想获得所使用的确切密钥,只是任何一个密钥,当传递到函数中时,将返回未知密钥的相同散列 uint32_t jenkins_one_at_a_time_hash(const uint8_t* key, size_t length) { size_t i = 0; uint32_t hash = 0; while (i != length) { hash += key[i++]; ha

如何获取与返回的哈希匹配的任何可能的字符串值

我不想获得所使用的确切密钥,只是任何一个密钥,当传递到函数中时,将返回未知密钥的相同散列

uint32_t jenkins_one_at_a_time_hash(const uint8_t* key, size_t length) {
      size_t i = 0;
      uint32_t hash = 0;
      while (i != length) {
        hash += key[i++];
        hash += hash << 10;
        hash ^= hash >> 6;
      }
      hash += hash << 3;
      hash ^= hash >> 11;
      hash += hash << 15;
      return hash;
    }
uint32\u t jenkins\u一次一个\u散列(const uint8\u t*键,大小\u t长度){
尺寸i=0;
uint32\u t hash=0;
while(i!=长度){
散列+=键[i++];
哈希+=哈希>6;
}
哈希+=哈希>11;

hash+=hash如果hash函数是好的,只需尝试大量的键组合,看看hash是否匹配。这就是好的hash的要点。很难逆转

我估计,大约2^32次尝试,你会有50%的机会找到一个。下面的花了几秒钟

有了这个散列,可以走捷径

int main() {
  const char *key1 = "keynumber1";
  uint32_t match = jenkins_one_at_a_time_hash(key1, strlen(key1));
  printf("Target 0x%lX\n", (unsigned long) match);
  uint32_t i = 0;
  do {
    uint32_t hash = jenkins_one_at_a_time_hash(&i, sizeof i);
    if (hash == match) {
      printf("0x%lX: 0x%lX\n", (unsigned long) i, (unsigned long) hash);
      fflush(stdout);
    }
  } while (++i);

  const char *key2 = "\x3C\xA0\x94\xB9";
  uint32_t match2 = jenkins_one_at_a_time_hash(key2, strlen(key2));
  printf("Match 0x%lX\n", (unsigned long) match2);
}
输出

Target 0xA7AF2FFE
0xB994A03C: 0xA7AF2FFE
Match 0xA7AF2FFE

虽然蛮力方法可以很好地工作,但事实上,我们可以将其速度提高256倍左右(事实上,如果我们使用下面描述的所有优化,速度会更高)

这里的关键实现是,用于计算哈希的所有操作都是可逆的。(这是经过设计的,因为它可以确保,例如,向所有输入字符串添加相同的后缀不会增加哈希冲突的数量。)具体来说:


  • 操作
    hash+=hash如果我希望反转的值仅为ascii字符,该怎么办?然后尝试仅使用ascii字符的键。您现在是否询问如何生成随机ascii字符串?@JosephJones try
    PB3PT“!!
    .Hmm.我花了很长时间运行循环。从0到0x7FFFFFFF需要多长时间?@JosephJones 0到0xFFFF_FFFF不到5秒。请尝试“yerkn11ke1”和“SKXs0SVGGx”。您的代码有效地反转哈希的能力是哈希不加密安全的良好证据(当然,它并没有被吹捧为那样)-我怀疑这种“捷径可能适用”的逆转,但你的洞察力分析做到了!干得好,第一次紫外线。
    hash ^= (hash >> n) ^ (hash >> 2*n) ^ (hash >> 3*n) ^ (hash >> 4*n) ^ ...
    
    hash ^= hash >> n;
    hash ^= hash >> 2*n;
    hash ^= hash >> 4*n;
    hash ^= hash >> 8*n;
    // etc.
    
    uint32_t reverse_one_at_a_time_hash(const uint8_t* key, size_t length, uint32_t hash) {
      hash *= 0x3FFF8001;  // inverse of hash += hash << 15;
      hash ^= (hash >> 11) ^ (hash >> 22);
      hash *= 0x38E38E39;  // inverse of hash += hash << 3;
      size_t i = length;
      while (i > 0) {
        hash ^= (hash >> 6) ^ (hash >> 12) ^ (hash >> 18) ^ (hash >> 24) ^ (hash >> 30);
        hash *= 0xC00FFC01;  // inverse of hash += hash << 10;
        hash -= key[--i];
      }
      return hash;  // this should return 0 if the original hash was correct
    }
    
    #define TARGET_HASH 0xA7AF2FFE
    #define INPUT_LEN 4
    
    int main() {
      uint8_t buf[INPUT_LEN+1];  // buffer for guessed input (and one more null byte at the end)
      for (int i = 0; i <= INPUT_LEN; i++) buf[i] = 0;
    
      do {
        uint32_t ch = reverse_one_at_a_time_hash(buf, INPUT_LEN, TARGET_HASH);
    
        if (ch <= 255) {
          buf[0] = ch;
          // print the input with unprintable chars nicely quoted
          printf("hash(\"");
          for (int i = 0; i < INPUT_LEN; i++) {
            if (buf[i] < 32 || buf[i] > 126 || buf[i] == '"' || buf[i] == '\\') printf("\\x%02X", buf[i]);
            else putchar(buf[i]);
          }
          printf("\") = 0x%08X\n", TARGET_HASH);
          return 0;
        }
    
        // increment buffer, starting from second byte
        for (int i = 1; ++buf[i] == 0; i++) /* nothing */;
      } while (buf[INPUT_LEN] == 0);
    
      printf("No matching input of %d bytes found for hash 0x%08X. :(", INPUT_LEN, TARGET_HASH);
      return 1;
    }
    
    #define TARGET_HASH 0xA7AF2FFE
    #define MIN_INPUT_CHAR ' '
    #define MAX_INPUT_CHAR '~'
    #define INPUT_LEN 5
    
    int main() {
      uint8_t buf[INPUT_LEN+1];  // buffer for guessed input (and one more null byte at the end)
      buf[0] = buf[INPUT_LEN] = 0;
      for (int i = 1; i < INPUT_LEN; i++) buf[i] = MIN_INPUT_CHAR;
    
      do {
        uint32_t ch = reverse_one_at_a_time_hash(buf, INPUT_LEN, TARGET_HASH);
        if (ch >= MIN_INPUT_CHAR && ch <= MAX_INPUT_CHAR) {
          buf[0] = ch;
          printf("hash(\"%s\") = 0x%08X\n", buf, TARGET_HASH);
          return 0;
        }
    
        // increment buffer, starting from second byte, while keeping bytes within the valid range
        int i = 1;
        while (buf[i] >= MAX_INPUT_CHAR) buf[i++] = MIN_INPUT_CHAR;
        buf[i]++;
      } while (buf[INPUT_LEN] == 0);
    
      printf("No matching input of %d bytes found for hash 0x%08X. :(", INPUT_LEN, TARGET_HASH);
      return 1;
    }
    
    #define TARGET_HASH 0xA7AF2FFE
    #define MIN_INPUT_CHAR 'A'
    #define MAX_INPUT_CHAR 'Z'
    #define INPUT_LEN 7
    
    #define TARGET_HASH 0xA7AF2FFE
    #define MIN_INPUT_CHAR 'A'
    #define MAX_INPUT_CHAR 'Z'
    #define INPUT_LEN 7
    
    static bool find_preimage(uint32_t hash, uint8_t *buf, int depth) {
      // first invert the hash mixing step
      hash ^= (hash >> 6) ^ (hash >> 12) ^ (hash >> 18) ^ (hash >> 24) ^ (hash >> 30);
      hash *= 0xC00FFC01;  // inverse of hash += hash << 10;
    
      // then check if we're down to the first byte
      if (depth == 0) {
        bool found = (hash >= MIN_INPUT_CHAR && hash <= MAX_INPUT_CHAR);
        if (found) buf[0] = hash;
        return found;
      }
    
      // otherwise try all possible values for this byte
      for (uint32_t ch = MIN_INPUT_CHAR; ch <= MAX_INPUT_CHAR; ch++) {
        bool found = find_preimage(hash - ch, buf, depth - 1);
        if (found) { buf[depth] = ch; return true; }
      }
      return false;
    }   
    
    int main() {
      uint8_t buf[INPUT_LEN+1];  // buffer for results
      for (int i = 0; i <= INPUT_LEN; i++) buf[INPUT_LEN] = 0;
    
      // first undo the finalization step
      uint32_t hash = TARGET_HASH;
      hash *= 0x3FFF8001;  // inverse of hash += hash << 15;
      hash ^= (hash >> 11) ^ (hash >> 22);
      hash *= 0x38E38E39;  // inverse of hash += hash << 3;
    
      // then search recursively until we find a matching input
      bool found = find_preimage(hash, buf, INPUT_LEN - 1);
      if (found) {
        printf("hash(\"%s\") = 0x%08X\n", buf, TARGET_HASH);
      } else {
        printf("No matching input of %d bytes found for hash 0x%08X. :(", INPUT_LEN, TARGET_HASH);
      }
      return !found;
    }
    
      // optimization: return early if no first two bytes can possibly match
      if (depth == 1 && hash > MAX_INPUT_CHAR * 1043) return false;
    
    static bool find_preimage(uint32_t hash, uint8_t *buf, int depth) {
      // first invert the hash mixing step
      hash ^= (hash >> 6) ^ (hash >> 12) ^ (hash >> 18) ^ (hash >> 24) ^ (hash >> 30);
      hash *= 0xC00FFC01;  // inverse of hash += hash << 10;
    
      // for the lowest three levels, abort early if no solution is possible    
      switch (depth) {
        case 0:
          if (hash < MIN_INPUT_CHAR || hash > MAX_INPUT_CHAR) return false;
          buf[0] = hash;
          return true;
        case 1:
          if (hash > MAX_INPUT_CHAR * 1043) return false;
          else break;
        case 2:
          if (hash > MAX_INPUT_CHAR * 1084746) return false;
          else break;
      }
    
      // otherwise try all possible values for this byte
      for (uint32_t ch = MIN_INPUT_CHAR; ch <= MAX_INPUT_CHAR; ch++) {
        bool found = find_preimage(hash - ch, buf, depth - 1);
        if (found) { buf[depth] = ch; return true; }
      }
      return false;
    }