增加一个字节数组

11

我有一个byte[] testKey = new byte[8];

这显然从一开始就所有字节都是0。 我想遍历所有字节,在每次循环迭代时将其增加1,以便最终通过字节数组的所有可能性。 我还希望尽可能快地完成此操作。 是的,我正在尝试编写暴力破解程序。

更新:我已经让不安全方法工作,并且它是最快的。 但是,根据我的计算,使用.Net DESCryptoServiceProvider对每个密钥执行DES加密并遍历需要76000000年。 10000次加密需要1.3秒。非常感谢所有对最无用问题提供的精彩答案!


6
测试所有2^64种组合需要很长时间。 - Jonas Elfström
仅循环遍历组合就需要大约七年的时间,如果您对每个组合都进行实际操作,则需要更长时间。暴力破解似乎不是这个问题的正确方法... - Guffa
除非你拥有政府机构或大型企业的计算资源,否则你没有机会彻底测试一个大小为2^64的搜索空间。即使每个测试在现代CPU上只需要一个周期运行,也需要71000年才能完成对整个搜索空间的测试。 - Stephen Canon
@stephentyrone:你是如何得出71000年这个数字的?2 ^ 64 / 3E9 / 3600 / 24 / 365约为195年。 - Guffa
抱歉,我的意思是71,000天 =) - Stephen Canon
11个回答

13

顺便提一句,要检查2^64个选项需要大量的处理...

嗯,最快的方法可能是直接使用Int64(也称为long)或UInt64ulong),并使用++?你真的需要byte[]吗?

作为一个巧妙的替代方案,怎么样:

Array.Clear(data, 0, data.Length);
while (true)
{
  // use data here
  if (++data[7] == 0) if (++data[6] == 0)
    if (++data[5] == 0) if (++data[4] == 0)
      if (++data[3] == 0) if (++data[2] == 0)
        if (++data[1] == 0) if (++data[0] == 0) break;
}

我所能想到的另一种方法是使用不安全的代码,通过将数组视为int64来访问它...有些混乱。

unsafe static void Test() {
    byte[] data = new byte[8];
    fixed (byte* first = data) {
        ulong* value = (ulong*)first;
        do {
            // use data here
            *value = *value + 1;
        } while (*value != 0);
    }
}

我认为在内存中修复数组以在不安全代码中使用它的开销大于将其视为 int64 的好处。 - Guffa
你应该在不安全代码中使用ulong,而不是uint。即使你在fixed语句内循环,它的速度仍然非常慢。我的托管代码增加数组的速度大约快了200倍... - Guffa

7

这是如何增加数组中的值:

int index = testKey.Length - 1;
while (index >= 0) {
   if (testKey[index] < 255) {
      testKey[index]++;
      break;
   } else {
      testKey[index--] = 0;
   }
}

当代码执行到这里时,如果index的值为-1,则说明你已经迭代了所有的组合。

相比于使用BitConverter,这种方法会稍微快一些,因为它不会在每次迭代时创建一个新的数组。

编辑:
进行了一项小型性能测试,结果表明这种方法比使用BitConverter快大约1400倍...


我点赞是因为我看到这里有潜力。问题在于break语句放错了位置。按照现在的代码,它不会超过1。 - makerofthings7
@makerofthings7:感谢您的点赞。我认为您误解了代码。它将使数组增加一个元素。要再增加一个元素,您需要再次使用该代码。如果您想在循环中使用它,您可以将代码放在循环中。 - Guffa
哇,太快了!我在4.2秒内就达到了10亿! - makerofthings7
虽然@Marc上面的不安全代码在相同的迭代次数下计时为2.7秒。 - makerofthings7

4

这是一个非常好的问题!以下是一种不使用不安全代码的方法:

public struct LongAndBytes
{
    [FieldOffset(0)]
    public ulong UlongValue;
    [FieldOffset(0)]
    public byte Byte0;
    [FieldOffset(1)]
    public byte Byte1;
    [FieldOffset(2)]
    public byte Byte2;
    [FieldOffset(3)]
    public byte Byte3;
    [FieldOffset(4)]
    public byte Byte4;
    [FieldOffset(5)]
    public byte Byte5;
    [FieldOffset(6)]
    public byte Byte6;
    [FieldOffset(7)]
    public byte Byte7;

