Python多线程压缩包密码破解:ZIP/RAR/7Z暴力破解实战
2026/9/10 3:01:43 网站建设 项目流程

简介:针对日常忘记RAR、ZIP、7Z压缩包密码的恢复需求,这份Python多线程可视化解密项目提供了完整的源代码与配套词表。程序内置常见弱密码列表作为优先尝试项,表内未命中时自动转入随机数遍历,支持从1位到16位由低到高逐级探测;多线程并发让多个候选项同时校验,冲刺阶段可有效利用CPU资源,整体耗时依密码随机程度和长度呈指数增长。资源共238个文件,包括9个Python脚本、208个TXT字典/词表、5个MD说明文档、3个CSV高频密码统计文件,并附带RAR、ZIP等示例压缩包用于验证,包体约301.88MB。目前已有91人学习下载,读者可从中获取密码表整理方法、线程池调度逻辑、可视化界面以及不同压缩格式的底层调用封装,也可在此基础上扩展掩码攻击或加速,用于个人数据恢复及安全测试。

1. 从基础到实战:Python 多线程压缩包密码破解工具的实现思路

先给结论:这份源代码的核心并不是“破解加密算法”,而是围绕多线程 + 可视化这两个工程点,把“尝试密码”这件事做得足够快、足够好用。RAR、ZIP、7Z 三者的加密机制各不相同,但都有一个共同弱点——密码空间是有限的,只要你愿意花时间,暴力测试总能撞开。这个项目要解决的四个问题分别是:用什么库来创建压缩包并测试密码、怎么利用多线程把百万次尝试压到可接受的时间窗内、怎么让不懂命令行的人也能操作、以及怎么在三类格式之间切换而不用改主流程。适合的人群是:熟悉 Python 语法但还没写过完整工具的开发者、需要批量处理加密压缩包的运维人员,以及想了解爆破工具内部工作原理的安全测试者。

2. 三种格式的密码机制与 Python 侧选型

2.1 先搞清楚破解对象:ZIP、RAR、7Z 的加密差异

很多人以为压缩包密码是“一视同仁”的,实际上三种格式从加密算法到密钥派生方式都不一样,这直接决定了你用什么库去撞密码。

传统 ZIP 加密(ZipCrypto)是一种流密码方案,密钥由密码和一个 12 字节的随机头生成,加密强度很低,用已知明文攻击在毫秒级就能还原头部信息。从 PKZIP 2.0 时代就存在,winrar 和 7-Zip 至今仍默认兼容这种老格式。如果你的目标 ZIP 包是 2003 年以前生成的,或是某些老系统自动打包的,基本都是这种弱加密,用普通 zipfile 模块就能测密码。

WinZip 和 7-Zip 后来为 ZIP 格式增加了 AES-256 加密选项,密钥派生走 PBKDF2-HMAC-SHA1,迭代次数 1000 次(WinZip 标准)。这种就绕不开 pyzipper 这个库,标准库 zipfile 不支持 AES 解压,原因也很简单:zipfile 内部只实现了 ZipCrypto 的加解密,AES 是后来补的扩展,官方标准库没有跟进。rarfile 库本身也不做 AES 解密,它通过调用外部的 unrar 命令行工具完成实际解压,这意味着你在部署时要在系统里装好 WinRAR 或对应的 unrar 可执行文件,并把它放到 PATH 中。7Z 的加密实现更厚,AES-256-CBC 加上 PBKDF2-HMAC-SHA256 的密钥派生,py7zr 库是社区里支持最完整的纯 Python 实现。

2.2 四个库的分工与取舍

支持格式加密支持底层实现依赖成本
zipfileZIP仅 ZipCrypto纯 Python
pyzipperZIPZipCrypto + AES-256纯 Python
rarfileRARRAR3/RAR5 AES外部 unrar 命令需要安装 unrar
py7zr7ZAES-256-CBC纯 Python

