2013-06-13 35 views
2

我正在编写一个程序来帮助我收集我在密码安全性方面撰写的研究报告的统计数据。当我尝试暴力破解MD5散列密码以显着提高性能时,我决定让应用程序在多个线程上运行。该应用程序在单线程上运行良好,但在2个线程正在运行的时刻,在TryPass函数中,StackOverFlowException抛出了“using(MD5 md5Hash = MD5.Create())”。仅在多线程时抛出StackOverFlowException

// Microsoft's GetMd5Hash function. 
    static string GetMd5Hash(MD5 md5Hash, string input) 
    { 
     // Convert the input string to a byte array and compute the hash. 
     byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input)); 

     // Create a new Stringbuilder to collect the bytes 
     // and create a string. 
     StringBuilder sBuilder = new StringBuilder(); 

     // Loop through each byte of the hashed data 
     // and format each one as a hexadecimal string. 
     for (int i = 0; i < data.Length; i++) 
     { 
      sBuilder.Append(data[i].ToString("x2")); 
     } 

     // Return the hexadecimal string. 
     return sBuilder.ToString(); 
    } 

    static bool TryPass(string attempt, string password) 
    { 
     using (MD5 md5Hash = MD5.Create()) 
     { 
      if (GetMd5Hash(md5Hash, attempt) == password) 
       return true; 
      else 
       return false; 
     } 
    } 



    static bool BruteForce(BruteOptions bruteOptions) 
    { 
     if (bruteOptions.prefix.Length == 1 && TryPass(bruteOptions.prefix, bruteOptions.password)) // If it's the first in a series, try it. 
      return true; 

     for (int i = 0; i < bruteOptions.chars.Length; i++) 
     { 
      if (TryPass(bruteOptions.prefix + bruteOptions.chars[i], bruteOptions.password)) 
      { 
       Console.WriteLine("The password is: " + bruteOptions.prefix + bruteOptions.chars[i]); 
       return true; 
      } 

      if (bruteOptions.prefix.Length + 1 < bruteOptions.maxLength) 
       if (BruteForce(bruteOptions)) 
        return true; 

      //Console.WriteLine(prefix + chars[i]); 
     } 

     return false; 
    } 



    public struct BruteOptions 
    { 
     public string password, prefix; 
     public char[] chars; 
     public int maxLength; 
    } 

    static void OptionBruteForce() 
    { 
     Console.WriteLine("-----------------------"); 
     Console.WriteLine("----- Brute-Force -----"); 
     Console.WriteLine("-----------------------"); 

     BruteOptions bruteOptions = new BruteOptions(); 
     bruteOptions.password = ReadString("Enter the MD5 password hash to brute-force: "); 
     bruteOptions.chars = ReadString("Enter the characters to use: ").ToCharArray(); 
     bruteOptions.maxLength = ReadIntegerRange("Max length of password: ", 1, 16); 
     bruteOptions.prefix = ""; 

     Stopwatch myStopWatch = Stopwatch.StartNew(); 

     int NUM_THREADS = bruteOptions.chars.Length; 
     Thread[] workers = new Thread[NUM_THREADS]; // Run a thread for each char. 
     var countdownEvent = new CountdownEvent(NUM_THREADS); 
     bool result = false; 

     // Start workers. 
     for (int i = 0; i < NUM_THREADS; i++) 
     { 
      int index = i; 
      BruteOptions newBruteOptions = bruteOptions; 
      newBruteOptions.prefix = bruteOptions.chars[index].ToString(); 

      workers[index] = new Thread(delegate() 
      { 
       // Also check single char. 
       if (BruteForce(bruteOptions)) 
       { 
        result = true; 

        // End all other threads. 
        for (int ii = 0; ii < NUM_THREADS; ii++) 
        { 
         if (workers[ii].ThreadState == System.Threading.ThreadState.Running && index != ii) // Ensures we don't prematurely abort this thread. 
         { 
          workers[ii].Abort(); 
          countdownEvent.Signal(); // Signal so we can zero it and continue on the UI thread. 
         } 
        } 
       } 

       // Signal the CountdownEvent. 
       countdownEvent.Signal(); 
      }); 

      workers[index].Start(); 
     } 

     // Wait for workers. 
     countdownEvent.Wait(); 

     if (!result) 
      Console.WriteLine("No Match."); 

     Console.WriteLine("Took " + myStopWatch.ElapsedMilliseconds + " Milliseconds"); 
    } 

这就是所有相关的代码。为什么会发生这种情况的任何洞察力将不胜感激!我完全被难住了。我试图在初始化每个线程时指定更大的堆栈大小,但无济于事。

在此先感谢!

回答

1

static bool BruteForce(BruteOptions bruteOptions)是无限递归的:它自称如果长度允许,使用相同的参数:

if (bruteOptions.prefix.Length + 1 < bruteOptions.maxLength) 
    if (BruteForce(bruteOptions)) 

bruteOptions保持不变,因为它是在进入函数。

作为一个解决方案,您可以使用此代码:

if (bruteOptions.prefix.Length + 1 < bruteOptions.maxLength) 
{ 
    BruteOptions newOptions = bruteOptions; 
    newOptions.prefix += bruteOptions.chars[i]; 
    if (BruteForce(newOptions)) 
     return true; 
} 

