【问题标题】:Reversing multiplication operation that has overflowed反转已溢出的乘法运算
【发布时间】:2011-08-04 16:17:15
【问题描述】:

给定代码:

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

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

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

【问题讨论】:

  • 你可以使用 UInt64 来获得更大的范围吗?
  • 否 -- 我现在已经编辑了我的问题以表明这一点。
  • 根据我的计算,输入 300 产生 0x55555BBC。除非 C# 做奇怪的事情来溢出。
  • 是的,我将计算器设置为十六进制。它是 0x300,而不是 300。我已经编辑了我的问题。
  • 你为什么用安全标记它?

标签: c# security math overflow reverse


【解决方案1】:

你需要的是模除法,它可以用欧几里得算法的一个版本来计算。在这种情况下,结果是 768。

这是非常快的——时间 (log n)2 即使对于一个幼稚的实现也是如此。 (如果您需要处理大量数字,我可以提供更好的算法参考。)

请参阅extended Euclidean algorithm 了解如何实现此功能的草图。

【讨论】:

  • 300 是十进制的,而不是十六进制的,但这看起来很有希望。我可以在任何地方阅读这个特定版本的欧几里得算法?
  • 300 是十六进制,我的错,所以 768 是正确的!你是怎么得出这个答案的?我不能只计算 GCD(0x69D04700, 0x123456D)。
  • 在 GP 中输入Mod(1775257344,2^32)/19088749。但是您必须定义自己的运算符,从扩展的欧几里得算法开始。我在维基百科参考中进行了编辑。
  • 还有一个话题C# ModInverse function
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    好吧,您可以构造长值,其下半部分等于您的结果,而上半部分 = 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
    

    好的,我看到商(第一个除外)> 32 位。

    【讨论】:

    • 这就是我所害怕的。如果没有人能给出更好的答案,我稍后会将此标记为答案。
    • 我正要说答案的数量几乎是无限的,但经过反思,我猜只有大约 224 个。
    • 只有一个答案满足条件 (value * 0x123456D) & 0xFFFFFFFF == 0x69D04700,也就是上面提到的768。
    • 我不知道我想要开始的“*300L”。我无法在您使用时获得变量“offset”。
    【解决方案4】:

    是的,有可能。

    使用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

    【讨论】:

      【解决方案5】:

      你的功能:

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

      uint 乘以一个奇数 数(就像在所谓的unchecked 上下文中以2**64 为模的整数一样)。这样一个奇数有一个唯一的逆,模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。默认使用十进制。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 我知道操作溢出,只返回低 32 位。这只是伪代码,不是实际的生产代码。我想知道是否有一种快速的方法可以找到所有乘以已知因子的值,即使溢出也会产生特定的结果。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-21
        • 2015-08-16
        • 2015-04-11
        • 2011-03-11
        • 2015-03-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多