在这个哈希函数之后有没有办法让原始值恢复?

时间:2014-01-01 09:28:15

标签: c++

代码:

for( int i = 0; i < 0x10; i++ ) {    
   serialvar = (((magic1 * i + serialvar) << 0x10) ^ serialvar) + 0x13371337;
   serialvar = (((i * magic1 + serialvar) >> 0x10) ^ serialvar) + 0x73317331;
}

我理解为了扭转某些东西,一切都必须按相反的顺序完成。但是,我很困惑,因为这是非常复杂的,似乎它需要一个先前的值来反转当前的值,否则将无法获得?我们正在转移比特,因此有可能永远丢失价值。 即如果转发为x + 1,则向后为x - 1

现在,如果我们有最终的serialvar值,是否可以反转这个循环以找到未知值serialvar?

4 个答案:

答案 0 :(得分:3)

是。 serialvar只有40亿个值(假设您省略了类型,但是由上下文隐含)。其中许多甚至可能不是有效的序列号。计算所有这些的哈希值,直到找到你想要的那个。

答案 1 :(得分:3)

这些类型的功能主要是单向功能。单向函数的存在似乎在直觉上是不可能的,但实际上是非常可能的。事实上,所有现代密码学都依赖于这些功能。单向函数的存在方式是因为许多值映射到相同的值,例如:

1234 -> abc
2345 -> cde
4567 -> abc

好的,现在如果你有abc,并且正在尝试备份,你去哪里,1234或4567?通过将许多vaules映射到一个,你可以阻止人们倒退。现代密码学功能在数千个步骤中完成了数百万次。 “反转”过程的唯一方法是猜测一个起点并希望你降落在正确的终点,即便如此,你也无法确定自己是否拥有正确的价值。

您的功能是这种单向函数的一个很好的简单示例。你可以试着通过猜测来“强行”它,但你不能真正“向后运行”。

编辑:

在查看此函数后,在@hvd的帮助下,如果magic1 * i不会导致溢出,则看起来这个特定函数可能是可逆的。在这种情况下,代码看起来像这样:

for ( int i = 15; i >= 0; i-- ) {
    serialvar -= 0x73317331;
    serialvar = ((i * magic1 + serialvar) >> 0x10) ^ serialvar; //doesn't work... carry
    serialvar -= 0x13371337;
    serialvar = ((magic1 * i + serialvar) << 0x10) ^ serialvar; //reverse by doing again
}

只有当magic1 * i <&lt; 2 ^ 15。如果你想要考虑magic1的所有可能值,你必须遵循一个小树,它在每个&gt;&gt;处分支一次。线条,一位被剪裁。所以一个2 ^ 16的搜索区域,实际上非常小。

答案 2 :(得分:1)

如果没有暴力强制,此功能不是完全可逆的,但您只需要“强力”一位值即可。我将首先稍微扩展一下这些操作。

for( int i = 0; i < 0x10; i++ ) {    
  serialvar ^= (magic1 * i + serialvar) << 0x10;
  serialvar += 0x13371337;
  serialvar ^= (magic1 * i + serialvar) >> 0x10;
  serialvar += 0x73317331;
}

显然,+=可以通过应用具有相同值的-=来实现。操作数是常量,所以这是微不足道的。

显然,^=可以通过再次应用具有相同值的^=来实现。但是,这里的操作数不是常数。

从第一个开始:

serialvar ^= (magic1 * i + serialvar) << 0x10;

因为操作数左移16,所以它只取表达式magic1 * i + serialvar的低16位,并且仅切换serialvar的高16位。这很简单:在此操作之后,(magic1 * i + serialvar) << 0x10仍会产生完全相同的值。

第二个更复杂:

serialvar ^= (magic1 * i + serialvar) >> 0x10;

这只改变serialvar的低16位,只取magic1 * i + serialvar的高16位,但由于加法与进位有效,所以仍取决于低16位好。您将获得(((magic1 * i) >> 16) + (serialvar >> 16) + carry) & 0xFFFF,其中carry可能为0或1。

现在,这部分 可能会导致数据丢失。如果magic1 * i534985191serialvar50712,则结果为55803。如果magic1 * i534985191,而serialvar50719,则结果也为55803。因此,有些值无法恢复原始serialvar

但是,由于您知道carry01,因此您可以尝试这两种可能性。如果您使用0而不是1获得了正确的结果,那么您就得到了答案。如果您使用1而不是0获得了正确的结果,那么您就得到了答案。如果您同时获得01的正确结果,请输入错误消息并退出。由于这是一个特制的程序,应该是可逆的,不应该发生。

(免责声明:我假设magic1serialvarunsigned int,而unsigned int有32位值。该程序可能无法移植。)

答案 3 :(得分:1)

与此处的答案相反,这是可以解决的,没有任何强制执行。

循环和添加很容易反转。剩下的就是这两个操作:

serialvar ^= (magic1 * i + serialvar) << 0x10;
serialvar ^= (i * magic1 + serialvar) >> 0x10;

让我们将0x10低位称为低位,将0x10高位称为高位。

在第一次操作中,很容易看出低部分不受影响。但它也是此操作中唯一重要的部分,因为高部分会被移出。所以这个操作实际上是它自己的反转。

第二个操作有点复杂。这一次,高部分不受影响。除了加法i * magic1 + serialvar的低部分溢出并将总和的高部分加1时,你几乎不关心低部分。

因此,此操作始终采用以下两种形式之一:

serialvar ^= (i * magic1 + (serialvar & 0xFFFF0000)) >> 0x10; // no overflow
serialvar ^= (i * magic1 + (serialvar & 0xFFFF0000) + 0x10000) >> 0x10;

现在,这些形式中的每一个都不依赖于serialvar的低部分,因此每个形式都是它自己的反转。因此,您可以简单地尝试它们并通过应用原始操作来验证哪个是正确的。

完整代码如下:

serialvar = key;

// compute an inverse
for (int i = 0xF; i >= 0; --i) {
    serialvar -= 0x73317331;

    unsigned int serialvar0 = serialvar ^ ((i * magic1 + (serialvar & 0xFFFF0000)) >> 0x10);
    unsigned int serialvar1 = serialvar ^ ((i * magic1 + (serialvar & 0xFFFF0000) + 0x10000) >> 0x10);

    if (serialvar == (serialvar0 ^ ((i * magic1 + serialvar0) >> 0x10)))
        serialvar = serialvar0;
    else
        serialvar = serialvar1;

    serialvar -= 0x13371337;
    serialvar ^= (magic1 * i + serialvar) << 0x10;
}

cout << hex << serialvar << endl;

// verification
for (int i = 0; i < 0x10; i++) {
    serialvar = (((magic1 * i + serialvar) << 0x10) ^ serialvar) + 0x13371337;
    serialvar = (((i * magic1 + serialvar) >> 0x10) ^ serialvar) + 0x73317331;
}

if (serialvar == key)
    cout << "success" << endl;

如果两项检查都成功,可能会出现多次反转,但我没有考虑到这一点,因为它并非真的有必要。