【问题标题】:Generating every character combination up to a certain word length生成每个字符组合,直到某个字长
【发布时间】:2010-09-03 23:11:04
【问题描述】:

我将在几周后为我的计算机和信息安全课程做一个安全演示,在这个演示中,我将展示不同攻击(字典、彩虹和蛮力)的优缺点。我可以很好地执行字典和彩虹攻击,但我需要即时生成蛮力攻击。我需要找到一种算法,让我可以循环遍历字母、符号和数字的每个组合,直到达到一定的字符长度。

因此,例如,对于 12 的字符长度,第一代和最后几代将是:

a
ab
abc
abcd
...
...
zzzzzzzzzzzx
zzzzzzzzzzzy
zzzzzzzzzzzz

但它也会使用数字和符号,所以我很难解释......但我想你明白了。只使用 ASCII 表中的符号就可以了。

我可以用一个 ASCII 函数来用一个计数器来做这件事,但我就是想不出来。如果有人能提供一些源代码(我可能会使用 C#)甚至是一些我可以编写函数的伪代码,那就太好了。

提前谢谢你。 :)

【问题讨论】:

  • 我猜你已经发现了暴力攻击的缺点之一 :)

标签: c# random brute-force


【解决方案1】:

一个递归函数可以让你遍历 ValidChars 的所有组合:

    int maxlength = 12;
    string ValidChars;
    private void Dive(string prefix, int level)
    {
        level += 1;
        foreach (char c in ValidChars)
        {
            Console.WriteLine(prefix + c);
            if (level < maxlength)
            {
                Dive(prefix + c, level);
            }
        }
    }

将有效字符集分配给 ValidChars,即您想要 maxlength 的最大字符串长度,然后调用 Dive("", 0); 就可以了。

【讨论】:

  • 你的代码和我的很相似...我会尝试做一个比较以寻求一些优化
  • @Andrea:这些套路在性能长杆方面是相同的。字符串 concat 将花费比递归函数传递一个或两个参数更多的时间。
  • @Andrea:我们的答案之间的主要区别在于我比你早 10 分钟完成了我的答案。 ;>
  • 我不想竞争,只是想研究最好的解决方案:我发现 1)我没有在 start 调用和 2)你的代码返回字符串 1 char 之间重置秒表大于指定的:你写的地方:if (level
  • @Andrea:你的不太灵活,但正确。我将
【解决方案2】:

您需要从一组有效字符中生成所有字符组合;我们称这个集合为validChars。基本上,每组长度为 N 的组合是validChars 与其自身 N 次的笛卡尔积。使用 Linq 很容易做到这一点:

char[] validChars = ...;

var combinationsOfLength1 =
    from c1 in validChars
    select new[] { c1 };

var combinationsOfLength2 =
    from c1 in validChars
    from c2 in validChars
    select new[] { c1, c2 };

...

var combinationsOfLength12 =
    from c1 in validChars
    from c2 in validChars
    ...
    from c12 in validChars
    select new[] { c1, c2 ... c12 };

var allCombinations =
    combinationsOfLength1
    .Concat(combinationsOfLength2)
    ...
    .Concat(combinationsOfLength12);

显然,您不想为每个长度手动编写代码,特别是如果您事先不知道最大长度...

Eric Lippert an article 关于生成任意数量序列的笛卡尔积。使用文章提供的CartesianProduct扩展方法,可以生成长度为N的所有组合如下:

var combinationsOfLengthN = Enumerable.Repeat(validChars, N).CartesianProduct();

由于您想要从长度 1 到 MAX 的所有组合,您可以这样做:

var allCombinations = 
    Enumerable
        .Range(1, MAX)
        .SelectMany(N => Enumerable.Repeat(validChars, N).CartesianProduct());

allCombinations 是一个IEnumerable&lt;IEnumerable&lt;char&gt;&gt;,如果你想得到一个字符串序列的结果,你只需要添加一个投影:

var allCombinations = 
    Enumerable
        .Range(1, MAX)
        .SelectMany(N => Enumerable.Repeat(validChars, N).CartesianProduct())
        .Select(combination => new string(combination.ToArray()));

请注意,它肯定不是最有效的解决方案,但至少它简短易读……

【讨论】:

  • new string(combination.ToArray()) 是否与 .Net 4 中的 String.Join("", combination) 相同?
  • 不一样,但是结果是一样的。我考虑过使用 String.Join,但是使用空分隔符看起来很奇怪,而且我认为使用 String 构造函数的性能更好,因为我们已经有了一个字符序列
  • 文章拉取之前,这个答案需要包含使用的扩展方法(有多种),以及如何在这个答案有用之前将其添加到项目中。
  • @vapcguy 只有一种非标准扩展方法(CartesianProduct,来自文章),其余的都包含在 Linq 中。关于“如何将它添加到项目中”......好吧,我不打算在 SO 答案中完成完整的 C# 开发课程。
  • 不是我的意思。提供扩展方法并说创建一个类并将“this”粘贴到其中并不需要“完整的 C# 开发课程”。你写了这么多……把它带到箍上。没有方法,答案是行不通的,他们在网站上提供的方法也不能像书面的那样工作。而且,正如您应该知道的那样,有一项政策反对仅链接的答案,其中帖子不能与链接分开。
【解决方案3】:

你可以试试这个代码,它使用递归打印所有可能的字符串 0 到 stringsLenght 字符长度,由从 firstRangeChar 到 lastRangeChar 的所有字符组合组成。

class BruteWriter
{
    static void Main(string[] args)
    {
        var bw = new BruteWriter();
        bw.WriteBruteStrings("");
    }

    private void WriteBruteStrings(string prefix)
    {
        Console.WriteLine(prefix);
        if (prefix.Length == stringsLenght)
            return;

        for (char c = firstRangeChar; c <= lastRangeChar; c++)
            WriteBruteStrings(prefix + c);

    }

    char firstRangeChar='A';
    char lastRangeChar='z';
    int stringsLenght=10;


}

这看起来比@dthorpe 的解决方案更快。我已经使用此代码比较了算法:

class BruteWriter
    {
        static void Main(string[] args)
        {
            var st = new Stopwatch();
            var bw = new BruteWriter();
            st.Start();
            bw.WriteBruteStrings("");
            Console.WriteLine("First method: " + st.ElapsedMilliseconds);

            for (char c = bw.firstRangeChar; c <= bw.lastRangeChar; c++)
                bw.ValidChars += c;

            st.Start();
            bw.Dive("", 0);
            Console.WriteLine("Second method: " + st.ElapsedMilliseconds);

            Console.ReadLine();


        }

        private void WriteBruteStrings(string prefix)
        {
            if (prefix.Length == stringsLenght)
                return;

            for (char c = firstRangeChar; c <= lastRangeChar; c++)
                WriteBruteStrings(prefix + c);

        }

        char firstRangeChar='A';
        char lastRangeChar='R';
        int stringsLenght=5;



        int maxlength = 5;
        string ValidChars;
        private void Dive(string prefix, int level)
        {
            level += 1;
            foreach (char c in ValidChars)
            {
                if (level <= maxlength)
                {
                    Dive(prefix + c, level);
                }
            }
        }
    }

并且,在我的电脑上,我得到了这些结果:

First method: 247
Second method: 910

【讨论】:

  • “这看起来比@dthorpe 的解决方案更快”:好吧,你只是忘记在两次测试之间重置秒表,所以显然最后一个测试的方法总是看起来更慢;)跨度>
