ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python多线程压缩包密码破解:ZIP/RAR/7Z暴力破解实战

Python多线程压缩包密码破解:ZIP/RAR/7Z暴力破解实战 简介针对日常忘记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 命令需要安装 unrarpy7zr7ZAES-256-CBC纯 Python低选型时有一个容易被新手忽略的坑rarfile 虽然叫“纯 Python 库”但它只是解析器真正解密和提取还是要落到 unrar 可执行文件上。我自己一般在 Linux 服务器上做批量测试时就直接用 apt 装 unrar-freeWindows 开发机上装 WinRAR 后把它安装目录下的 UnRAR.exe 单独拷到项目 bin/ 目录里这样 rarfile.UNRAR_TOOL 指向相对路径不会因为机器不同而找不到命令。pyzipper 有一点要注意AESZipFile和普通ZipFile在解压失败时的报错类型不同pyzipper 会抛RuntimeError而标准库抛BadZipFile。写统一封装层时必须把两种异常都捕获到否则线程池里一个异常抛出来整个 worker 就中断了。密码错误时 py7zr 抛的是py7zr.exceptions.PasswordRequired和Bad7zFile的混合情况我在实战里遇到过密码对但 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, pwdpassword.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, pwdpassword.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, pwdpassword.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, moder, passwordpassword) 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, repeatlength): 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_workersself.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_futuresTrue) 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_futuresTrue)确保未开始的任务被取消已完成的任务正常回收。参数方面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 位以内的小写数字密码再多就是在赌运气对 ZipCrypto8 位小写字母也能在一个小时内跑完。日常运维里如果遇到打不开的压缩包第一选择永远是先试试对方常用的密码集合比如公司名称加年份、手机号后六位这类社会工程学字典纯穷举是最低效的手段。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, padding10) main_frame.pack(filltk.BOTH, expandTrue) # 文件选择行 file_row ttk.Frame(main_frame) file_row.pack(filltk.X, pady5) ttk.Label(file_row, text压缩包:).pack(sidetk.LEFT) self.file_path_var tk.StringVar() ttk.Entry(file_row, textvariableself.file_path_var).pack( sidetk.LEFT, filltk.X, expandTrue, padx5 ) ttk.Button(file_row, text浏览, commandself._select_file).pack(sidetk.RIGHT) # 线程数 thread_row ttk.Frame(main_frame) thread_row.pack(filltk.X, pady5) ttk.Label(thread_row, text线程数:).pack(sidetk.LEFT) self.workers_var tk.IntVar(value4) ttk.Spinbox( thread_row, from_1, to16, textvariableself.workers_var, width5 ).pack(sidetk.LEFT, padx5) # 字符集选择 charset_row ttk.Frame(main_frame) charset_row.pack(filltk.X, pady5) ttk.Label(charset_row, text字符集:).pack(sidetk.LEFT) self.charset_var tk.StringVar(valueabcdefghijklmnopqrstuvwxyz0123456789) ttk.Entry(charset_row, textvariableself.charset_var).pack( sidetk.LEFT, filltk.X, expandTrue ) # 密码长度范围 len_row ttk.Frame(main_frame) len_row.pack(filltk.X, pady5) ttk.Label(len_row, text最小长度:).pack(sidetk.LEFT) self.min_len_var tk.IntVar(value1) ttk.Spinbox(len_row, from_1, to20, textvariableself.min_len_var, width4).pack( sidetk.LEFT ) ttk.Label(len_row, text最大长度:).pack(sidetk.LEFT, padx(10, 0)) self.max_len_var tk.IntVar(value6) ttk.Spinbox(len_row, from_1, to20, textvariableself.max_len_var, width4).pack( sidetk.LEFT ) # 进度条和状态 self.progress_var tk.DoubleVar(value0) ttk.Progressbar( main_frame, variableself.progress_var, maximum100, length400 ).pack(filltk.X, pady10) self.status_var tk.StringVar(value就绪) ttk.Label(main_frame, textvariableself.status_var).pack(filltk.X) # 结果日志 self.log_text tk.Text(main_frame, height10, statetk.DISABLED) self.log_text.pack(filltk.BOTH, expandTrue, pady10) # 操作按钮 btn_row ttk.Frame(main_frame) btn_row.pack(filltk.X) self.start_btn ttk.Button(btn_row, text开始破解, commandself._start_crack) self.start_btn.pack(sidetk.LEFT) self.stop_btn ttk.Button( btn_row, text停止, commandself._stop_crack, statetk.DISABLED ) self.stop_btn.pack(sidetk.LEFT, padx5) # 以下是控件的回调方法 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( crackercracker, charsetcharset, min_lengthself.min_len_var.get(), max_lengthself.max_len_var.get(), workersworkers, ) # 启动后台线程 self.worker_thread threading.Thread(targetself.engine.run, daemonTrue) self.worker_thread.start() self.start_btn.config(statetk.DISABLED) self.stop_btn.config(statetk.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(statetk.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(statetk.NORMAL) self.stop_btn.config(statetk.DISABLED) elif msg[type] error: self._log(f错误: {msg[message]}) self.start_btn.config(statetk.NORMAL) self.stop_btn.config(statetk.DISABLED) except Empty: pass # 继续轮询 self.root.after(100, self._poll_progress_queue) def _log(self, message: str): 往日志文本框追加一行 self.log_text.config(statetk.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(statetk.DISABLED)这段代码你在本地跑的时候需要把文件拆成 cracker.py、engine.py、app.py 三个模块注意 import 路径对应。设计中值得说明的细节有三个第一daemonTrue是为了防止用户直接关窗口时线程还挂着导致进程不能退出第二停止操作是“软停止”引擎只会在完成当前批次后检查标志位不会中断正在试的密码这样能避免文件句柄泄漏第三进度条用 DoubleVar 是因为我需要计算百分比但爆破任务没有总量上限生成器是无限的所以这里实际上展示的不是百分比而是速率信息如果你需要真正的百分比必须在生成器外层套一个可计数的包装器用计数器除以当前长度字段的总组合数。5. 参数调优与绕过误报的三个实战姿势5.1 线程数和字典顺序的调参思路你把这个工具跑起来之后会发现默认参数大概率不是最优解。我对不同场景的调参建议如下表场景线程数字符集长度范围预期效果老 ZIPZipCrypto8小写数字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, encodingutf-8, errorsignore) as fp: for line in fp: pwd line.strip() if pwd: yield pwd这个文件编码问题极易踩坑Windows 下创建的字典可能是 GBK 编码你按 UTF-8 读会乱码导致密码错误。我的处理方式是先以二进制读取再用chardet或cchardet探测编码或者直接提供编码选择下拉框。实际项目中我通常先跑字典再跑掩码规则比如字典词 两位数字最后才跑纯暴力。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, pwdpassword.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 -mheon 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_futuresTrue)的调用路径。另外还有一个常用的方法在爆破过程中打开系统资源监视器观察 CPU 使用率如果你设了 8 个线程但 CPU 总占用率不到 50%说明压缩包读取的 IO 等待成了瓶颈此时降低线程数反而更快。RAR 文件尤其明显unrar子进程每次启动加载要几百毫秒线程数加到 16 反而拖慢整体。验证环境里我建议直接打一组从 1 到 8 线程的对比基准记录每组跑完 10000 个候选密码的耗时选耗时曲线的拐点作为默认线程数。代码日志里的速度字段会帮你自动记录这些数据跑完对比一下即可。本文还有配套的精品资源点击获取
返回列表