选型时有一个容易被新手忽略的坑:rarfile 虽然叫“纯 Python 库”,但它只是解析器,真正解密和提取还是要落到 unrar 可执行文件上。我自己一般在 Linux 服务器上做批量测试时就直接用 apt 装 unrar-free,Windows 开发机上装 WinRAR 后把它安装目录下的 UnRAR.exe 单独拷到项目 bin/ 目录里,这样 rarfile.UNRAR_TOOL 指向相对路径,不会因为机器不同而找不到命令。

pyzipper 有一点要注意:AESZipFile和普通ZipFile在解压失败时的报错类型不同,pyzipper 会抛RuntimeError,而标准库抛BadZipFile。写统一封装层时必须把两种异常都捕获到,否则线程池里一个异常抛出来,整个 worker 就中断了。密码错误时 py7zr 抛的是py7zr.exceptions.PasswordRequiredBad7zFile的混合情况,我在实战里遇到过密码对但 CRC 校验失败的场景,这种会被裸 catch 误判成“密码不对”,其实应该单独归类为“密码正确但文件损坏”。

#### 2.2.1 统一封装解耦三格式 既然三种格式各有各的库,我的做法是写一个 facade 类,对外只暴露 `is_password_correct(filepath, password) -> bool` 这一个方法。内部按扩展名路由到不同的实现,未来要加 ACE 或 RAR4 也只是在这个文件里增加一个分支。 ```python import zipfile import pyzipper import rarfile import py7zr from enum import Enum class ArchiveType(Enum): ZIP = ".zip" RAR = ".rar" SEVEN_ZIP = ".7z" class ArchiveCracker: """统一封装三种压缩格式的密码验证接口""" def __init__(self, filepath: str, archive_type: ArchiveType): self.filepath = filepath self.archive_type = archive_type def is_password_correct(self, password: str) -> bool: """返回 True 表示密码正确,False 表示密码错误""" try: if self.archive_type == ArchiveType.ZIP: return self._try_zip_standard(password) or self._try_zip_aes(password) if self.archive_type == ArchiveType.RAR: return self._try_rar(password) if self.archive_type == ArchiveType.SEVEN_ZIP: return self._try_7z(password) except Exception: return False return False def _try_zip_standard(self, password: str) -> bool: try: with zipfile.ZipFile(self.filepath) as zf: # 测试第一个加密文件的密码 first = zf.infolist()[0] zf.open(first, pwd=password.encode("utf-8")).read(1) return True except (RuntimeError, zipfile.BadZipFile): return False def _try_zip_aes(self, password: str) -> bool: try: with pyzipper.AESZipFile(self.filepath) as zf: first = zf.infolist()[0] zf.open(first, pwd=password.encode("utf-8")).read(1) return True except RuntimeError: return False def _try_rar(self, password: str) -> bool: try: with rarfile.RarFile(self.filepath) as rf: first = rf.infolist()[0] # 同样只读取1字节来验证密码 rf.open(first, pwd=password.encode("utf-8")).read(1) return True except rarfile.RarWrongPassword: return False def _try_7z(self, password: str) -> bool: try: with py7zr.SevenZipFile(self.filepath, mode="r", password=password) as zf: # 不提取全部文件,只测试能否读取文件列表 zf.getnames() # 尝试解压到内存,密码错误会在第一次读取时抛出异常 zf.readall() return True except py7zr.exceptions.PasswordRequired: return False except py7zr.exceptions.Bad7zFile: return False except Exception: return False

逻辑说明:ZIP 格式先试标准库的 ZipCrypto,再试 AES,因为同一个文件理论上只能用一种加密,但 pyzipper 对 ZipCrypto 文件的兼容性有时候会出问题,标准库反而更稳。RAR 和 7Z 分别调用各自的专属库,亮点在“只读 1 字节”这个操作——解压全部文件到磁盘的耗时是按秒计的,而读取加密流的前 1 个字节只要触发密钥派生和首块解密就足够了,密码对不对在这一步就能暴露。参数方面,pwd参数接收的是 bytes,所以编码统一走 UTF-8。如果压缩包是用非 UTF-8 编码(比如 GBK 中文密码)创建的,这里需要改成传入编码参数,让用户在界面上选。

