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 tryPB3PT“!!
.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;
}