    public byte[] ToArray()
    {
        return new byte[8] {Byte0, Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7};
    }
}


// ...

    LongAndBytes lab = new LongAndBytes();

    lab.UlongValue = 0;
    do {
        // stuff
        lab.UlongValue++;
    } while (lab.ULongValue != 0);

Byte0到Byte7中的每个成员都重叠在ulong上并共享其成员。它不是一个数组 - 我尝试过对此进行调整,但结果不尽如人意。我敢打赌有人知道使这种情况发生的魔术声明。我可以为P/Invoke做到这一点,但不能将其用作.NET中的数组,因为数组是一个对象。


1
byte[] b = new byte[8] {Byte0, Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7}; - Chris
+1 点赞,我喜欢这个答案,简单而又强大。 - Scott Lance
for循环将会错过最后一组。你需要一个不同的循环,在增加值之后检查它是否再次达到了零。 - Guffa

3

byte[8]本质上是一个ulong,但如果你确实需要它是byte[8],你可以使用以下代码:

byte[] bytes = new byte[8];
ulong i = 0;
bytes = BitConverter.GetBytes(i);

2
您可以使用位运算符提取字节:
byte[] bytes = new byte[8];
for (ulong u = 0; u < ulong.MaxValue; u++)
{
    bytes[0] = (byte)(u & 0xff);
    bytes[1] = (byte)((u >> 8) & 0xff);
    bytes[2] = (byte)((u >> 16) & 0xff);
    bytes[3] = (byte)((u >> 24) & 0xff);
    bytes[4] = (byte)((u >> 32) & 0xff);
    bytes[5] = (byte)((u >> 40) & 0xff);
    bytes[6] = (byte)((u >> 48) & 0xff);
    bytes[7] = (byte)((u >> 56) & 0xff);
    // do your stuff...
}

这种方法不太“hackish”,因为它首先操作一个无符号64位整数,然后提取字节。但是要小心CPU的字节序。


1
for (UInt64 i = 0; i < UInt64.MaxValue; i++)
{
    byte[] data = BitConverter.GetBytes(i)
}

每次调用都会创建一个新的 byte[],这会造成大量的分配和垃圾回收工作;有更快的方法可以使用固定数组。 - Marc Gravell
这个循环要么立即完成,要么在公元7854年的春天完成。 - Jonas Elfström
仅仅通过循环遍历所有组合,使用我回答中更快的方法需要大约七年时间。而使用BitConverter则需要大约10000年… - Guffa
要在7年内完成,您需要每毫秒处理83506006个2的64次方除以(7365.252436001000)。真的吗? - Jonas Elfström

1
byte[] array = new byte[8];
int[] shifts = new int[] { 0, 8, 16, 24, 32, 40, 48, 56 };    
for (long index = long.MinValue; index <= long.MaxValue; index++)
{
    for (int i = 0; i < 8; i++)
    {
        array[i] = (byte)((index >> shifts[i]) & 0xff);
    }
    // test array
}

1
for (int i = 0; i < bytes.Length & 0 == ++bytes[i]; i++);

应该和不安全的方法一样快,并允许任意大小的数组。

可能是最好的答案。没有任何黑科技,简单快捷。 - Thorham

0

简单迭代:

static IEnumerable<byte[]> Iterate(int arrayLength) {
    var arr = new byte[arrayLength];
    var i = 0;
    yield return arr;
    while (i < arrayLength)
    {
        if (++arr[i] != 0)
        {
            i = 0;
            yield return arr;
        }
        else i++;
    }
}

static void Main(string[] args)
{
    foreach (var arr in Iterate(2))
    {
        Console.Write(String.Join(",", arr.Select(x => $"{x:D3}")));
        Console.WriteLine();
    }
}

0

抱歉晚发文,但我也需要所描述的功能,并且我认为以相对简单的方式实现了它。也许这对其他人也有用:

private byte[] IncrementBytes(byte[] bytes)
{
    for (var i = bytes.Length - 1; i >= 0; i--)
    {
        if (bytes[i] < byte.MaxValue)
        {
            bytes[i]++;
            break;
        }
        bytes[i] = 0;
    }
    return bytes;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接