1. 前言
2. 破解zip、7z等
2.1 快速开始
python里有个内置库zipfile可以对zip格式压缩包进行操作。
1 2 3 4 5 6 7 8 import zipfile file = zipfile.ZipFile("测试.zip" , 'r' ) file.extractall(path='.' , pwd='123' .encode('utf-8' )) file.close()
知道了如何使用代码解压文件,下面只需生成所有可能的密码,编写代码逐一调用函数,直至成功。
2.2 拓展:“深浅拷贝”
在生成密码列表的时候,需要对数字/字母进行排列组合,此时利用itertools可以方便的生成。
1 2 nums = [str (i) for i in range (10 )] password_lst = itertools.permutations(nums, 2 )
这里有2个需要注意的地方,第一个是permutations()生成的是不重复的“排列”,故上述代码生成的列表只有90个元素。
正确的姿势应该是用product()。
itertools官方文档
第二个,生成的密码列表password_lst实际上是一个迭代器,对他进行去长度实际上是遍历了一次,这时他指向最后一个元素,生命周期已然耗尽。故我们在获取长度时需要复制一个新的迭代器。
重点来了,这里涉及到深浅拷贝的问题。简单的赋值操作是浅拷贝,只是传地址,新的变量仍然指向password_lst而不是独立的。
要实现深拷贝需要用到copy模块
1 2 3 4 5 6 7 8 9 10 11 12 13 import copydef get_length (generator ): if hasattr (generator,"__len__" ): return len (generator) else : return sum (1 for _ in generator) nums = [str (i) for i in range (10 )] password_lst = itertools.permutations(nums, 2 ) new_lst = copy.deepcopy(password_lst) length = get_length(new_lst)
还有一种方法,直接遍历一遍password_lst并将所有元素加入到空列表。这样做的好处是方便求长度。下面的代码都会采用这种方法。
1 2 3 nums = [str (i) for i in range (10 )] password_lst = ['' .join(x) for x in itertools.product(*[nums] * 2 )] length = len (password_lst)
2.3 多线程提高效率
为了提高破解速度,还可以加入多线程,而且使用线程池(ThreadPoolExecutor),方便管理、重复利用资源。
但是在实验过程中还发现一个问题:ThreadPoolExecutor默认使用的是无界队列,尝试密码的速度跟不上生产密码的速度,会把生产任务无限添加到队列中,导致内存被占满,最终导致程序崩溃。
此处不做展示,请自行测试。
要解决这个问题,可以重写ThreadPoolExecutor类,给队列设置一个最大值,使无界队列变成有界。
代码如下
展开/隐藏代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import zipfileimport itertoolsfrom concurrent.futures import ThreadPoolExecutorimport queueclass BoundedThreadPoolExecutor (ThreadPoolExecutor ): def __init__ (self, max_workers=None , thread_name_prefix='' ): super ().__init__(max_workers, thread_name_prefix) self._work_queue = queue.Queue(self._max_workers * 2 ) def extract (file, password ): if not flag: return file.extractall(path='.' , pwd='' .join(password).encode('utf-8' ))def result (f ): global flag exception = f.exception() if not flag: return if not exception: flag = False print ('\n密码为:' , '' .join(f.pwd)) flag = True pool = BoundedThreadPoolExecutor(4 ) nums = [str (i) for i in range (10 )] chrs = [chr (i) for i in range (65 , 91 )] password_lst = ['' .join(x) for x in itertools.product(*[nums] * 4 )] zfile = zipfile.ZipFile("test.zip" , 'r' )for pwd in password_lst: if not flag: break f = pool.submit(extract, zfile, pwd) f.pwd = pwd f.pool = pool f.add_done_callback(result) zfile.close() pool.shutdown(wait=True )
2.3.1 测试
下面来测试一下。
创建一个测试文件test.txt,并压缩成test.zip,设置密码为1234
运行程序
可以看到已经解压成功了
2.4 进度条+用时(两种方案)
2.4.1 ①子线程打印
进度条功能需要实时监控当前已完成数量,并且与生成的密码列表长度进行比对。因为主线程在不断遍历生成的密码列表,并且不断将新任务放入线程池,故不可能使用主线程进行。而线程池使用队列的形式,不可能让一个线程占用太长时间,否则后面就会卡住进行不下去。
在这种情况下,只能使用最原始的多线程方式threading开始一个子线程。threading有两种写法,这里我选择的是重写Tread类,并在run方法里写上要运行的代码。
1 2 3 4 5 6 7 8 9 10 11 class myThread (threading.Thread): def __init__ (self, name, length ): threading.Thread.__init__(self) self.name = name self.length = length def run (self ): global flag,process while True : if not flag: return percent = process / self.length * 100 print (f"\r当期进度:{process} - {percent} %" , end="" )
效果
完整代码如下
展开/隐藏代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 import zipfileimport itertoolsfrom concurrent.futures import ThreadPoolExecutorimport queueimport threadingimport timeclass BoundedThreadPoolExecutor (ThreadPoolExecutor ): def __init__ (self, max_workers=None , thread_name_prefix='' ): super ().__init__(max_workers, thread_name_prefix) self._work_queue = queue.Queue(self._max_workers * 2 ) class myThread (threading.Thread): def __init__ (self, name, length ): threading.Thread.__init__(self) self.name = name self.length = length def run (self ): global flag,process last_time = time.time() number = 0 while True : time.sleep(0.1 ) p = process if not flag: return percent = p / self.length * 100 current_time = time.time() speed = (p-number)/(current_time-last_time) number = p last_time = current_time print ("\r已完成:%d/%d - %.2f" %(p,self.length,percent)+"% " +"速度:%.2fp/s" %(speed), end="" ) if (p >= self.length) and flag: print ('\n未找到密码' ) return def extract (file, password ): global process if not flag: return process += 1 file.extractall(path='after' , pwd='' .join(password).encode('utf-8' ))def result (f ): global flag exception = f.exception() if not flag: return if not exception: flag = False print ('\n密码为:' , f.pwd) flag = True pool = BoundedThreadPoolExecutor(4 ) process = 0 nums = [str (i) for i in range (10 )] chrs = [chr (i) for i in range (65 , 91 )] password_lst = ['' .join(x) for x in itertools.product(*[nums+chrs] * 4 )] process_bar = myThread(name='process_bar' ,length=len (password_lst)) process_bar.setDaemon(True ) process_bar.start() zfile = zipfile.ZipFile("before/test.zip" , 'r' )for pwd in password_lst: if not flag: break f = pool.submit(extract, zfile, pwd) f.pwd = pwd f.pool = pool f.add_done_callback(result) zfile.close() pool.shutdown(wait=True ) process_bar.join()
2.4.2 ②tqdm第三方库
2.4.2.1 快速开始
安装
示例
1 2 3 4 5 from tqdm import tqdmimport timefor i in tqdm(range (6 )): time.sleep(0.5 )
输出
2.4.2.2 应用
只需将tqdm写在主线程的for循环上即可
1 2 3 4 5 6 for pwd in tqdm(password_lst): if not flag: break f = pool.submit(extract, zfile, pwd) f.pwd = pwd f.pool = pool f.add_done_callback(result)
效果:
2.5 封装
至此,程序搭建成功。
下面可以进行封装,方便调用。
完整代码如下
展开/隐藏代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 import zipfileimport itertoolsimport queueimport threadingimport timeimport sysfrom concurrent.futures import ThreadPoolExecutorfrom tqdm import tqdmclass BoundedThreadPoolExecutor (ThreadPoolExecutor ): def __init__ (self, max_workers=None , thread_name_prefix='' ): super ().__init__(max_workers, thread_name_prefix) self._work_queue = queue.Queue(self._max_workers * 2 ) class BreakZip (): def __init__ (self, srcpath, despath ): self.srcpath = srcpath self.despath = despath self.zfile = None self._check_file() self.flag = True self.process = 0 self.pool = BoundedThreadPoolExecutor(2 ) self.password_lst = [] self._gen_pwd_lst() self.length = len (self.password_lst) self.lock = threading.Lock() def _check_file (self ): try : self.zfile = zipfile.ZipFile(self.srcpath, 'r' ) except Exception as e: print (e) sys.exit() def set_pwd_lst (self, password_lst ): self.password_lst = password_lst self.length = len (self.password_lst) def _gen_pwd_lst (self ): nums = [str (i) for i in range (10 )] chrs = [chr (i) for i in range (65 , 91 )] self.password_lst = ['' .join(x) for x in itertools.product(*[nums + chrs] * 2 )] def _extract (self, password ): if not self.flag: return with self.lock: self.process += 1 self.zfile.extractall(path=self.despath, pwd='' .join(password).encode('utf-8' )) def _result (self, f ): exception = f.exception() if not self.flag: return if not exception: self.flag = False print ('\n密码为:' , f.pwd) def start (self ): for pwd in tqdm(self.password_lst): if not self.flag: break f = self.pool.submit(self._extract, pwd) f.pwd = pwd f.pool = self.pool f.add_done_callback(self._result) self.zfile.close() self.pool.shutdown(wait=True ) if self.flag: print ('\n未找到密码' ) return def gen_pwd_lst (): nums = [str (i) for i in range (10 )] chrs = [chr (i) for i in range (65 , 91 )] password_lst = ['' .join(x) for x in itertools.product(*[nums + chrs] * 4 )] return password_lstif __name__ == '__main__' : srcpath = 'before/test.zip' despath = 'after' password_lst = gen_pwd_lst() breakzip = BreakZip(srcpath,despath) breakzip.set_pwd_lst(password_lst) breakzip.start()
上面的密码本生成和文件路径都是写死的,所以除此之外,还可以通过输入来获取以上信息。而且还加入了第三方库filetype用来判断文件类型。
完整代码如下
展开/隐藏代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 import zipfileimport itertoolsimport queueimport threadingimport timeimport filetypeimport osimport sysimport refrom concurrent.futures import ThreadPoolExecutorfrom tqdm import tqdmclass BoundedThreadPoolExecutor (ThreadPoolExecutor ): def __init__ (self, max_workers=None , thread_name_prefix='' ): super ().__init__(max_workers, thread_name_prefix) self._work_queue = queue.Queue(self._max_workers * 2 ) class BreakZip (): def __init__ (self, srcpath, despath ): self.srcpath = srcpath self.despath = despath self.zfile = None self._check_file() self.flag = True self.process = 0 self.pool = BoundedThreadPoolExecutor(2 ) self.password_lst = [] self._gen_pwd_lst() self.length = len (self.password_lst) self.lock = threading.Lock() def _check_file (self ): try : self.zfile = zipfile.ZipFile(self.srcpath, 'r' ) except Exception as e: print (e) sys.exit() def set_pwd_lst (self, password_lst ): self.password_lst = password_lst self.length = len (self.password_lst) def _gen_pwd_lst (self ): nums = [str (i) for i in range (10 )] chrs = [chr (i) for i in range (65 , 91 )] self.password_lst = ['' .join(x) for x in itertools.product(*[nums + chrs] * 2 )] def _process_bar (self ): last_time = time.time() number = 0 while True : time.sleep(0.1 ) p = self.process if not self.flag: return percent = p / self.length * 100 current_time = time.time() speed = (p - number) / (current_time - last_time) number = p last_time = current_time print ("\r已完成:%d/%d - %.2f" % (p, self.length, percent) + "% " + "速度:%.2fp/s" % (speed), end="" ) if (p >= self.length) and self.flag: return def _extract (self, password ): if not self.flag: return with self.lock: self.process += 1 self.zfile.extractall(path=self.despath, pwd='' .join(password).encode('utf-8' )) def _result (self, f ): exception = f.exception() if not self.flag: return if not exception: self.flag = False print ('\n密码为:' , f.pwd) def start (self ): for pwd in tqdm(self.password_lst): if not self.flag: break f = self.pool.submit(self._extract, pwd) f.pwd = pwd f.pool = self.pool f.add_done_callback(self._result) self.zfile.close() self.pool.shutdown(wait=True ) if self.flag: print ('\n未找到密码' ) return def get_input_info (): while True : srcpath = str (input ('请输入要破解的压缩包位置(绝对/相对路径):' )) despath = str (input ('请输入要保存的位置(回车默认文档名字目录):' )) if despath == '' : despath = srcpath.replace('.zip' , '\\' ) try : kind = filetype.guess(srcpath) if kind is None : print ('Cannot guess file type!' ) continue elif kind.extension != 'zip' and '7z' : print ('Is not a zip or 7z file' ) continue except Exception as e: print (e) continue break if not os.path.isdir(despath): os.mkdir(despath) nums = [str (i) for i in range (10 )] hchrs = [chr (i) for i in range (65 , 91 )] lchrs = [chr (i) for i in range (97 , 123 )] spes = [chr (i) for i in range (20 , 48 )]+[chr (i) for i in range (91 , 97 )]+[chr (i) for i in range (123 , 127 )] type_all = [chr (i) for i in range (32 , 127 )] wordstype_all = [nums,lchrs,hchrs,spes] words = [] print ('请指定密码可能涉及的字符:' ) print ('1.数字' ) print ('2.小写英文字母' ) print ('3.大写英文字母' ) print ('4.特殊字符' ) print ('5.所有可打印字符' ) print ('6.自由组合' ) while True : wordstype = input ('>>' ) if wordstype == '1' : words = nums elif wordstype == '2' : words = lchrs elif wordstype == '3' : words = hchrs elif wordstype == '4' : words = spes elif wordstype == '5' : words = type_all elif wordstype == '6' : comwords = input ('请指定组合类型(如2,1或3,4):' ) comlist = re.split(',|,' ,comwords) print (comlist) for i in comlist: words += wordstype_all[int (i) - 1 ] else : print ('非法输入,请重试' ,end='' ) continue break while True : wordsnum = input ('请输入长度:' ) if wordsnum.isdigit(): wordsnum = int (wordsnum) break else : print ('非法输入,请重试' ) password_lst = ['' .join(x) for x in itertools.product(*[words] * wordsnum)] return (srcpath,despath,password_lst)if __name__ == '__main__' : srcpath, despath, password_lst = get_input_info() breakzip = BreakZip(srcpath,despath) breakzip.set_pwd_lst(password_lst) breakzip.start()
效果如下(绿色字体为手动输入)
3. 破解rar
3.1 环境配置
安装unrar模块
下载安装 unrar library(按照默认路径)
1 http://www.rarlab.com/rar/UnRARDLL.exe
(注:下面都是以win64位演示)
配置环境变量
系统变量中新建变量,变量名输入 UNRAR_LIB_PATH,变量值为 C:Files
(x86).dll
进入目录C:Files (x86)64修改下文件名
重启pycharm
3.2 快速开始
将test.txt压缩成test.rar,密码设置为123,运行代码
1 2 3 4 5 from unrar import rarfile file = rarfile.RarFile('test.rar' ) file.extractall(path='.' , pwd='123' )
解压成功
需要注意的是,与zipfile不同,此处extractall()的pwd不需要encode转码,该方法会自动转码。
3.3 多线程
待续。。。