3. 多线程爆破引擎的实现:队列、线程数与进度回传

3.1 为什么说 GIL 不是这里的瓶颈

很多文章一聊到 Python 多线程就搬出 GIL,说“线程不可能并行执行 CPU 密集型任务”。这个结论在纯计算场景基本成立,但压缩包密码验证不是纯计算——每一次尝试都要完成“生成密码 → 初始化解压上下文 → 读取 1 字节 → 关闭上下文”这一串操作,其中涉及大量 C 扩展库的系统调用、内存分配和文件 IO。你可以简单测一下:单线程跑 pyzipper 测试 1000 个密码,再看多线程 4 核跑同一批任务,耗时通常能降到 40% 左右。GIL 确实让 Python 字节码不能并行,但 C 扩展内部的调用会释放 GIL,所以这种“混合型”任务反而吃到了多核红利。

提示:如果你的目标是极致速度,multiprocessing 永远比 threading 快,但进程间数据结构共享和进度回传的复杂度几何级上升。这个项目走的是线程池路线,保持代码简洁的同时已经能拿到接近线性加速的效果。

3.2 密码字典生成器:不占内存的流水线

爆破引擎的第一步是密码生成。最朴素的做法是嵌套 for 循环生成所有组合,但组合数量随长度指数增长,例如小写字母 6 位就是 26^6 = 308915776 个组合,全部放进列表内存直接爆掉。正确姿势是用生成器按需产出,配合 itertools.product。

import itertools import string from typing import Iterator def password_factory(charset: str, min_length: int, max_length: int) -> Iterator[str]: """按顺序生成所有候选密码,使用生成器避免内存爆炸。 Args: charset: 字符集,例如 "abcdefghijklmnopqrstuvwxyz0123456789" min_length: 密码最小长度 max_length: 密码最大长度 """ for length in range(min_length, max_length + 1): for combo in itertools.product(charset, repeat=length): yield "".join(combo) # 使用示例:生成6位纯小写字母密码 gen = password_factory(string.ascii_lowercase, 6, 6) for _ in range(10): print(next(gen))

参数说明:itertools.product以字典序生成笛卡尔积,配合 repeat 参数等于多重循环,但它的优势是完全惰性,每次只生成一个元组。瓶颈在"".join(combo)这个操作——每次要新建一个字符串对象。如果密码长度只有 4-6 位,这个开销可忽略,但到了 8 位以上,字符串拼接会占掉总耗时的 20% 左右。优化方向是提前把字符集转成字节数组,直接拼 bytes,最后才 decode。

3.3 ThreadPoolExecutor 任务切分与结果回收

多线程部分我选择concurrent.futures.ThreadPoolExecutor,写起来最短,而且自带任务队列和线程复用。核心问题不是怎么写,而是怎么避免提交了 1 亿个任务后队列内存爆掉。正确做法是分块提交——每次提交固定数量(比如 10000 个密码)给线程池,等这批全部完成后处理进度和结果,再提交下一批。

