好友
阅读权限 10
听众
最后登录 1970-1-1
本程序使用python,使用了qt5,需要安装pip install pikepdf tqdm pyqt5
源码里面的CHARACTERS = (是自定义字符集的区域,根据自己的需要填写,字符越多破解 的时间就越长。
以下为源代码
import pikepdf
import itertools
import sys
import time
import os
from datetime import datetime
import multiprocessing
from multiprocessing import Pool
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QLineEdit, QPushButton,
QTextEdit, QMessageBox, QSpinBox,
QGridLayout, QGroupBox, QFileDialog
)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QObject
from PyQt5.QtGui import QTextCursor, QFont
# 定义破解使用的字符集
CHARACTERS = (
'ABCD' # 大写字母
'abc' # 小写字母
'0123456789' # 数字
'#' # 特殊符号
)
def try_password(args):
"""尝试使用给定密码打开PDF文件(纯函数,可序列化)"""
pdf_path, password = args
try:
with pikepdf.open(pdf_path, password=password):
return password
except pikepdf.PasswordError:
return None
except Exception as e:
return f"ERROR:{password}:{str(e)}"
def generate_passwords(min_length, max_length):
"""生成指定长度范围内的所有字符组合密码(纯生成器)"""
for length in range(min_length, max_length + 1):
for pwd_tuple in itertools.product(CHARACTERS, repeat=length):
yield ''.join(pwd_tuple)
def crack_core(pdf_path, min_length, max_length, max_workers, progress_queue, stop_event):
pdf_path = os.path.abspath(pdf_path)
if not os.path.isfile(pdf_path):
progress_queue.put(("ERROR", "文件不存在: " + pdf_path))
return None
# 计算总密码数
char_count = len(CHARACTERS)
total_passwords = sum(char_count ** length for length in range(min_length, max_length + 1))
progress_queue.put(("LOG", f"尝试范围: {min_length}-{max_length}位字符"))
progress_queue.put(("LOG", f"字符集大小: {char_count}个字符"))
progress_queue.put(("LOG", f"密码组合总数: {total_passwords:,}"))
progress_queue.put(("LOG", f"使用进程数: {max_workers} (CPU核心数: {multiprocessing.cpu_count()})"))
start_time = time.time()
tested = 0
found_password = None
# 初始化进程池
pool = Pool(processes=max_workers)
password_generator = generate_passwords(min_length, max_length)
batch_size = max_workers * 10
futures = []
# 提交初始批次任务
try:
for _ in range(batch_size):
if stop_event.is_set():
break
pwd = next(password_generator)
futures.append(pool.apply_async(try_password, args=((pdf_path, pwd),)))
except StopIteration:
pass
# 处理任务结果
while futures and not stop_event.is_set():
# 检查已完成的任务
for i, future in enumerate(futures):
if future.ready():
tested += 1
result = future.get()
futures.pop(i)
# 更新进度
progress = int((tested / total_passwords) * 100) if total_passwords > 0 else 0
progress_queue.put(("PROGRESS", (progress, f"已尝试: {tested:,} / {total_passwords:,}")))
# 处理结果
if result is None:
pass # 密码错误
elif result.startswith("ERROR"):
_, pwd, err = result.split(":", 2)
progress_queue.put(("LOG", f"尝试密码 {pwd} 出错: {err}"))
else:
# 找到密码
found_password = result
stop_event.set()
pool.terminate() # 终止所有子进程
break
# 补充新任务
try:
while len(futures) < batch_size and not stop_event.is_set():
pwd = next(password_generator)
futures.append(pool.apply_async(try_password, args=((pdf_path, pwd),)))
except StopIteration:
pass
# 清理资源
pool.close()
pool.join()
elapsed = time.time() - start_time
# 最终结果
if found_password:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
save_msg = ""
try:
with open('pdf_password.txt', 'w', encoding='utf-8') as f:
f.write(f"破解时间: {timestamp}\n")
f.write(f"PDF文件: {pdf_path}\n")
f.write(f"密码: {found_password}\n")
save_msg = f" 密码已保存到: {os.path.abspath('pdf_password.txt')}"
except Exception as e:
save_msg = f" 密码保存失败: {str(e)}"
progress_queue.put(("LOG", f"\n尝试了 {tested:,} 个密码,耗时 {elapsed:.2f} 秒"))
progress_queue.put(("LOG", f"平均速度: {tested/elapsed:.2f} 个密码/秒"))
progress_queue.put(("FINISH", (True, f"✅ 成功破解! 时间: {timestamp} 密码是: {found_password}{save_msg}")))
else:
progress_queue.put(("LOG", f"\n尝试了 {tested:,} 个密码,耗时 {elapsed:.2f} 秒"))
progress_queue.put(("LOG", f"平均速度: {tested/elapsed:.2f} 个密码/秒"))
progress_queue.put(("FINISH", (False, "❌ 未找到正确密码,请尝试扩大密码长度范围或调整字符集")))
# ---------------------------
# Qt线程部分(负责监听进程通信队列,更新界面)
# ---------------------------
class CrackThread(QThread):
progress_update = pyqtSignal(int, str)
log_update = pyqtSignal(str)
finished = pyqtSignal(bool, str)
def __init__(self, pdf_path, min_length, max_length, max_workers):
super().__init__()
self.pdf_path = pdf_path
self.min_length = min_length
self.max_length = max_length
self.max_workers = max_workers
self.stop_event = multiprocessing.Event() # 进程间停止信号
self.progress_queue = multiprocessing.Queue() # 进程间通信队列
def run(self):
# 启动破解核心进程(单独的子进程,避免阻塞Qt线程)
crack_process = multiprocessing.Process(
target=crack_core,
args=(self.pdf_path, self.min_length, self.max_length, self.max_workers,
self.progress_queue, self.stop_event)
)
crack_process.start()
# 监听队列,更新界面
while crack_process.is_alive() or not self.progress_queue.empty():
try:
# 非阻塞读取队列
msg_type, msg = self.progress_queue.get(block=True, timeout=0.1)
if msg_type == "LOG":
self.log_update.emit(msg)
elif msg_type == "PROGRESS":
progress, info = msg
self.progress_update.emit(progress, info)
elif msg_type == "FINISH":
success, result = msg
self.finished.emit(success, result)
elif msg_type == "ERROR":
self.log_update.emit(f"错误: {msg}")
self.finished.emit(False, f"错误: {msg}")
except:
continue
crack_process.join()
def stop(self):
self.stop_event.set()
# ---------------------------
# Qt主窗口
# ---------------------------
class PDFCrackWindow(QMainWindow):
def __init__(self):
super().__init__()
self.crack_thread = None
self.init_ui()
def init_ui(self):
# 确保中文显示正常
try:
self.font = QFont("SimHei")
except:
self.font = QFont("Arial")
self.setWindowTitle("PDF文件打开密码破解工具")
self.setGeometry(100, 100, 800, 600)
self.setStyleSheet("""
QMainWindow {background-color: #f0f0f0;}
QGroupBox {font-weight: bold; margin-top: 10px;}
QPushButton {background-color: #4CAF50; color: white; padding: 8px; border-radius: 4px;}
QPushButton:disabled {background-color: #cccccc;}
QPushButton#stopBtn {background-color: #f44336;}
QTextEdit {font-family: Consolas; font-size: 12px;}
QSpinBox, QLineEdit {padding: 4px;}
""")
# 中心部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(15)
main_layout.setContentsMargins(20, 20, 20, 20)
# 1. 文件选择区域
file_group = QGroupBox("PDF文件选择")
file_layout = QHBoxLayout(file_group)
self.file_edit = QLineEdit()
self.file_edit.setPlaceholderText("请选择需要破解的PDF文件路径")
file_btn = QPushButton("浏览")
file_btn.clicked.connect(self.select_pdf_file)
file_layout.addWidget(self.file_edit)
file_layout.addWidget(file_btn)
main_layout.addWidget(file_group)
# 2. 破解参数设置区域
param_group = QGroupBox("破解参数设置")
param_layout = QGridLayout(param_group)
param_layout.addWidget(QLabel("密码长度范围:"), 0, 0)
self.min_len_spin = QSpinBox()
self.min_len_spin.setRange(1, 20)
self.min_len_spin.setValue(1)
param_layout.addWidget(self.min_len_spin, 0, 1)
param_layout.addWidget(QLabel("至"), 0, 2)
self.max_len_spin = QSpinBox()
self.max_len_spin.setRange(1, 20)
self.max_len_spin.setValue(6)
param_layout.addWidget(self.max_len_spin, 0, 3)
param_layout.addWidget(QLabel("进程数:"), 1, 0)
self.worker_spin = QSpinBox()
self.worker_spin.setRange(1, multiprocessing.cpu_count())
self.worker_spin.setValue(multiprocessing.cpu_count())
param_layout.addWidget(self.worker_spin, 1, 1)
charset_label = QLabel("字符集: ABCD + abc + 数字 + #")
charset_label.setAlignment(Qt.AlignLeft)
param_layout.addWidget(charset_label, 1, 2, 1, 2)
main_layout.addWidget(param_group)
# 3. 控制按钮区域
btn_layout = QHBoxLayout()
self.start_btn = QPushButton("开始破解")
self.start_btn.clicked.connect(self.start_cracking)
self.stop_btn = QPushButton("停止破解")
self.stop_btn.setObjectName("stopBtn")
self.stop_btn.clicked.connect(self.stop_cracking)
self.stop_btn.setEnabled(False)
btn_layout.addWidget(self.start_btn)
btn_layout.addWidget(self.stop_btn)
btn_layout.addStretch()
main_layout.addLayout(btn_layout)
# 4. 进度展示区域
progress_group = QGroupBox("破解进度")
progress_layout = QVBoxLayout(progress_group)
self.progress_label = QLabel("等待开始...")
self.progress_label.setAlignment(Qt.AlignCenter)
progress_layout.addWidget(self.progress_label)
main_layout.addWidget(progress_group)
# 5. 日志输出区域
log_group = QGroupBox("日志输出")
log_layout = QVBoxLayout(log_group)
self.log_edit = QTextEdit()
self.log_edit.setReadOnly(True)
self.log_edit.setFont(self.font)
log_layout.addWidget(self.log_edit)
main_layout.addWidget(log_group)
def select_pdf_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self, "选择PDF文件", "", "PDF Files (*.pdf);;All Files (*.*)"
)
if file_path:
self.file_edit.setText(file_path)
def append_log(self, text):
self.log_edit.append(text)
cursor = self.log_edit.textCursor()
cursor.movePosition(QTextCursor.End)
self.log_edit.setTextCursor(cursor)
def update_progress(self, progress, info):
self.progress_label.setText(f"{info} (进度: {progress}%)")
def start_cracking(self):
pdf_path = self.file_edit.text().strip()
if not pdf_path or not os.path.isfile(pdf_path):
QMessageBox.warning(self, "警告", "请选择有效的PDF文件路径!")
return
min_len = self.min_len_spin.value()
max_len = self.max_len_spin.value()
if min_len > max_len:
QMessageBox.warning(self, "警告", "最小长度不能大于最大长度!")
return
workers = self.worker_spin.value()
# 重置界面状态
self.log_edit.clear()
self.progress_label.setText("准备开始...")
self.start_btn.setEnabled(False)
self.stop_btn.setEnabled(True)
# 启动破解线程
self.crack_thread = CrackThread(pdf_path, min_len, max_len, workers)
self.crack_thread.progress_update.connect(self.update_progress)
self.crack_thread.log_update.connect(self.append_log)
self.crack_thread.finished.connect(self.on_crack_finished)
self.crack_thread.start()
def stop_cracking(self):
if self.crack_thread:
self.crack_thread.stop()
self.progress_label.setText("正在停止破解...")
self.stop_btn.setEnabled(False)
def on_crack_finished(self, success, msg):
self.start_btn.setEnabled(True)
self.stop_btn.setEnabled(False)
if success:
QMessageBox.information(self, "成功", msg)
else:
QMessageBox.warning(self, "失败", msg)
self.crack_thread = None
if __name__ == "__main__":
# 解决多进程在Windows下的兼容问题
multiprocessing.freeze_support()
app = QApplication(sys.argv)
window = PDFCrackWindow()
window.show()
sys.exit(app.exec_())
免费评分
查看全部评分