给定此哈希函数,预期的输出以及输入字符串的长度,如何找到返回给定结果的输入字符串?

时间:2019-03-30 07:07:19

标签: javascript reverse-engineering brute-force hash-function

我下面有这个哈希函数。

我知道对于长度为8的输入字符串,我得到的哈希值为16530092119764772

输入字符串只能包含字符“ abcdefghijklmnop”

查找输入字符串的最佳方法是什么?

有没有一种方法可以在数学上解决问题,而无需依靠蛮力方法来找到字符串?

递归解决方案会使堆栈溢出吗?

command

以字符串“ agile”为例,它哈希为29662550362

2 个答案:

答案 0 :(得分:1)

这甚至不是真正的哈希,因为charset中没有82个字符。这更像是将字符串解析为以82为基数的数字,在该数字中您只能使用前16个符号。如果不使用浮点数,那将是完全可逆的,而浮点数对于那么大的整数是不精确的。如果您不知道为什么,简化版是循环内的操作:

g * 82 + d
只要d小于82,

对g和d的每个可能值给出不同的结果,因为g * 82和(g + 1)* 82之间有足够的空间来容纳82个不同的 d s(从0到81)。除以82,每个不同的结果都可逆回到g和d。整个值为g,其余为d。当循环中的每个操作都是可逆的时,您可以逆转整个过程。

因此,就像您可以通过一次一次将一位数字除尽的循环将数字手动转换为十进制一样,您可以将此不精确的数字转换为以82为底的数字:

const getDigits = (value, base) => {
    const result = [];
  
    while (value) {
        result.push(value % base);
        value /= base;
    }
  
    return result.reverse();
};

const getLetter = index =>
    String.fromCharCode(97 + index);

const getPreimage = value =>
    getDigits(value, 82n)
        .map(Number)
        .map(getLetter)
        .join('');

console.log(getPreimage(29662550362n));
console.log(getPreimage(16530092119764772n));

结果以“ i”开头,因为g从8而不是0开始。第二个数字也足够大而不能唯一(与agile的“哈希”相反)可以用JavaScript数字精确表示),但是如果您只是想查找任何原像,就足够了。

function hash(str) {

  let g = 8;
  let charset = "abcdefghijklmnop";

  for(let i = 0; i < str.length; i++) {
    g = (g * 82 + charset.indexOf(str[i]));
  }

  return g;

}

for (const s of ['hijackec', 'hijacked', 'hijackee', 'hijackef', 'hijackeg']) {
    console.log(s, hash(s) === 16530092119764772);
}

答案 1 :(得分:0)

您可以创建一个从8开始的递归函数,以迭代charset索引,并在当前值超过传递的哈希值时停止(返回)。

查看以下评论​​以获取更多详细信息:

const charset = 'abcdefghijklmnop';

function bruteforce(hash, base = 8, result = {value: ''}) {
  // Always multiply the previous value by 82
  base *= 82;

  for (let i = 0; i < charset.length; i++) {
    // Add the char index to the value
    value = base + i;
    // If we found the hash, append the current char and return
    if (value === hash) {
      result.value += charset[i];
      return base === 656 ? result.value : value;
    }
    // If we went past the hash, return null to mark this iteration as failed
    if (value > hash) {
      return null;
    }
    // Otherwise, attempt next level starting from current value
    value = bruteforce(hash, value, result);
    // If we found the hash from there, prepend the current char and return
    if (value === hash) {
      result.value = charset[i] + result.value;
      return base === 656 ? result.value : value;
    }
  }

  // We tried everything, no match found :(
  return null;
}

console.log(bruteforce(29662550362));