from concurrent.futures import ThreadPoolExecutor, as_completed from queue import Queue from threading import Event import time class BruteForceEngine: """多线程爆破引擎:按批次提交任务,维护进度与取消状态""" def __init__( self, cracker: ArchiveCracker, charset: str, min_length: int, max_length: int, workers: int = 4, batch_size: int = 10000, ): self.cracker = cracker self.charset = charset self.min_length = min_length self.max_length = max_length self.workers = workers self.batch_size = batch_size self.stop_event = Event() # 用于取消爆破 self.progress_queue: Queue = Queue() # 用于向GUI回传进度 def run(self) -> str | None: """执行爆破,返回找到的密码;被取消返回 None""" gen = password_factory(self.charset, self.min_length, self.max_length) total_tried = 0 start_time = time.time() while not self.stop_event.is_set(): batch = [] # 从生成器捞取一批密码 for _ in range(self.batch_size): try: batch.append(next(gen)) except StopIteration: break if not batch: break # 字典耗尽 # 提交批次到线程池 with ThreadPoolExecutor(max_workers=self.workers) as executor: future_map = { executor.submit(self.cracker.is_password_correct, pwd): pwd for pwd in batch } for future in as_completed(future_map): if self.stop_event.is_set(): executor.shutdown(cancel_futures=True) return None total_tried += 1 if future.result(): # 找到正确密码,取消后续任务 self.stop_event.set() self.progress_queue.put( {"type": "found", "password": future_map[future]} ) return future_map[future] # 每个批次结束后推送进度 elapsed = time.time() - start_time self.progress_queue.put( { "type": "progress", "tried": total_tried, "elapsed": elapsed, "speed": total_tried / elapsed if elapsed > 0 else 0, } ) return None def cancel(self): """外部调用,触发停止标志""" self.stop_event.set()

逻辑说明:外层 while 循环从生成器拉数据,每次切出 10000 个密码组成一个批次,提交给线程池。as_completed阻塞等待批内所有任务完成,这里容易犯的错是拿到第一个结果就立刻 break——因为线程池还有正在跑的任务,直接退出会导致它们变成孤儿线程。所以我在找到密码时调用executor.shutdown(cancel_futures=True),确保未开始的任务被取消,已完成的任务正常回收。参数方面,batch_size不建议设得太大,10 万以上会导致进度刷新滞后;workers取值一般是 CPU 核数,但如果是机械硬盘上的大压缩包,IO 争用会抵消多线程收益,实测 4-8 线程对单文件爆破已经是甜点区间。

提示:不要试图用executor.map替代手写提交,map 必须在所有任务完成后才能拿到结果,你无法在中间取消或提前返回。

3.4 速度基准:什么才是“可接受”的破解速度

拿一个 5MB 的 ZIP 文件(AES-256 加密)在 Intel i5-12400 上测试,单线程大约每秒 40 次尝试,4 线程约每秒 120 次。听起来很慢吧?确实慢,原因在于 PBKDF2 的 1000 次迭代让每次解密尝试都要消耗大量 CPU。但如果是老的 ZipCrypto,单线程每秒能到 3000 次以上,4 线程接近 10000 次。这两个数据直接决定了你的策略选择:对 AES 加密的包,纯暴力只能覆盖 6 位以内的小写数字密码,再多就是在赌运气;对 ZipCrypto,8 位小写字母也能在一个小时内跑完。日常运维里如果遇到打不开的压缩包,第一选择永远是先试试对方常用的密码集合,比如公司名称加年份、手机号后六位这类社会工程学字典,纯穷举是最低效的手段。

4. 可视化界面:tkinter 与线程间的安全通信

4.1 线程和 GUI 不吵架的通信设计

tkinter 是 Python 标准库自带的 GUI 框架,对于这种工具型软件完全够用,而且免去 PyQt5 动辄几百 MB 的打包体积。但 tkinter 有一个硬性要求:所有界面更新操作必须在主线程执行。后台爆破线程不能直接调用progress_var.set()label.config(text=...),否则轻则界面卡死、重则崩溃。解决方案是在主线程轮询一个queue.Queue,工作线程只把进度数据往队列里塞,主线程用一个定时器定期取。

