代码:
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?
答案 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 * i
为534985191
,serialvar
为50712
,则结果为55803
。如果magic1 * i
为534985191
,而serialvar
为50719
,则结果也为55803
。因此,有些值无法恢复原始serialvar
。
但是,由于您知道carry
为0
或1
,因此您可以尝试这两种可能性。如果您使用0
而不是1
获得了正确的结果,那么您就得到了答案。如果您使用1
而不是0
获得了正确的结果,那么您就得到了答案。如果您同时获得0
和1
的正确结果,请输入错误消息并退出。由于这是一个特制的程序,应该是可逆的,不应该发生。
(免责声明:我假设magic1
且serialvar
为unsigned 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;
如果两项检查都成功,可能会出现多次反转,但我没有考虑到这一点,因为它并非真的有必要。