用Python生成暴力破解字典
其实暴力破解字典就是将可输入的字符进行全排列,是非常简单的方法,只不过用C语言写起来很繁琐,因此我想到了Python。
先看看怎么生成所有可输入的字符吧(不考虑中文等字符),方法很多,这里列举2种:
生成2位的全排列麻烦一些:
不过这也很简单,因为Python是动态语言,我只要动态构造一个列表解析即可:
最容易想到的方法就是改成生成器表达式,也就是把中括号改成小括号,这样瞬间就生成了,而遍历的方法仍然是用for。
不过用eval总觉得有些不好,生成的式子也很难看懂。于是切换到Python 2.6,用itertools.product()函数就行了:
当然,标准模块是C实现,所以速度仍然是比我快很多的。
此外,因为是生成1~n位的全排列,所以还得合并生成器。如果是自己写yield的,那只要再套个for循环即可;而如果是用生成器表达式,写起来就麻烦多了。
好在itertools里还有chain()和chain.from_iterable()这2个函数(后者为2.6新增)。
于是最终的函数如下:
先看看怎么生成所有可输入的字符吧(不考虑中文等字符),方法很多,这里列举2种:
chars = string.printable[:-5] # 后面5个是换行、tab等字符 chars = ''.join([chr(i) for i in xrange(32, 127)])
取下长度就发现有95个字符…生成2位的全排列麻烦一些:
p = [] for i in chars: for j in chars: p.append(i + j)
当然也可以直接用列表来生成: p = [i + j for i in chars for j in chars]
生成更多位当然也可以用更多的变量来实现,但如果位数是变量,那我就不知道得写几个了。不过这也很简单,因为Python是动态语言,我只要动态构造一个列表解析即可:
n = 3 pattern = '["' + '%%s' * n + '" %% (' + 'i%d,' * n + ')' + ' for i%d in chars' * n + ']' pattern %= tuple(range(n) * 2) p = eval(pattern)
你会发现当n大于3时,生成已经慢得要死了,而且很可能出现内存错误。因为954= 81450625,已经接近1亿了,要在内存里放那么多字符串可不容易。最容易想到的方法就是改成生成器表达式,也就是把中括号改成小括号,这样瞬间就生成了,而遍历的方法仍然是用for。
不过用eval总觉得有些不好,生成的式子也很难看懂。于是切换到Python 2.6,用itertools.product()函数就行了:
import itertools import string n = 3 itertools.product(string.printable[:-5], repeat=n)
没有2.6咋办呢,看看文档里给出的源码吧,写得比我的易懂些,但是性能会差一些(速度约为我的1/4): def product(*args, **kwds): # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 pools = map(tuple, args) * kwds.get('repeat', 1) result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod)
注意result用的是列表解析,事实上用生成器表达式更节省内存。当然,标准模块是C实现,所以速度仍然是比我快很多的。
此外,因为是生成1~n位的全排列,所以还得合并生成器。如果是自己写yield的,那只要再套个for循环即可;而如果是用生成器表达式,写起来就麻烦多了。
好在itertools里还有chain()和chain.from_iterable()这2个函数(后者为2.6新增)。
于是最终的函数如下:
import itertools import string def crackdict(max, min=1, chars=None): assert max >= min >= 1 if chars is None: import string chars = string.printable[:-5] p = [] for i in xrange(min, max + 1): p.append(itertools.product(string.printable[:-5], repeat=i)) return itertools.chain(*p) #测试一下: max, min = 4, 1 d = crackdict(max, min) print sum((1 for i in d)) print sum([95 ** i for i in xrange(min, max + 1)])
4位一共用生成了82317120个(95 + 952 + 953 + 954)字符串,时间为14.4秒左右。