import tkinter as tk from tkinter import ttk, filedialog, messagebox from queue import Queue, Empty import threading import os from cracker import ArchiveCracker, ArchiveType from engine import BruteForceEngine class CrackerApp: """压缩包密码破解可视化界面""" def __init__(self, root: tk.Tk): self.root = root self.root.title("压缩包密码爆破工具") self.root.geometry("640x520") self.progress_queue: Queue = Queue() # 工作线程向这里写数据 self.engine: BruteForceEngine | None = None self.worker_thread: threading.Thread | None = None self._build_widgets() # 每 100ms 轮询一次队列,检查进度和结果 self.root.after(100, self._poll_progress_queue) def _build_widgets(self): """组装界面控件""" main_frame = ttk.Frame(self.root, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) # 文件选择行 file_row = ttk.Frame(main_frame) file_row.pack(fill=tk.X, pady=5) ttk.Label(file_row, text="压缩包:").pack(side=tk.LEFT) self.file_path_var = tk.StringVar() ttk.Entry(file_row, textvariable=self.file_path_var).pack( side=tk.LEFT, fill=tk.X, expand=True, padx=5 ) ttk.Button(file_row, text="浏览", command=self._select_file).pack(side=tk.RIGHT) # 线程数 thread_row = ttk.Frame(main_frame) thread_row.pack(fill=tk.X, pady=5) ttk.Label(thread_row, text="线程数:").pack(side=tk.LEFT) self.workers_var = tk.IntVar(value=4) ttk.Spinbox( thread_row, from_=1, to=16, textvariable=self.workers_var, width=5 ).pack(side=tk.LEFT, padx=5) # 字符集选择 charset_row = ttk.Frame(main_frame) charset_row.pack(fill=tk.X, pady=5) ttk.Label(charset_row, text="字符集:").pack(side=tk.LEFT) self.charset_var = tk.StringVar(value="abcdefghijklmnopqrstuvwxyz0123456789") ttk.Entry(charset_row, textvariable=self.charset_var).pack( side=tk.LEFT, fill=tk.X, expand=True ) # 密码长度范围 len_row = ttk.Frame(main_frame) len_row.pack(fill=tk.X, pady=5) ttk.Label(len_row, text="最小长度:").pack(side=tk.LEFT) self.min_len_var = tk.IntVar(value=1) ttk.Spinbox(len_row, from_=1, to=20, textvariable=self.min_len_var, width=4).pack( side=tk.LEFT ) ttk.Label(len_row, text="最大长度:").pack(side=tk.LEFT, padx=(10, 0)) self.max_len_var = tk.IntVar(value=6) ttk.Spinbox(len_row, from_=1, to=20, textvariable=self.max_len_var, width=4).pack( side=tk.LEFT ) # 进度条和状态 self.progress_var = tk.DoubleVar(value=0) ttk.Progressbar( main_frame, variable=self.progress_var, maximum=100, length=400 ).pack(fill=tk.X, pady=10) self.status_var = tk.StringVar(value="就绪") ttk.Label(main_frame, textvariable=self.status_var).pack(fill=tk.X) # 结果日志 self.log_text = tk.Text(main_frame, height=10, state=tk.DISABLED) self.log_text.pack(fill=tk.BOTH, expand=True, pady=10) # 操作按钮 btn_row = ttk.Frame(main_frame) btn_row.pack(fill=tk.X) self.start_btn = ttk.Button(btn_row, text="开始破解", command=self._start_crack) self.start_btn.pack(side=tk.LEFT) self.stop_btn = ttk.Button( btn_row, text="停止", command=self._stop_crack, state=tk.DISABLED ) self.stop_btn.pack(side=tk.LEFT, padx=5) # 以下是控件的回调方法 def _select_file(self): """选择压缩包,自动识别格式""" filepath = filedialog.askopenfilename( filetypes=[ ("压缩包", "*.zip *.rar *.7z"), ("ZIP", "*.zip"), ("RAR", "*.rar"), ("7Z", "*.7z"), ] ) if filepath: self.file_path_var.set(filepath) ext = os.path.splitext(filepath)[-1].lower() self._log(f"已选择文件: {os.path.basename(filepath)} (格式: {ext})") def _start_crack(self): """启动爆破,在后台线程中运行引擎""" filepath = self.file_path_var.get().strip() if not filepath or not os.path.exists(filepath): messagebox.showerror("错误", "请先选择有效的压缩包文件") return ext = os.path.splitext(filepath)[-1].lower() archive_type_map = {".zip": ArchiveType.ZIP, ".rar": ArchiveType.RAR, ".7z": ArchiveType.SEVEN_ZIP} archive_type = archive_type_map.get(ext) if archive_type is None: messagebox.showerror("错误", "不支持的压缩包格式") return # 创建引擎对象 cracker = ArchiveCracker(filepath, archive_type) charset = self.charset_var.get() workers = self.workers_var.get() self.engine = BruteForceEngine( cracker=cracker, charset=charset, min_length=self.min_len_var.get(), max_length=self.max_len_var.get(), workers=workers, ) # 启动后台线程 self.worker_thread = threading.Thread(target=self.engine.run, daemon=True) self.worker_thread.start() self.start_btn.config(state=tk.DISABLED) self.stop_btn.config(state=tk.NORMAL) self.status_var.set("正在爆破...") self._log(f"开始爆破: {filepath} | 字符集长度 {len(charset)} | {workers} 线程") def _stop_crack(self): """通知引擎停止""" if self.engine: self.engine.cancel() self._log("收到停止请求,等待当前批次完成...") self.stop_btn.config(state=tk.DISABLED) def _poll_progress_queue(self): """主线程定时轮询队列,更新界面""" try: while True: msg = self.progress_queue.get_nowait() if msg["type"] == "progress": self.status_var.set( f"已尝试 {msg['tried']:,} 个密码 | " f"速度 {msg['speed']:.0f}/s | 用时 {msg['elapsed']:.1f}s" ) elif msg["type"] == "found": pwd = msg["password"] self.status_var.set(f"密码已找到: {pwd}") self._log(f"破解成功! 密码是: {pwd}") self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) elif msg["type"] == "error": self._log(f"错误: {msg['message']}") self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) except Empty: pass # 继续轮询 self.root.after(100, self._poll_progress_queue) def _log(self, message: str): """往日志文本框追加一行""" self.log_text.config(state=tk.NORMAL) self.log_text.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {message}\n") self.log_text.see(tk.END) self.log_text.config(state=tk.DISABLED)

