一个小型优雅的暴力破解类






4.18/5 (22投票s)
2006年12月12日
5分钟阅读

66061

787
一个返回给定子集所有可能组合的类,不使用递归。
引言
许多进行某种暴力破解并需要计算给定字符集或对象子集的所有可能组合的程序,通常会求助于递归或一些杂乱的循环机制,例如内层循环等。几年前,我在一个黑客论坛上发起了一个比赛,要求用任何语言编写代码来返回所有长度不超过 6 的字母组合。只有两个人提交了参赛作品,包括我自己。另一个解决方案仍然是使用了递归的方法。我的方法使用了其他东西,即任何字母组合都可以用一个唯一的数字来表示,这样就可以使用一个简单的计数器机制。程序运行良好,但有些静态。这篇文章是将最初的想法转化为一个小型易用、可重用的类的结果。它可能不是超级高效的(否则应该用汇编或类似语言编写),但它可以让你用几行代码编写一个简单的密码暴力破解程序……希望你喜欢它并且它很有用。任何改进都非常欢迎。
该代码还将包含在 一个即将发布的项目中。该项目将尝试捆绑有用的类,用于创建可用于安全测试的程序,例如渗透测试等…希望很快在线。
背景
整个想法是,任何字符子集的组合 - 字符数组、符号或其他任何东西 - 都可以被转换成一个代表该组合的唯一数字。也许举个例子会更清楚。
假设我们有一个数组 {"a","b","c","d"},我们想检查所有长度为 3 的可能组合。这将是 aaa, aab, aac, aad 等等,总共有 4 的 3 次方种组合。每种组合都可以转换为一个数字,如下所示。
aaa= 0, aab=1, aac =2 .... 更具体地说,我们这样做:dac = 4*4^2+1*4^1+3*4^0 (数组中的第四个元素(d=4)乘以数组的长度(4个字符=4)的长度减一的幂(3-1=2)加上数组中第一个元素乘以...以此类推)。就像,例如,5420 可以写成 5*10^3 + 4*10^2 + 2*10^1 +0*10^0。
所以现在,我们有了一种将组合转换为数字的方法,并且我们可以轻松地进行反向转换,这实际上正是我们需要的,因为我们将实现一个计数器并返回相应的字符串。这是处理此转换的类中的相应代码片段。我将该方法命名为 factoradic
,因为我后来发现 MSDN 文章中使用了类似的技术,他们称之为 factoradic
。
private string factoradic(ulong l, int power)
{
StringBuilder sb = new StringBuilder();
//set the maximum capacity of our stringbuilder object
sb.Capacity = power +1;
while (power >=0)
{
int len = this.charset.Length;//
ulong q = l / (ulong)Math.Pow((double)len,(double)power);
ulong m = l % (ulong)Math.Pow((double)len, (double)power);
sb = sb.Append(this.charset[(int)q]);
l = m;
power--;
}
return sb.ToString();
}
我们使用 StringBuilder
而不是字符串,因为它更节省内存。l
是我们要转换的数字,power
是我们要返回的字符串的长度减一(调用函数时会减一)。所以基本上,我们做的是创建一个 StringBuilder
对象,然后进入一个循环,在那里我们执行例程的运行时,通过递减幂并使用模数作为重入点,将数字转换为 char
数组中的相应位置。我知道这听起来很复杂,但请记住上面的例子(5420那个例子)。我们使用的字符集在类中声明。不幸的是,对于 Math.Pow
操作,我们必须将其转换为 double
。
既然我们能够将数字转换为字符串,我们就必须想一种方法来以一种简单而优雅的方式返回所有可能的组合。这正是枚举派上用场的时候。首先,我们让类实现 IEnumerable
。
public class Bruteforce : IEnumerable
然后我们的枚举例程看起来是这样的。
public System.Collections.IEnumerator GetEnumerator()
{
for (int x = min; x <= max;x++ )
{
ulong counter =0;
while (counter < (ulong)
Math.Pow((double)charset.Length, (double)x))
{
string a = factoradic(counter, x-1);
yield return a;
counter++;
}
}
}
这里的诀窍是在循环开始时将计数器重置为零,否则在切换暴力破解长度时,你将继续使用之前的值。所以这个循环在我们要暴力破解的最小长度和最大长度之间循环,然后生成所有可能的字符串,用它们的数字对应物表示,这只是一个从零开始到 (ulong)Math.Pow((double)charset.Length, (double)x)
的计数器,这是所有可能组合的数量。
其余代码只是一堆声明。
优化代码 (更新)
现在这段代码运行良好,但缺乏一些性能。首先,我们使用了大量的强制类型转换,这很耗费资源,而且我们在循环中声明了一些本可以在之前声明的东西(正如一些读者指出的)。此外,factoradic
方法中的逻辑可以稍作修改,这样就不再需要 Math.Pow
了。所以我们将所有声明尽可能地移到前面,并修改 factoradic
方法。我仍然使用 StringbBuilder
。修改 factoradic
方法的副作用是返回的字符串是以反向顺序进行暴力破解的,但这不成问题,因为它不会影响我们想要实现的目标。如果我们修改我们的例子并包含一个 Systems.Diagnostics.StopWatch
,我们可以衡量结果,结果是惊人的。
正如你所看到的,新代码的性能大约是旧代码的五倍。它也更优雅了;)。这是完整的最新代码(下载包含注释)。
using System;
using System.Collections;
using System.Text;
namespace Hacking
{
public class Bruteforce : IEnumerable
{
#region constructors
private StringBuilder sb = new StringBuilder();
//the string we want to permutate
public string charset = "abcdefghijklmnopqrstuvwxyz";
private ulong len;
private int _max;
public int max { get { return _max; } set { _max = value; } }
private int _min;
public int min { get { return _min; } set { _min = value; } }
#endregion
#region Methods
public System.Collections.IEnumerator GetEnumerator()
{
len = (ulong)this.charset.Length;
for (double x = min; x <= max; x++)
{
ulong total = (ulong)Math.Pow((double)charset.Length, (double)x);
ulong counter = 0;
while (counter < total)
{
string a = factoradic(counter, x - 1);
yield return a;
counter++;
}
}
}
private string factoradic(ulong l, double power)
{
sb.Length = 0;
while (power >= 0)
{
sb = sb.Append(this.charset[(int)(l % len)]);
l /= len;
power--;
}
return sb.ToString();
}
#endregion
}
}
感谢您的建议!
使用代码
代码非常简单易用。导入 C# 文件或 DLL,然后只需设置最小值和最大值,更改字符集(默认=字母表),然后开始一个 foreach
循环来遍历枚举,并在循环中执行你想要的操作。
using System;
using System.Collections.Generic;
using System.Text;
using Hacking;
namespace example
{
class Program
{
static void Main(string[] args)
{
Bruteforce b = new Bruteforce();
b.min = 2;
b.max = 4;
b.charset = "abcdefghijklmnopqrstuvwxyz0123456789";
string target= "abc1";
foreach (string result in b)
{
Console.Write(result +"\r");
if (result == target)
{
Console.WriteLine("target found:" + result);
return;
}
}
}
}
}
我们首先初始化一个新的 Bruteforce
对象。然后我们设置我们想要使用的最小和最大值以及字符集。在这个例子中,我们将遍历我们的枚举,直到遇到 "abc1" 值。试想一下,目标字符串是一个 SHA1 哈希,并且你将每个枚举值处理成一个 SHA1,那么你就有了一个简单的 SHA1 暴力破解器。
希望你能使用这个代码... 尽情享用。