记录瞬间
最近碰到一个烦心的问题,就是找了一些需要的文件,但是碰到的都是加密的文件
这样做不符合开放、开源的原则啊!
所以,经过慎重思考,决定写一个解密的方法,进行解密操作
前置条件:
1、针对密码只能猜测有哪些可能,所以就将数字+字母(大小写)作为排序的全排列,进行文件的密码组装
2、针对zip或者rar文件,需要进行判别处理,毕竟压缩的方法不一样
3、要考虑操作的效率,就必须进行多进程的操作
方法一、
简单介绍一下:使用product函数生成密码的笛卡尔积,本质上比较容易理解,但是由于设置密码长度不确定,加之运行机器的限制
product方法针对密码位数小于等于 4 位的情况比较有效,大于4位的密码,由于运行占用内存的原因,就无法运行了(单位的笔记本,配置没那么好o(* ̄︶ ̄*)o)
代码如下:
from itertools import product import os import string from multiprocessing import Process import zipfile # 需要安装一下 pip install rarfile import rarfile def read_file(file_name, pwd): if file_name.endswith(".zip"): fp = zipfile.ZipFile(file_name) elif file_name.endswith(".rar"): fp = rarfile.RarFile(file_name) else: return try: fp.extractall(path="./") fp.close() print("没有密码,直接解压就行了。") except: print("有密码的啊!") try: pwd = pwd except: print("没有密码本吗?") return for p in pwd: p = p.strip() try: fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode()) print("Success === >", p) fp.close() break except: pass def generate_pwd(): pwd_list = [str(i) for i in range(10)] + list(string.ascii_letters) # print(pwd_list) count = 1 with open(file=r'./pwd.txt', mode='a', encoding='UTF-8') as f: # 生成4位的时候就已经达到 15018570 条了,到了第5位密码时,机器内存溢出了。(✿◡‿◡) # 后续可以考虑使用其他方法生成,product方法太消耗内存了。 for i in range(1, 7): product_str = "product({})".format("pwd_list,"*i) product_str = product_str.replace(',)', ')') print(product_str) result = list(eval(product_str)) for mix in result: # print(mix) pwd_str = str(mix).replace("'", "").replace("(", "").replace(")", "").replace(", ", "").replace(",", "") print(pwd_str, file=f) count += 1 print(count) def multi_process(file_name): if not os.path.isfile('./pwd.txt'): generate_pwd() with open(file=r'./pwd.txt', mode='r', encoding='utf-8') as f: context = f.readlines() print(len(context)) # 确定cpu数量,进行多进程执行,但是要考虑到实际数据的量,主要是密码的量 # 机器性能好的,可以把这个值调大一点儿 cpus = 3 peer_num = num = int(len(context) / cpus) data_dict = {} sum_mark = 0 process_list = [] for i in range(cpus): if i == (cpus - 1): data_dict[i] = context[sum_mark:] else: data_dict[i] = context[sum_mark:peer_num] sum_mark = peer_num peer_num += num process_list.append(Process(target=read_file, args=(file_name, data_dict[i]))) # 启动进程 for pl in process_list: pl.start() if __name__ == '__main__': file_name = r'D:\BaiduNetdiskDownload\xxx.rar' multi_process(file_name=file_name)
方法二、
改进,考虑到使用product方法的局限性,希望能从生成器的角度进行解决此问题,来避免消耗过多内存
同时,需要针对多进程间的通信方式,进行改进,主要考虑的就是使用queue的方法来进行通信
但是方法二,只针对密码为5位时进行了解决,相对来说还有一些局限性
数字加大小写字母[0-9a-zA-Z] 可遍历密码数据个数: 62 5位密码全排列结果数据个数: 916132832 亿哦! 6位密码全排列结果数据个数: 56800235584 百亿哦! 7位密码全排列结果数据个数: 3521614606208 兆哦! 8位密码全排列结果数据个数: 218340105584896 百兆哦!
代码如下:
import string import sys import time from multiprocessing import Process, Queue import zipfile # 需要安装一下 pip install rarfile import rarfile def generate_secrete(pwd): for a in pwd: # ab = "{}".format(a) # yield ab for b in pwd: for c in pwd: for d in pwd: for e in pwd: ll = "{}{}{}{}{}".format(a, b, c, d, e) yield ll def get_pwd_data(q): pwd = [str(i) for i in range(10)] + list(string.ascii_letters) print("可遍历密码数据个数:", len(pwd)) print("5次组合结果数据个数:", len(pwd) ** 5) gs = generate_secrete(pwd=pwd) count = 0 while True: try: get_pwd = next(gs) # print(get_pwd) # 压入队列数据 q.put(get_pwd) count += 1 flag = 0 # 压入数据很快,但是消耗的有点儿慢,所以,需要等一等 while count % 5000000 == 0: if flag == 0: print("当前压入总数为:", count) flag = 1 # 动态调整一下结果数据 if q.qsize() > 1000000: time.sleep(50.0) elif q.qsize() > 750000: time.sleep(5.0) elif q.qsize() > 250000: time.sleep(2.0) else: time.sleep(2.0) # 设置一定的存量数据,提高整体读取的速度 if q.qsize() < 100000: print("队列中数据少于10万", q.qsize()) break # 都清空了不好,需要重新压入数据,效率太低了 if q.empty(): break except StopIteration: print("Already fetch all data from generator.") print("压入队列的总数为:", count) break except: pass # 解压程序,将文件进行解压操作 def read_file(file_name, q): if file_name.endswith(".zip"): fp = zipfile.ZipFile(file_name) elif file_name.endswith(".rar"): fp = rarfile.RarFile(file_name) else: return try: fp.extractall(path="./") fp.close() print("没有密码,直接解压就行了。") except: print("有密码的啊!") while True: try: # 判断队列是否为空,如果为空就等待一下再获取 if q.empty(): # print("等待 0.5 s") time.sleep(0.5) p = q.get() if p == "==ok==": break except: print("没有密码本吗?") time.sleep(0.5) continue else: try: fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode()) print("Success === >", p) fp.close() # q.put("==ok==") with open("success.txt", mode='w', encoding='UTF-8') as f: f.write(p) for i in range(10): q.put("==ok==") print("压入队列暂停数据。") break except: pass # 启动多进程进行操作 def multi_process(file_name): q = Queue() process_list = [] cpus = 8 start_time = time.time() # 不断的向队列中压入数据 process_list.append(Process(target=get_pwd_data, args=(q, ))) # 启动其余进程从队列中读取数据 for i in range(1, cpus): process_list.append(Process(target=read_file, args=(file_name, q))) print("当前执行的进程的个数", len(process_list), " 个") # 启动进程 for pl in process_list: print("启动进程:", pl.name) pl.start() # 让数据不断的压入进程,并阻塞执行 print("压入数据的进程进行阻塞执行!") process_list[0].join() print("使用队列时间:", time.time() - start_time) print("等待队列数据全部被取出") # 等待队列数据全部被取出 while True: print("当前剩余个数", q.qsize()) if not q.empty(): time.sleep(5.0) continue else: break # 队列执行完成后,退出其他的进程 for pl in process_list: try: pl.terminate() print(pl.name, "Yes it's quit!") except: print(pl.name, "was already quit!") pass if __name__ == '__main__': file_name = r'D:\BaiduNetdiskDownload\xxx.rar' multi_process(file_name=file_name)
方法三、
针对方法二的局限,考虑进一步改进,解决更多位密码的方式方法,办法是动态生成一个生成器函数,加载生成器函数进行数据校验
代码如下:
import os import string import sys import time from multiprocessing import Process, Queue import zipfile # 需要安装一下 pip install rarfile import rarfile # 动态创建生成函数方法,主要生成生成器函数,以供后续调用 # 参数num代表,密码的位数,全排列的结果 def create_function(num): ll = "" symbol = "" if os.path.isfile("./temp.py"): os.remove("./temp.py") with open(file="./temp.py", mode='w', encoding='UTF-8') as f: f.write("def generate_pwd(pwd):\n") for i in range(num): f.write(" "*(i+1)) f.write("for a{} in pwd:\n".format(i)) symbol += "{}" ll = ll + "a{}, ".format(i) ll = ll[:-2] f.write(" " * (num + 1)) f.write("ll = '" + symbol + "'" + ".format(" + ll + ")\n") f.write(" " * (num + 1)) f.write("yield ll\n") # 参数说明:q是代表队列,num代表密码的位数 def get_pwd_data(q, num, default_count=0): # print(string.printable) # 输出为:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[]^_`{|}~ pwd = [str(i) for i in range(10)] + list(string.ascii_letters) print("可遍历密码数据个数:", len(pwd)) print(num, "位密码组合结果数据个数:", len(pwd) ** num) create_function(num=num) module = __import__("temp") gp = module.generate_pwd(pwd) count = 0 while True: try: get_pwd = next(gp) # 此处依然可以针对获取的密码进行判断,比如可以猜测密码前几位是字母,后几位是数字等信息,来进一步筛选密码,达到快速获取的目的 # print(get_pwd) count += 1 # 指定从哪一次获取密码进行执行,默认不进行判断 if count <= default_count: continue # 压入队列数据 q.put(get_pwd) flag = 0 # 压入队列数据操作很快,但是消耗数据有点儿慢,所以,需要等一等 # 此处需要增加判断,主要是判断循环多少次之后,直接结束(return)该方法,结束之前,也建议清空队列 # 目的是不让循环无终止的进行下去,过程中,可能已经获取到密码,此时没有进程在获取数据了 while count % 5000000 == 0: if flag == 0: print("当前压入总数为:", count) flag = 1 # 动态调整一下结果数据 if q.qsize() > 1000000: time.sleep(50.0) elif q.qsize() > 750000: time.sleep(5.0) elif q.qsize() > 250000: time.sleep(2.0) else: time.sleep(2.0) # 设置一定的存量数据,提高整体读取的速度 if q.qsize() < 100000: print("队列中数据少于10万", q.qsize()) break # 都清空了不好,需要重新压入数据,效率低了 if q.empty(): break except StopIteration: print("Already fetch all data from generator.") print("压入队列的总数为:", count) break except: pass # 解压程序,将文件进行解压操作 def read_file(file_name, q): if file_name.endswith(".zip"): fp = zipfile.ZipFile(file_name) elif file_name.endswith(".rar"): fp = rarfile.RarFile(file_name) else: return try: fp.extractall(path="./") fp.close() print("没有密码,直接解压就行了。") except: print("有密码的啊!") while True: try: # 判断队列是否为空,如果为空就等待一下再获取 if q.empty(): # print("等待 0.5 s") time.sleep(0.5) p = q.get() if p == "==ok==": return except: print("没有密码本吗?") time.sleep(0.5) continue else: try: fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode()) print("Success === >", p) fp.close() # q.put("==ok==") with open("success.txt", mode='w', encoding='UTF-8') as f: f.write(p) for i in range(10): q.put("==ok==") print("压入队列暂停数据。") while True: if q.empty(): break else: q.get() q.task_done() break except: pass # 启动多进程进行操作 def multi_process(file_name): q = Queue() cpus = 9 # 密码位数从 1~8 位,可以根据实际情况进行修改 for num in range(1, 9): if os.path.isfile("./success.txt"): os.remove("./success.txt") process_list = [] start_time = time.time() # 不断的向队列中压入数据 process_list.append(Process(target=get_pwd_data, args=(q, num))) # 启动其余进程,从队列中读取数据 for i in range(1, cpus): process_list.append(Process(target=read_file, args=(file_name, q))) print("当前执行的进程的个数", len(process_list), " 个") # 启动进程 for pl in process_list[:]: print("启动进程:", pl.name) pl.start() print("压入数据的进程进行阻塞执行!", process_list[0].name) # 让数据不断的压入队列进程,并阻塞执行 process_list[0].join() print("数据完全压入到队列中,执行时间:", time.time() - start_time) print("等待队列数据全部被取出") # 设定标记,以用来判断程序是否需要退出 flag = 0 # 等待队列数据全部被取出 while True: print("当前剩余个数", q.qsize()) if q.qsize() == 0: # if os.path.isfile("./success.txt"): flag = 1 break else: if os.path.isfile("./success.txt"): flag = 1 break time.sleep(5.0) continue # 队列执行完成后,退出其他的进程 for pl in process_list: try: # 直接terminate会导致僵尸进程,需要等待进程彻底退出才是关闭进程 pl.terminate() print(pl.name, "Yes it's quit!") # 等待进程退出 pl.join() except: print(pl.name, "was already quit!") pass if flag == 1: break else: print("本次循环就没有正确的密码被执行:", num) if __name__ == '__main__': file_name = r'D:\BaiduNetdiskDownload\xxx.rar' multi_process(file_name=file_name)
除了进程操作之外,依然可以考虑其他的操作方法,如:线程、协程等方式来执行