这段代码你在本地跑的时候需要把文件拆成 cracker.py、engine.py、app.py 三个模块,注意 import 路径对应。设计中值得说明的细节有三个:第一,daemon=True是为了防止用户直接关窗口时线程还挂着导致进程不能退出;第二,停止操作是“软停止”,引擎只会在完成当前批次后检查标志位,不会中断正在试的密码,这样能避免文件句柄泄漏;第三,进度条用 DoubleVar 是因为我需要计算百分比,但爆破任务没有总量上限(生成器是无限的),所以这里实际上展示的不是百分比而是速率信息,如果你需要真正的百分比,必须在生成器外层套一个可计数的包装器,用计数器除以当前长度字段的总组合数。

5. 参数调优与绕过误报的三个实战姿势

5.1 线程数和字典顺序的调参思路

你把这个工具跑起来之后会发现,默认参数大概率不是最优解。我对不同场景的调参建议如下表:

场景线程数字符集长度范围预期效果
老 ZIP(ZipCrypto)8小写+数字1-8秒级到分钟级
AES 加密 ZIP4纯数字1-630 分钟内
RAR 5 加密4小写+数字1-7数小时,看运气
已知对方用生日类密码4数字+特殊字符6-8大幅缩减空间

5.2 字典文件支持:比暴力更快

如果写入一个常见的弱密码字典,会显著提升命中率。在界面里加一个“字典文件”选择框,然后把password_factory替换成读取本地文件逐行 yield 的生成器:

