反转已经溢出的乘法运算

时间:2011-08-04 16:17:16

标签: c# security math overflow reverse

鉴于代码:

uint Function(uint value)
{
  return value * 0x123456D;
}

输入值0x300会产生结果0x69D04700。这只是结果的低32位。 给定结果0x69D04700和因子0x123456D,是否可以检索所有数字(值* 0x123456D)& 0xFFFFFFFF = 0x69D04700快速?

编辑:我显示的代码是伪代码 - 我无法扩大返回类型。

6 个答案:

答案 0 :(得分:3)

您需要的是模块化划分,可以使用Euclid算法的版本进行计算。在这种情况下,结果是768。

这是一个非常快的时间(log n 2 ,即使对于一个天真的实现。 (如果你需要使用大数字,我可以提供更好的算法参考。)

有关如何实现此目的的草图,请参阅extended Euclidean algorithm

答案 1 :(得分:0)

好吧,你可以构造一个长值,其中低半部分等于你的结果,高半部分= 1,2,3,4,5 ...除以你的固定乘数,看看你是否得到了一个结果没有余数。通过理解数字中的模式可以略微加快这一点,这样你就可以抛出偶数值或者其他类似值。

但我认为没有明显更少的详尽方法(并且模糊地怀疑这很难做到的事实与某些加密技术有关)。

嗯......

把这一切都拿回来

import java.io.*;
public class multiply {
    public static void main(String[] argv) {
        long multiplier = 0x123456DL;
        // long result = 0x69D04700L;
        long result = (multiplier * 300L) & 0xFFFFFFFFL;
        System.out.println("New result = " + Long.toHexString(result));
        long offset = (multiplier * 300L) >> 32;
        System.out.println("New offset = " + offset);
        for (int i = 0; i < 30; i++) {
            long test = result + (((i * multiplier) + offset) << 32);
            long quotient = test / multiplier;
            long remainder = test % multiplier;
            System.out.println("Test: " + Long.toHexString(test) + " quotient: " + Long.toHexString(quotient) + " remainder: " + Long.toHexString(remainder));
        }
    }
}

结果(更正):

C:\JavaTools>java multiply
New result = 55555bbc
New offset = 1
Test: 155555bbc quotient: 12c remainder: 0
Test: 123456e55555bbc quotient: 10000012c remainder: 0
Test: 2468adb55555bbc quotient: 20000012c remainder: 0
Test: 369d04855555bbc quotient: 30000012c remainder: 0
Test: 48d15b555555bbc quotient: 40000012c remainder: 0
Test: 5b05b2255555bbc quotient: 50000012c remainder: 0
Test: 6d3a08f55555bbc quotient: 60000012c remainder: 0
Test: 7f6e5fc55555bbc quotient: 70000012c remainder: 0
Test: 91a2b6955555bbc quotient: 80000012c remainder: 0
Test: a3d70d655555bbc quotient: 90000012c remainder: 0
Test: b60b64355555bbc quotient: a0000012c remainder: 0
Test: c83fbb055555bbc quotient: b0000012c remainder: 0
Test: da7411d55555bbc quotient: c0000012c remainder: 0
Test: eca868a55555bbc quotient: d0000012c remainder: 0
Test: fedcbf755555bbc quotient: e0000012c remainder: 0
Test: 1111116455555bbc quotient: f0000012c remainder: 0
Test: 123456d155555bbc quotient: 100000012c remainder: 0
Test: 13579c3e55555bbc quotient: 110000012c remainder: 0
Test: 147ae1ab55555bbc quotient: 120000012c remainder: 0
Test: 159e271855555bbc quotient: 130000012c remainder: 0
Test: 16c16c8555555bbc quotient: 140000012c remainder: 0
Test: 17e4b1f255555bbc quotient: 150000012c remainder: 0
Test: 1907f75f55555bbc quotient: 160000012c remainder: 0
Test: 1a2b3ccc55555bbc quotient: 170000012c remainder: 0
Test: 1b4e823955555bbc quotient: 180000012c remainder: 0
Test: 1c71c7a655555bbc quotient: 190000012c remainder: 0
Test: 1d950d1355555bbc quotient: 1a0000012c remainder: 0
Test: 1eb8528055555bbc quotient: 1b0000012c remainder: 0
Test: 1fdb97ed55555bbc quotient: 1c0000012c remainder: 0
Test: 20fedd5a55555bbc quotient: 1d0000012c remainder: 0

好的,我看到商数(除了第一个)是&gt; 32位。

答案 2 :(得分:0)

如果取0x100000000并除以0x123456D,则得到224.9999(十进制)。这告诉你大约每225个数字,你会击中剩下的数字。当然,因为它不完全是225,所以你不会用整数命中余数。正如@Jacob指出的那样,在32位世界中,你只会遇到一个值(768或0x300)。所以对于这个特定的测试,答案是2 ^ 32 * X + 768,对于所有整数X> = 0。

答案 3 :(得分:0)

是的,这是可能的。

使用Chinese Remainder Theorem

你知道吗

n = 0 (mod 0x123456D)

n = 0x69D04700 (mod 0x100000000)

这些是相对素数,因为0x123456D是奇数而'0x100000000'是2的幂。所以中国剩余定理适用,它给你

n = 0x369D04700 (mod 0x123456D00000000)

这告诉您没有截断的结果是0x369D04700 + k * 0x123456D00000000。按0x123456D除以0x300 + k * 0x100000000

答案 4 :(得分:0)

你的职能:

uint Function(uint value)
{
  return value * 0x123456D;
}

乘以uint(在所谓的2**64上下文中的模数为unchecked的整数)与奇数数相乘。这样的奇数具有唯一的逆,模2**64。在这种情况下它是0xE2D68C65u,因为您可以检查(C#语法):

unchecked(0x123456Du * 0xE2D68C65u) == 1u

这种乘法是相关的和可交换的。所以你的“反向”方法是:

uint UndoFunction(uint value)
{
  return value * 0xE2D68C65u;
}

(假设unckecked上下文)。

对于任何输入xUndoFunction(Function(x))Function(UndoFunction(x))都会返回原始x

PS!为了找到模块化的逆0xE2D68C65u,我使用了.NET以外的东西。事实上,GP / PARI就像查尔斯一样。在GP中,您可以1/Mod(19088749, 2^32)Mod(19088749, 2^32)^-1。它默认使用十进制表示法。

答案 5 :(得分:-1)

不是您指定的返回类型。如果您希望能够处理64位,则必须将返回类型指定为ulong(或UInt64)。在这种情况下,C#使用严格的静态类型,如果结果无法存储在定义的类型中,则不会自动“上转换”值。即使它进行了upconvert,它也必须退回以提供合法的返回类型,因为在.NET的继承层次结构中,UInt64不是从UInt32派生的。