【解决方案4】:
public void BruteStrings(int maxlength)
{
   for(var i=1;i<i<=maxlength;i++)
      BruteStrings(Enumerable.Repeat((byte)0,i));

}

public void BruteStrings(byte[] bytes)
{
   Console.WriteLine(bytes
                       .Cast<char>()
                       .Aggregate(new StringBuilder(), 
                          (sb,c) => sb.Append(c))
                       .ToString());

   if(bytes.All(b=>b.MaxValue)) return;

   bytes.Increment();

   BruteStrings(bytes);
}

public static void Increment(this byte[] bytes)
{
   bytes.Last() += 1;

   if(bytes.Last == byte.MinValue)
   {
      var lastByte = bytes.Last()
      bytes = bytes.Take(bytes.Count() - 1).ToArray().Increment();
      bytes = bytes.Concat(new[]{lastByte});
   }
}

【讨论】:

  • 你测试了吗?我不认为Cast&lt;char&gt;() 会起作用,正如here 解释的那样
  • 没有测试。如果 Cast() 不起作用,则可以在 Aggregate 函数中转换字节,或者按照其他线程的建议在 Select(b=>(char)b) 中转换字节。
【解决方案5】:

我做的另一种选择,返回一个字符串。

我不关心这个东西的性能,因为它不是针对现实世界的场景。

private void BruteForcePass(int maxLength)
    {
        var tempPass = "";
        while (tempPass.Length <= maxLength)
        {
            tempPass = GetNextString(tempPass);//Use char from 32 to 256
            //Do what you want
        }
    }

    private string GetNextString(string initialString, int minChar= 32, int maxChar = 256)
    {
        char nextChar;
        if (initialString.Length == 0)
        {
            nextChar = (char)minChar;//the initialString Length will increase
        }
        else if (initialString.Last() == (char)maxChar)
        {
            nextChar = (char)minChar;
            var tempString = initialString.Substring(0, initialString.Length -1);//we need to increment the char just before the last one
            initialString = GetNextString(tempString, minChar, maxChar); 
        }
        else
        {
            nextChar = (char)(initialString.Last() + 1);//Get the lash Char and increment it;
            initialString= initialString.Remove(initialString.Length - 1);//Remove the last char.
        }
        return initialString + nextChar;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 2021-11-14
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多