def dict_factory(dict_path: str) -> Iterator[str]: """从字典文件逐行读取密码,自动去除换行符和空行""" with open(dict_path, "r", encoding="utf-8", errors="ignore") as fp: for line in fp: pwd = line.strip() if pwd: yield pwd

这个文件编码问题极易踩坑:Windows 下创建的字典可能是 GBK 编码,你按 UTF-8 读会乱码导致密码错误。我的处理方式是先以二进制读取,再用chardetcchardet探测编码,或者直接提供编码选择下拉框。实际项目中,我通常先跑字典,再跑掩码规则(比如字典词 + 两位数字),最后才跑纯暴力。

5.3 误报排查:密码正确但解压失败的三种情况

  • 文件头损坏:有些压缩包在传输过程中被截断,密码验证通过但解压到一半报 CRC 错误。这种情况下你拿到的密码其实是“正确的”,但文件不可用。验证方法是在_try_zip_standard中读前 4 个字节后检查 CRC32 是否和ZipInfo.CRC一致。
  • 编码不一致:密码中包含非 ASCII 字符,创建压缩包时用的是 GBK,测试时用 UTF-8 编码,结果正确密码被当成错误密码。解决方式是尝试多种编码,界面里加一个“密码编码”下拉框,选项为 UTF-8/GBK/GB18030。
  • 嵌套加密:压缩包里多个文件用不同密码。这个工具的验证逻辑读的是第一个加密文件,如果用户只改了第二个文件的密码,工具永远不会成功。处理方法是遍历infolist()中所有需要密码的文件,全部验证通过才算成功,代价是尝试速度会按文件数量成倍下降。
def _try_zip_standard(self, password: str) -> bool: try: with zipfile.ZipFile(self.filepath) as zf: # 遍历所有加密文件,全部能读取才判定密码正确 for info in zf.infolist(): if info.flag_bits & 0x1: # 第0位表示加密 zf.open(info, pwd=password.encode("utf-8")).read(1) return True except (RuntimeError, zipfile.BadZipFile): return False

判断文件是否加密用了flag_bits & 0x1,这是 ZIP 文件头里的通用位标志,第 0 位置 1 表示文件内容加密。这个遍历逻辑在 pyzipper 里同样适用,但rarfile没有对应的标志位判断,只能用异常捕获。这套统一封装能做到在不同格式之间切换时不需要改动上层代码。

6. 用一段性能压测验证工具可靠性

拿到代码后第一件事不是找真实压缩包去破解,而是先做一个可控的验证实验。自己生成一个密码为 “abc123” 的 7Z 文件,然后用这个工具去跑,看能不能正确识别。这能同时验证 py7zr 的读写兼容性和多线程引擎的状态管理。压测代码如下:

# 用系统的 7z 命令生成一个加密测试包 # 密码设为 abc123,使用 AES-256 加密 7z a -pabc123 -mhe=on test.7z /path/to/sample.txt # 确认生成的包是 AES-256 加密 7z l -slt test.7z | grep -E "Method|Encryption"

压测目的不只是找到密码,而是观察两个指标:每秒尝试次数是否随线程数线性增长,以及停止按钮按下到引擎真正退出之间有没有卡死。我一般用timeout 60 python app.py做超时保护运行 60 秒,如果 60 秒后进程没有自动退出,说明线程池没有正确回收,需要检查shutdown(cancel_futures=True)的调用路径。另外还有一个常用的方法,在爆破过程中打开系统资源监视器观察 CPU 使用率:如果你设了 8 个线程但 CPU 总占用率不到 50%,说明压缩包读取的 IO 等待成了瓶颈,此时降低线程数反而更快。RAR 文件尤其明显,unrar子进程每次启动加载要几百毫秒,线程数加到 16 反而拖慢整体。验证环境里我建议直接打一组从 1 到 8 线程的对比基准,记录每组跑完 10000 个候选密码的耗时,选耗时曲线的拐点作为默认线程数。代码日志里的速度字段会帮你自动记录这些数据,跑完对比一下即可。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询