吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 226|回复: 7
上一主题 下一主题
收起左侧

[Python 转载] pdf文件打开密码破解,忘记密码时使用

[复制链接]
跳转到指定楼层
楼主
life9999 发表于 2026-1-27 15:43 回帖奖励
本程序使用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"&#9989; 成功破解! 时间: {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, "&#10060; 未找到正确密码,请尝试扩大密码长度范围或调整字符集")))

# ---------------------------
# 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_())

crack.JPG (57.58 KB, 下载次数: 2)

程序运行界面

程序运行界面

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
helian147 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
fengtian99 发表于 2026-1-28 10:04
这个暴力破解,感觉并不靠谱吧,只有简单的密码能解决
3#
wanghy2023 发表于 2026-1-28 11:41
4#
 楼主| life9999 发表于 2026-1-28 16:07 |楼主
wanghy2023 发表于 2026-1-28 11:41
楼主太牛了,有时候确实需要破解密码。

不适合破解别人的密码,适合破解自己忘记的密码,知道哪些字母、数字和符号,把我程序里面的字符集修改一下,都能跑出来。别人要是设置的密码很长,字符很多,这种暴力破解需要很长时间。
5#
yihui2003 发表于 2026-1-29 08:36
学习了,谢谢楼主
6#
lxw804 发表于 2026-1-29 15:09
是暴力破解的思路,受秘钥长度限制,现在的秘钥都很长,这样暴破的时间成本太高
7#
chchmai 发表于 2026-1-29 20:56
自己用于学习可以,想用来破解他人密码不现实
8#
sqsqsqawhq123 发表于 2026-1-30 13:54
跑字典有点困难     不过很多pdf只是锁定不能更改    我觉得可以嵌入这个功能为主  暴力破解为辅可能好一点
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-1-30 15:13

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表