另外,你通过bruteOptions,不newBruteOptions您在主函数中使用委托。因此,实际上,您的多线程并未被使用。你的所有线程都测试相同的密码。将其更改为newBruteOptions

此外,不要假设有关线程执行顺序的任何内容。假设所有工作人员都找到了正确的密码,这可能是错误的。然后,您将在此行中得到NullReferenceException

if (workers[ii].ThreadState == System.Threading.ThreadState.Running && index != ii) // Ensures we don't prematurely abort this thread. 
+0

我不敢相信我错过了!感谢您的帮助,非常感谢您的好心!像魅力一样工作。 –

0

我猜你的单线程代码看起来有点不同。

我会失去递归。

+0

递归是计算每个可能的密码组合所必需的。除非有另一种方式? –

+1

递归总是可以用自定义堆栈循环替换。 – Aneri

+0

我该怎么办?谢谢! –

0

只是踢,这里没有递归的替代实现,使好一点使用的语言结构和可用的框架为净

class Program 
{ 
    private static string StringFromIndexPermutation(char[] characters, int[] indexes) 
    { 
     var buffer = new char[indexes.Length]; 
     for (var i = 0; i < buffer.Length; ++i) 
     { 
      buffer[i] = characters[indexes[i]]; 
     } 

     return new string(buffer); 
    } 

    /// <summary> 
    /// Increments a set of "digits" in a base "numberBase" number with the MSD at position 0 
    /// </summary> 
    /// <param name="buffer">The buffer of integers representing the numeric string</param> 
    /// <param name="numberBase">The base to treat the digits of the number as</param> 
    /// <returns>false if the number in the buffer has just overflowed, true otherwise</returns> 
    private static bool Increment(int[] buffer, int numberBase) 
    { 
     for (var i = buffer.Length - 1; i >= 0; --i) 
     { 
      if ((buffer[i] + 1) < numberBase) 
      { 
       ++buffer[i]; 
       return true; 
      } 

      buffer[i] = 0; 
     } 

     return false; 
    } 

    /// <summary> 
    /// Calculate all the permutations of some set of characters in a string from length 1 to maxLength 
    /// </summary> 
    /// <param name="characters">The characters to permute</param> 
    /// <param name="maxLength">The maximum length of the permuted string</param> 
    /// <returns>The set of all permutations</returns> 
    public static IEnumerable<string> Permute(char[] characters, int maxLength) 
    { 
     for (var i = 0; i < maxLength; ++i) 
     { 
      var permutation = new int[i + 1]; 
      yield return StringFromIndexPermutation(characters, permutation); 

      while (Increment(permutation, characters.Length)) 
      { 
       yield return StringFromIndexPermutation(characters, permutation); 
      } 
     } 
    } 

    static string ReadString(string message) 
    { 
     Console.Write(message); 
     return Console.ReadLine(); 
    } 

    private static int ReadIntegerRange(string message, int min, int max) 
    { 
     Console.Write(message + "({0} - {1})", min, max); 

     while(true) 
     { 
      var test = Console.ReadLine(); 
      int value; 

      if (int.TryParse(test, out value)) 
      { 
       return value; 
      } 
     } 

     return -1; 
    } 

    static void OptionBruteForce() 
    { 
     Console.WriteLine("-----------------------"); 
     Console.WriteLine("----- Brute-Force -----"); 
     Console.WriteLine("-----------------------"); 

     var password = ReadString("Enter the MD5 password hash to brute-force: "); 
     var chars = ReadString("Enter the characters to use: ").Distinct().ToArray(); 
     var maxLength = ReadIntegerRange("Max length of password: ", 1, 16); 

     var myStopWatch = Stopwatch.StartNew(); 

     var result = false; 
     string match = null; 
     var cancellationTokenSource = new CancellationTokenSource(); 
     var passwordBytes = Encoding.Default.GetBytes(password); 
     var originalMd5 = MD5.Create(); 
     var passwordHash = originalMd5.ComputeHash(passwordBytes); 
     var hashAlgGetter = new ConcurrentDictionary<Thread, MD5>(); 

     try 
     { 
      Parallel.ForEach(Permute(chars, maxLength), new ParallelOptions 
      { 
       CancellationToken = cancellationTokenSource.Token 
      }, test => 
      { 
       var md5 = hashAlgGetter.GetOrAdd(Thread.CurrentThread, t => MD5.Create()); 
       var data = Encoding.Default.GetBytes(test); 
       var hash = md5.ComputeHash(data); 

       if (hash.SequenceEqual(passwordHash)) 
       { 
        myStopWatch.Stop(); 
        match = test; 
        result = true; 
        cancellationTokenSource.Cancel(); 
       } 
      }); 
     } 
     catch (OperationCanceledException) 
     { 
     } 

     if (!result) 
     { 
      Console.WriteLine("No Match."); 
     } 
     else 
     { 
      Console.WriteLine("Password is: {0}", match); 
     } 

     Console.WriteLine("Took " + myStopWatch.ElapsedMilliseconds + " Milliseconds"); 
    } 

    static void Main() 
    { 
     OptionBruteForce(); 
     Console.ReadLine(); 
    } 
} 
的一部分
+0

这看起来很棒,做得很好!如果我将来再写一个,我一定要仔细看看这个,然后放手。谢谢你的加入! –

相关问题