2.3 函数和类
C、C++、Java、Ruby、Perl、Lisp……在笔者所知的编程语言之中,所有的程序都是由函数和类组成的。可以说任何程序里面包含的不是函数就是类,Pyther当然也不例外。
2.3.1 函数
曾经有一句非常出名的话是In Unix Everything Is A File,在Unix中所有的一切都是文件。在这里可以借鉴一下,In Python Everything Is A Function,在Python程序中,所有的一切都是函数。这是典型的C语言写法,把所需的功能都写成一个一个的函数,然后由函数调用函数。依次类推,最终完成整个程序的功能。
还记得上节提过的暴力破解吗?不管用什么工具,暴力破解都少不了一个合适的字典(此字典非彼字典,这里的字典指的是一个包含密码的文件,也就是一个密码集,而不是Python的变量类型)。当然网上有很多的密码字典可供下载,但它们要么太大遍历一次需要太多的时间,要么没有针对性根本就不包含所需的密码。如果已知了一些可能是密码的字符串,完全可以根据已知条件用程序编写有针对性的字典出来,这样会节省很多时间。
【示例2-13】现在来编写一个简单的程序makePasswordFileFunction.py,创建一个有针对性的专用密码字典。打开Putty连接到Linux,执行命令:
cd code/crawler vi makePasswordFileFunction.py
makePasswordFileFunction.py的代码如下:
1 #! /usr/bin/env python 2 #-*- coding: utf-8-*- 3 __author__ = 'hstking hstking@hotmail.com' 4 5 import os 6 import platform 7 import itertools 8 import time 9 10 def main(): 11 ''’主程序 ''' 12 global rawList #原始数据列表 13 rawList = [] 14 global denyList #非法单词列表 15 denyList = [' ', '', '@'] 16 global pwList #最终的密码列表 17 pwList = [] 18 global minLen #密码的最小长度 19 minLen = 6 20 global maxLen #密码的最大长度 21 maxLen = 16 22 global timeout 23 timeout = 3 24 global flag 25 flag = 0 26 run = { 27 '0':exit, #退出 28 '1':getRawList, #创建原始列表 29 '2':addDenyList, #添加不可能出现的元素 30 '3':clearRawList, #清空列表 31 '4':setRawList, #原始列表排序 32 '5':modifyPasswordLen, #修改最终的密码长度 33 '6':createPasswordList, #创建最终的字典列表 34 '7':showPassword, #显示密码 35 '8':createPasswordFile #创建密码文件 36 } 37 38 while True: 39 mainMenu() 40 op = raw_input(’输入选项:') 41 if op in map(str, range(len(run))): 42 run.get(op)() 43 else: 44 tipMainMenuInputError() 45 continue 46 47 def mainMenu(): 48 ''’主菜单 ''' 49 global denyList 50 global rawList 51 global pwList 52 global flag 53 clear() 54 print(u'||'), 55 print(u'='*40), 56 print(u'||') 57 print(u'|| 0:退出程序’) 58 print(u'|| 1:输入密码原始字符串’) 59 print(u'|| 2:添加非法字符到列表’) 60 print(u'|| 3:清空原始密码列表’) 61 print(u'|| 4:整理原始密码列表’) 62 print(u'|| 5:改变默认密码长度(%d-%d)' %(minLen, maxLen)) 63 print(u'|| 6:创建密码列表’) 64 print(u'|| 7:显示所有密码’) 65 print(u'|| 8:创建字典文件’) 66 print(u'||'), 67 print(u'='*40), 68 print(u'||') 69 print(u’当前非法字符为:%s' %denyList) 70 print(u’当前原始密码元素为:%s' %rawList) 71 print(u’共有密码%d个’ %len(pwList)) 72 if flag: 73 print(u"已在当前目录创建密码文件dic.txt") 74 else: 75 print(u"尚未创建密码文件") 76 77 def clear(): 78 ''’清屏函数 ''' 79 OS = platform.system() 80 if (OS == u'Windows'): 81 os.system('cls') 82 else: 83 os.system('clear') 84 85 def tipMainMenuInputError(): 86 ''’错误提示 ''' 87 clear() 88 print(u"只能输入0-7的整数,等待%d秒后重新输入" %timeout) 89 time.sleep(timeout) 90 91 def getRawList(): 92 ''’获取原始数据列表 ''' 93 clear() 94 global denyList 95 global rawList 96 print(u"输入回车后直接退出") 97 print(u"当前原始密码列表为:%s" %rawList) 98 st = None 99 while not st == '': 100 st = raw_input("请输入密码元素字符串:") 101 if st in denyList: 102 print(u"这个字符串是预先设定的非法字符串") 103 continue 104 else: 105 rawList.append(st) 106 clear() 107 print(u"输入回车后直接退出") 108 print(u"当前原始密码列表为:%s" %rawList) 109 110 def addDenyList(): 111 ''’添加非法词 ''' 112 clear() 113 global denyList 114 print(u"输入回车后直接退出") 115 print(u"当前非法字符为:%s" %denyList) 116 st = None 117 while not st == '': 118 st = raw_input("请输入需要添加的非法字符串:") 119 denyList.append(st) 120 clear() 121 print(u"输入回车后直接退出") 122 print(u"当前非法字符列表为:%s" %denyList) 123 124 def clearRawList(): 125 ''’清空原始数据列表 ''' 126 global rawList 127 rawList = [] 128 129 def setRawList(): 130 ''’整理原始数据列表 ''' 131 global rawList 132 global denyList 133 a = set(rawList) 134 b = set(denyList) 135 rawList = [] 136 for str in set(a - b): 137 rawList.append(str) 138 139 def modifyPasswordLen(): 140 ''’修改默认密码的长度 ''' 141 clear() 142 global maxLen 143 global minLen 144 while True: 145 print(u"当前密码长度为%d-%d" %(minLen, maxLen)) 146 min = raw_input("请输入密码最小长度:") 147 max = raw_input("请输入密码最大长度:") 148 try: 149 minLen = int(min) 150 maxLen = int(max) 151 except ValueError: 152 print(u"密码长度只能输入数字[6-18]") 153 break 154 if minLen not in xrange(6,19) or maxLen not in xrange(6,19): 155 print(u"密码长度只能输入数字[6-18]") 156 minLen = 6 157 maxLen = 16 158 continue 159 if minLen == maxLen: 160 res = raw_input("确定将密码长度设定为%d吗?(Yy/Nn)" %minLen) 161 if res not in list('yYnN'): 162 print(u"输入错误,请重新输入") 163 continue 164 elif res in list('yY'): 165 print(u"好吧,你确定就好") 166 break 167 else: 168 print(u"给个机会,改一下吧") 169 continue 170 elif minLen > maxLen: 171 print(u"最小长度比最大长度还大,可能吗?请重新输入") 172 minLen = 6 173 maxLen = 16 174 continue 175 else: 176 print(u"设置完毕,等待%d秒后回主菜单" %timeout) 177 time.sleep(timeout) 178 break 179 180 def createPasswordList(): 181 ''’创建密码列表 ''' 182 global rawList 183 global pwList 184 global maxLen 185 global minLen 186 titleList = [] 187 swapcaseList = [] 188 for st in rawList: 189 swapcaseList.append(st.swapcase()) 190 titleList.append(st.title()) 191 sub1 = [] 192 sub2 = [] 193 for st in set(rawList + titleList + swapcaseList): 194 sub1.append(st) 195 for i in xrange(2, len(sub1) + 1): 196 sub2 += list(itertools.permutations(sub1, i)) 197 for tup in sub2: 198 PW = '' 199 for subPW in tup: 200 PW += subPW 201 if len(PW) in xrange(minLen, maxLen + 1): 202 pwList.append(PW) 203 else: 204 pass 205 206 def showPassword(): 207 ''’显示创建的密码 ''' 208 global pwList 209 global timeout 210 for i in xrange(len(pwList)): 211 if i%4 == 0: 212 print("%s\n" %pwList[i]) 213 else: 214 print("%s\t" %pwList[i]), 215 print('\n') 216 print(u"显示%d秒,回到主菜单" %timeout) 217 time.sleep(timeout) 218 219 def createPasswordFile(): 220 ''’创建密码字典文件 ''' 221 global flag 222 global pwList 223 print(u"当前目录下创建字典文件:dic.txt") 224 time.sleep(timeout) 225 with open('./dic.txt', 'w+') as fp: 226 for PW in pwList: 227 fp.write(PW) 228 fp.write('\n') 229 flag = 1 230 231 232 if __name__ == '__main__': 233 main()
输入:wq,保存makePasswordFileFunction.py。makePasswordFileFunction.py稍微复杂一点点,它的作用就是根据用户输入的“密码元素”来创建一个字典列表。该脚本将输入的元素根据一定的规则修改、添加后当作新元素添加到元素列表中去。最后将元素列表排列组合得到最后的字典列表。执行命令:
python makePasswordFileFunction.py
得到的结果如图2-19所示。
图2-19 run makePasswordFileFunction.py
纯C语言的写法好处就是关系简单明了,函数调用一目了然。但如果调用的函数过多,就难免有些混乱了。简单功能的程序还无妨,稍大一点项目就有些吃力了。
提示
不要添加太多的“密码元素”,这个程序只是利用了Python的模块,没有优化算法。如果输入的“密码元素”超过了20个,那么创建密码字典的时间会非常的长。
2.3.2 类
既然有了In Python Everything Is A Fcuntion,当然会有In Python Everything Is A Class。这种C++的写法就是把所有相似的功能都封装到一个类里。最理想的情况是一个程序只有一个主程序,然后在主程序里实例化类。
【示例2-14】还是以编写密码字典为例,将makePasswordFileFunction.py改编成makePasswordFileClass.py。打开Putty连接到Linux,执行命令:
cd code/crawler vi makePasswordFileClass.py
makePasswordFileClass.py的代码如下:
1 #! /usr/bin/env python 2 #-*- coding: utf-8-*- 3 __author__ = 'hstking hstking@hotmail.com' 4 5 import os 6 import platform 7 import itertools 8 import time 9 10 class MakePassword(object): 11 def __init__(self): 12 self.rawList = [] 13 self.denyList = ['', ' ', '@'] 14 self.pwList = [] 15 self.minLen = 6 16 self.maxLen = 16 17 self.timeout = 3 18 self.flag = 0 19 self.run = { 20 '0':exit, 21 '1':self.getRawList, 22 '2':self.addDenyList, 23 '3':self.clearRawList, 24 '4':self.setRawList, 25 '5':self.modifyPasswordLen, 26 '6':self.createPasswordList, 27 '7':self.showPassword, 28 '8':self.createPasswordFile 29 } 30 self.main() 31 32 def main(self): 33 while True: 34 self.mainMenu() 35 op = raw_input(’输入选项:') 36 if op in map(str, range(len(self.run))): 37 self.run.get(op)() 38 else: 39 self.tipMainMenuInputError() 40 continue 41 42 def mainMenu(self): 43 self.clear() 44 print(u'||'), 45 print(u'='*40), 46 print(u'||') 47 print(u'|| 0:退出程序’) 48 print(u'|| 1:输入密码原始字符串’) 49 print(u'|| 2:添加非法字符到列表’) 50 print(u'|| 3:清空原始密码列表’) 51 print(u'|| 4:整理原始密码列表’) 52 print(u'|| 5:改变默认密码长度(%d-%d)' %(self.minLen, self.maxLen)) 53 print(u'|| 6:创建密码列表’) 54 print(u'|| 7:显示所有密码’) 55 print(u'|| 8:创建字典文件’) 56 print(u'||'), 57 print(u'='*40), 58 print(u'||') 59 print(u’当前非法字符为:%s' %self.denyList) 60 print(u’当前原始密码元素为:%s' %self.rawList) 61 print(u’共有密码%d个’ %len(self.pwList)) 62 if self.flag: 63 print(u"已在当前目录创建密码文件dic.txt") 64 else: 65 print(u"尚未创建密码文件") 66 67 def clear(self): 68 OS = platform.system() 69 if (OS == u'Windows'): 70 os.system('cls') 71 else: 72 os.system('clear') 73 74 def tipMainMenuInputError(self): 75 self.clear() 76 print(u"只能输入0-7的整数,等待%d秒后重新输入" %timeout) 77 time.sleep(timeout) 78 79 def getRawList(self): 80 self.clear() 81 print(u"输入回车后直接退出") 82 print(u"当前原始密码列表为:%s" %self.rawList) 83 st = None 84 while not st == '': 85 st = raw_input("请输入密码元素字符串:") 86 if st in self.denyList: 87 print(u"这个字符串是预先设定的非法字符串") 88 continue 89 else: 90 self.rawList.append(st) 91 self.clear() 92 print(u"输入回车后直接退出") 93 print(u"当前原始密码列表为:%s" %self.rawList) 94 95 def addDenyList(self): 96 self.clear() 97 print(u"输入回车后直接退出") 98 print(u"当前非法字符为:%s" %self.denyList) 99 st = None 100 while not st == '': 101 st = raw_input("请输入需要添加的非法字符串:") 102 self.denyList.append(st) 103 self.clear() 104 print(u"输入回车后直接退出") 105 print(u"当前非法字符列表为:%s" %self.denyList) 106 107 def clearRawList(self): 108 self.rawList = [] 109 110 def setRawList(self): 111 a = set(self.rawList) 112 b = set(self.denyList) 113 self.rawList = [] 114 for str in set(a - b): 115 self.rawList.append(str) 116 117 def modifyPasswordLen(self): 118 self.clear() 119 while True: 120 print(u"当前密码长度为%d-%d" %(self.minLen, self.maxLen)) 121 min = raw_input("请输入密码最小长度:") 122 max = raw_input("请输入密码最大长度:") 123 try: 124 self.minLen = int(min) 125 self.maxLen = int(max) 126 except ValueError: 127 print(u"密码长度只能输入数字[6-18]") 128 break 129 if self.minLen not in xrange(6,19) or self.maxLen not in xrange(6,19): 130 print(u"密码长度只能输入数字[6-18]") 131 self.minLen = 6 132 self.maxLen = 16 133 continue 134 if self.minLen == self.maxLen: 135 res = raw_input("确定将密码长度设定为%d吗?(Yy/Nn)" %self.minLen) 136 if res not in list('yYnN'): 137 print(u"输入错误,请重新输入") 138 continue 139 elif res in list('yY'): 140 print(u"好吧,你确定就好") 141 break 142 else: 143 print(u"给个机会,改一下吧") 144 continue 145 elif self.minLen > self.maxLen: 146 print(u"最小长度比最大长度还大,可能吗?请重新输入") 147 self.minLen = 6 148 self.maxLen = 16 149 continue 150 else: 151 print(u"设置完毕,等待%d秒后回主菜单" %self.timeout) 152 time.sleep(self.timeout) 153 break 154 155 def createPasswordList(self): 156 titleList = [] 157 swapcaseList = 158 for st in self.rawList: 159 swapcaseList.append(st.swapcase()) 160 titleList.append(st.title()) 161 sub1 = [] 162 sub2 = [] 163 for st in set(self.rawList + titleList + swapcaseList): 164 sub1.append(st) 165 for i in xrange(2, len(sub1) + 1): 166 sub2 += list(itertools.permutations(sub1, i)) 167 for tup in sub2: 168 PW = '' 169 for subPW in tup: 170 PW += subPW 171 if len(PW) in xrange(self.minLen, self.maxLen + 1): 172 self.pwList.append(PW) 173 else: 174 pass 175 176 def showPassword(self): 177 for i in xrange(len(self.pwList)): 178 if i%4 == 0: 179 print("%s\n" %self.pwList[i]) 180 else: 181 print("%s\t" %self.pwList[i]), 182 print('\n') 183 print(u"显示%d秒,回到主菜单" %self.timeout) 184 time.sleep(self.timeout) 185 186 def createPasswordFile(self): 187 print(u"当前目录下创建字典文件:dic.txt") 188 time.sleep(self.timeout) 189 with open('./dic.txt', 'w+') as fp: 190 for PW in self.pwList: 191 fp.write(PW) 192 fp.write('\n') 193 self.flag = 1 194 195 196 if __name__ == '__main__': 197 mp = MakePassword()
输入:wq,保存makePasswordFileClass.py。makePasswordFileClass.py和makePasswordFileFunction.py实质上没什么区别,只是一个使用的是C语言风格的函数调用,一个使用的是C++风格的类实例化。执行命令:
python makePasswordFileClass.py
得到的结果如图2-20所示。
图2-20 run makePasswordFileClass.py
执行结果完全一样。这种C++的写法好处就是调用过程简单,不再关心类具体的实现过程,只需要调用其功能即可;但随之而来就是类的继承、函数重载等麻烦。这种写法在写大项目时可能非常有用。个人写小程序也行,那就没那么多优势了。
提示
这个密码还有个问题就是在创建密码文件前并没有估算磁盘剩余空间是否足够。一般的解决办法是先估算密码文件的大小,然后创建一个大小相同的空文件。能创建成功则继续运行程序,不能则抛出异常。