我做了一个非常简单的暴力密码破解程序(我知道这很糟糕),我有一个运行的线程,它输出每秒有多少组合,以及它尝试了多少组合。
它开始瞬间开始每秒2000组合,然后迅速下降到150左右,然后在一小时左右后慢慢减少到1。
这是我运行的主题:
Thread thread = new Thread(new ThreadStart(UpdateGuessCount));
thread.Start();
tring currentGuess = "a";
while (currentGuess != password)
{
currentGuess = Shift(currentGuess);
guesses++;
guessesPerSecond++;
}
static void UpdateGuessCount()
{
while (true)
{
mre.WaitOne();
Console.Clear();
Console.WriteLine("Current guesses: " + guesses);
Console.WriteLine("Current guesses per second: " + guessesPerSecond);
guessesPerSecond = 0;
Thread.Sleep(1000);
}
}
“Shift”方法更改组合,向上移动。例如,Shift(“a”)将变为“b”,依此类推。
为什么会发生这种情况,有什么办法可以阻止它发生/加快速度?
提前致谢!
编辑:
转换源代码:
static string Shift(string toShift)
{
char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
char[] shiftChars = new char[toShift.Length + 1];
Array.Copy(toShift.ToCharArray(), shiftChars, toShift.Length);
shiftChars[shiftChars.Length - 1] = ' ';
int index = 0;
char chr = shiftChars[index];
if (chr == '0')
{
while (chr == '0')
{
shiftChars[index] = 'a';
index++;
chr = shiftChars[index];
}
if (shiftChars[index] != ' ')
{
int charIndex = 0;
foreach (char c in chars)
{
if (c == chr)
{
shiftChars[index] = chars[charIndex + 1];
}
charIndex++;
}
}
else
{
shiftChars[index] = 'a';
}
}
else
{
int charIndex = 0;
foreach (char c in chars)
{
if (c == chr)
{
shiftChars[index] = chars[charIndex + 1];
}
charIndex++;
}
}
string returnString = "";
foreach (char c in shiftChars)
{
returnString = returnString + c;
}
return returnString;
}
答案 0 :(得分:1)
以下程序维护您的原始算法,并在我的笔记本电脑上以~20M比较/秒运行,并且不会随着时间的推移而降级。我和原版的速度相同~2k / sec,并且快速降级。
您的实施的核心问题是过多的字符串分配导致了大量的GC活动。罪魁祸首是Shift()方法中的以下行:
char[] shiftChars = new char[toShift.Length + 1];
在每次调用Shift()时,它会增加猜测字符串的长度。在20k比较之后,你有一个20k长的猜测字符串,只使用了前几个字符。
Shift()也将字符串作为参数并返回字符串。这意味着每次转移呼叫的分配。您已经使用char []来完成工作,所以只需接受并返回它们即可。这样我们就可以预先分配猜测缓冲区,只需移动里面的字符。
static string Shift(string toShift);
替换为:
static void Shift(ref char[] shiftChars, ref int tailIndex)
根据您想要转移的值,您可以循环查找当前索引。我创建了一个以“'值”为基础的地图。这会将下一个索引返回到chars []数组中。所以给出了' D'地图返回' E'的索引。可能有更好的方法,但这节省了大量的处理时间。
...
int charIndex = 0;
foreach (char c in chars)
{
if (c == chr)
{
shiftChars[index] = chars[charIndex + 1];
}
charIndex++;
}
...
替换为:
shiftChars[index] = chars[charsLookupNext[chr]];
VC中的CPU诊断在Shift()中显示65%的时间,在checkBuffers()中显示30%。我知道checkBuffers()很快,因此很适合Shift()。
using System;
using System.Collections.Generic;
using System.Threading;
namespace PwdCrackPerf
{
class Program
{
private static UInt64 guesses = 0;
private static char[] currentGuessBuffer;
private static readonly char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
private static readonly Dictionary<char,int> charsLookupNext = new Dictionary<char, int>() {{'a',1}, {'b',2}, {'c',3}, {'d',4}, {'e',5}, {'f',6}, {'g',7},
{ 'h',8}, {'i',9}, {'j',10}, {'k',11}, {'l',12}, {'m',13}, {'n',14}, {'o',15}, {'p',16}, {'q',17}, {'r',18}, {'s',19}, {'t',20}, {'w',21}, {'x',22}, {'y',23},
{ 'z',24}, {'A',25}, {'B',26}, {'C',27}, {'D',28}, {'E',29}, {'F',30}, {'G',31}, {'H',32}, {'I',33}, {'J',34}, {'K',35}, {'L',36}, {'M',37}, {'N',38}, {'O',39},
{ 'P',40}, {'Q',41}, {'R',42}, {'S',43}, {'T',44}, {'W',45}, {'X',46}, {'Y',47}, {'Z',48}, {'1',49}, {'2',50}, {'3',51}, {'4',52}, {'5',53}, {'6',54}, {'7',55},
{ '8',56}, {'9',57}, {'0',58}};
private static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
static void Main(string[] args)
{
var thread = new Thread(new ThreadStart(UpdateGuessCount));
thread.Start();
var passwordBuffer = args[0].ToCharArray();
currentGuessBuffer = new char[passwordBuffer.Length];
currentGuessBuffer[0] = chars[0];
var tailIndex = 0;
while ( !checkBuffers(passwordBuffer, currentGuessBuffer))
{
Shift(ref currentGuessBuffer, ref tailIndex);
guesses++;
}
cancellationTokenSource.Cancel();
Console.WriteLine($"Password: {new string(currentGuessBuffer)}");
Console.ReadKey();
return;
}
static bool checkBuffers(char[] charBuffer1, char[] charBuffer2)
{
var index = 0;
while ((charBuffer1[index] == charBuffer2[index]) && ++index < charBuffer1.Length) ;
return index == charBuffer1.Length;
}
static void Shift(ref char[] shiftChars,ref int tailIndex)
{
int index = 0;
char chr = shiftChars[index];
if (chr == chars[chars.Length-1])
{
while (chr == chars[chars.Length-1])
{
shiftChars[index++] = chars[0];
if (index < shiftChars.Length)
chr = shiftChars[index];
else break;
}
if (index <= tailIndex)
shiftChars[index] = chars[charsLookupNext[chr]];
else if(tailIndex+1<shiftChars.Length)
shiftChars[++tailIndex] = chars[0];
}
else
shiftChars[index] = chars[charsLookupNext[chr]];
}
static void UpdateGuessCount()
{
UInt64 lastGuessCount=0;
while (!cancellationTokenSource.Token.IsCancellationRequested)
{
//Just for ballpark
Console.Clear();
Console.WriteLine($"Current guesses: {guesses}");
Console.WriteLine($"Current guesses per second: {guesses-lastGuessCount}");
Console.WriteLine($"Current guess:{new string(currentGuessBuffer)}");
lastGuessCount = guesses;
Thread.Sleep(1000);
}
}
}
}