我有以下哈希函数,我正试图改变它,以便我可以从散列值中找到密钥。
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函数也必须遵守此条件。
有什么想法吗?谢谢。
答案 0 :(得分:8)
这应该扭转操作:
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());
}
这应该返回一个字符串,该字符串提供与原始字符串相同的哈希值,但它不太可能是完全相同的字符串。
答案 1 :(得分:3)
字符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%的信心认为这只应该做一些小改动。
答案 2 :(得分:2)
哈希函数被设计为难以或不可能反转,因此名称(可视化肉+土豆被磨碎)
答案 3 :(得分:2)
如果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 :(得分:0)
我首先要写一个result = ((result << 5) + result) + s[i];
在单独一行上执行的每一步。这将使解决更容易。然后你所要做的就是每一行的相反(也是相反的顺序)。