-->

Parallel Brute-Force Algorithm

2019-04-13 04:21发布

问题:

So I was looking at http://codahale.com/how-to-safely-store-a-password/# and became curious how fast different hash could be bruteforced on a somewhat powerful desktop computer and was tempted to test it

Most of the algorithms I've seen though are single-threaded and it struck me that this would be a really interesting challenge in using c# 4.0 Parallel.net/Plinq extensions and concurrent structures (like ConcurrentBag and IProducerConsumer).

So my task is as follows, build the most efficient/performant bruteforce checker of a password of n-length and charset[x] using parallelization, ie generate all possible strings of a given charset and length until a match is found. Assume at least two cores and reasonable amount of ram

I'm going to give it a whirl myself, let the best man/woman win :)

EDIT

First attempt without comparing performance yet and limited scope and known password length

    char[] chars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };

    public long NrCombinations(int nrChars, int stringLength)
    {
        Func<long, int, long> power = null;
        power = (i, p) => p == 1 ? i : i * power(i, p - 1);

        return power(nrChars, stringLength);
    }


    public static bool StringArrayEquals(char[] a, char[] b)
    {
        if (a.Length != b.Length)
            return false;
        for (int i = 0; i < a.Length; i++)
        {
            if (!a[i].Equals(b[i]))
                return false;
        }
        return true;
    }

    public char[]  GenerateString(int i, int stringLength)
    {
        char[] current = new char[stringLength];
        for (int i = 0; i < stringLength; i++)
        {
            double remainder = i % this.chars.Length;   
            i = i / this.chars.Length;         
            current[i] = this.chars[(int) remainder];
        }
        return current;
    }

    public bool IsMatch(int i, char[] password)
    {
        return StringArrayEquals(GenerateString(i, password.Length), password);
    }

    private int GetMatching(string passwordString)
    {
        char[] password = passwordString.ToArray();
        int nrCombinations = (int)NrCombinations(this.chars.Length, password.Length);

        return ParallelEnumerable.Range(0, nrCombinations).WithDegreeOfParallelism(10).FirstOrDefault(i => IsMatch(i, password));

    }

Next Attempt

Using ParallelEnumerable wasnt to clever since it's restricted to int in size, you pretty soon need atleast long even though I doubt that will hold you for long with large passwords charsets. Guess you either have to go BigInt or start breaking it down somehow after that.

    public long NrCombinations(int nrChars, int stringLength)
    {
        Func<long, int, long> power = null;
        power = (i, p) => p == 1 ? i : i * power(i, p - 1);

        return power(nrChars, stringLength);
    }


    public string GenerateString(long number, int sentenceLength)
    {
        char[] current = new char[sentenceLength];
        for (int i = 0; i < sentenceLength; i++)
        {
            double remainder = number % this.chars.Length;   
            number = number / this.chars.Length;         
            current[i] = this.chars[(int) remainder];
        }
        return new string(current);
    }

    public bool IsMatch(string hash, long  i, int passwordLength)
    {
        string generated = GenerateString(i, passwordLength);
        string hashed = GetMasterHash(generated, this.site);
        return string.Equals(hashed, hash);
    }

    private string GetMatching(string hash,int passwordLength)
    {
        string result = string.Empty;
        int stringlength = passwordLength;
        long  nrCombinations = NrCombinations(this.chars.Length, stringlength);
        long x = 0;

        Parallel.For(0, nrCombinations, (i, loopState) =>
        {
            if (IsMatch(hash,i, passwordLength))
            {
                x = i;
                loopState.Stop();
                return;
            }
        }); 


        if (x > 0)
        {
            result = this.GenerateString(x, passwordLength);
        }

        return result;

    }

回答1:

Why the NrCombinations method and not just

long combinations = (long)Math.Pow(base, stringLength);

I would also recommend against int for nrCombinations because with only six characters with your base 36 alphabet you will get in trouble (36^6 > 2^31). Use long. I don't think BigInteger is needed because if you need that big numbers brute force will not be an option anyway.

I have this idea that it might be possible to speed up brute force by using a kind of De Bruijn sequence stream. Seems reasonable but I have to get back on that because I have no code to show right now.