Python-PDF文件密码移除小工具

在这里插入图片描述

背景

PDF文件的密码分为两种,一种是编辑限制密码,一种是打开密码;设置了编辑限制密码时,文档可以打开,但是无法打印、编辑或添加书签;设置了打开密码的时候,则文件的查看受限制,必须输入密码方可打开。

本工具是基于tkinter、python完成的,拥有简单的GUI、进度条、选择破解字典等功能。

下载必要的库

# 处理PDF文件的库       
pip install pypdf2                                                     
# GUI库     
pip install tkinter          
# 加解密相关库                                          
pip install crypto                                                     

代码

import os
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
#pdf的读取方法
from PyPDF2 import PdfReader
#pdf的写入方法
from PyPDF2 import PdfWriter
#高加密的方法,要引入不然会报错
from Crypto.Cipher import AES
from tkinter import ttk

from pyparsing import Or
# 设置一个终止标识
stop_thread = False

def count_lines_in_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return sum(1 for _ in file)

def select_input_file(root, input_file_path_var):
    input_file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.PDF;*.pdf")])
    if input_file_path:
        input_file_path_var.delete(0, tk.END)
        input_file_path_var.insert(0, input_file_path)
    return input_file_path

def select_cracker_dic(root,input_crack_dic_var):
    crack_dic = filedialog.askopenfilename(filetypes=[("TXT file","*.txt;*.TXT")])
    if crack_dic:
        input_crack_dic_var.delete(0,tk.END)
        input_crack_dic_var.insert(0,crack_dic)
        return crack_dic



# 定义一个函数来读取PDF文件
def get_reader(filename, password=None):

    try:
        old_file = open(filename, 'rb')
    except Exception as err:
        return print('文件打开失败!' + str(err))
    #如果使用python2需要将PdfReader改为PdfFileReader
    pdf_reader = PdfReader(old_file, strict=False)
    # 没有传入密码,则直接返回读取的PDF
    if password == None:
        return pdf_reader

    # 如果使用python2需要将is_encrypted改为isEncrypted
    # 执行解密操作
    if pdf_reader.is_encrypted:
        if password is None:
            return print('文件被加密,需要密码!--{}'.format(filename))
        else:
            success = pdf_reader.decrypt(password)
            if not success :
                #return print('密码不正确!--{}'.format(filename))
                return
            else:
                #print("解密成功,密码为{}".format(password))
                return pdf_reader
    elif old_file in locals():
        old_file.close()
        # 返回结果
    return pdf_reader

def deception_pdf(root,filename, progress,progress_label,decrypted_filename=None,crack_dic=None,password=None) :
    global stop_thread
    if crack_dic is None or crack_dic == '':
        pdf_reader = get_reader(filename, password)
        if pdf_reader is None:
            return print("无内容读取")
        # 如果使用python2需要将is_encrypted改为isEncrypted
        elif not pdf_reader.is_encrypted:
            return print('文件没有被加密,无需操作')

        # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
        pdf_writer = PdfWriter()

        #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
        pdf_writer.append_pages_from_reader(pdf_reader)
        #创建解密后的pdf文件和展示文件的路径
        if decrypted_filename is None:
            decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
            print("解密文件已生成:{}".format(decrypted_filename))
        # 写入新文件
        pdf_writer.write(open(decrypted_filename, 'wb'))
        root.destroy()
    else :
        countlines = count_lines_in_file(crack_dic)
        progress['maximum'] = countlines
        with open(crack_dic,'r') as crack_dic_file:

            count = 1

            pdf_reader = None
            for password in crack_dic_file:
                if stop_thread:
                    break
                #print("当前的密码为{}".format(password))
                password = password.strip()
                count = count + 1
                progress['value'] = count
                progress_label['text'] = "已完成:{:.1f}%".format((progress["value"]/countlines)*100)
                root.update_idletasks()
                pdf_reader = get_reader(filename, password)
                if(pdf_reader):
                    # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                    pdf_writer = PdfWriter()
                    #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                    pdf_writer.append_pages_from_reader(pdf_reader)
                    #创建解密后的pdf文件和展示文件的路径
                    if decrypted_filename is None:
                        decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                        #print("解密文件已生成:{},原来的密码为:{}".format(decrypted_filename,password))
                    # 写入新文件
                    pdf_writer.write(open(decrypted_filename, 'wb'))
                    messagebox.showinfo("解密完成", "您的PDF文件已破解完成,密码为{}".format(password))
                    root.destroy()
                    break
                else:
                    if(count<countlines):
                        #print(("正在尝试第{}个密码==>{}".format(count,password)))
                        pass
                    else:
                        if(pdf_reader==None):
                            messagebox.showinfo("标题", "抱歉,未找到匹配的密码,请换个字典库再试一下吧")
                            break


def closeApp(root):
    root.destroy()

def start_deception_pdf_thread(root, filename, progress, progress_label, crack_dic, decrypted_filename=None,password=None):
    global stop_thread
    stop_thread = False  # 重置终止标志
    thread = threading.Thread(target=deception_pdf, args=(root, filename , progress, progress_label, decrypted_filename,crack_dic,password))
    thread.start()

# 创建主函数
def main():
    # 创建主窗口
    root = tk.Tk()
    root.title("PDF解密小工具")
    root.geometry("600x300")
    root.resizable(False, False)
    # 创建一个Tkinter变量来存储文件路径
    input_file_path_var = tk.StringVar()
    # 输入文件框
    input_frame = tk.Frame(root)
    input_frame.grid(column=0, row=0, padx=0, pady=10,sticky="w")
    input_label = tk.Label(input_frame, text="输入文件:")
    input_label.grid(column=0, row=0, padx=17, pady=10,sticky="w")
    input_file_path_var = tk.Entry(input_frame, width=50)
    input_file_path_var.grid(column=1, row=0, padx=10, pady=10,sticky="e")
    input_button = tk.Button(input_frame, text="选择文件", command=lambda:select_input_file(root,input_file_path_var))
    input_button.grid(column=2, row=0, padx=6, pady=10,sticky="e")
    # 选择密码字典
    dic_frame = tk.Frame(root)
    dic_frame.grid(column=0, row=1, padx=0, pady=10,sticky="w")
    dic_frame_label = tk.Label(dic_frame, text="请选择密码字典:")
    dic_frame_label.grid(column=1, row=1, padx=0, pady=10,sticky="w")
    input_crack_dic_var = tk.Entry(dic_frame, width=50)
    input_crack_dic_var.grid(column=2, row=1, padx=10, pady=10,sticky="e")
    dic_frame_button = tk.Button(dic_frame, text="选择文件", command=lambda:select_cracker_dic(root,input_crack_dic_var))
    dic_frame_button.grid(column=3, row=1, padx=5, pady=10,sticky="e")
    # 创建进度条
    progress_frame= tk.Frame(root)
    progress_frame.grid(column=0, row=4, padx=5, pady=10,sticky="w")
    progress = ttk.Progressbar(progress_frame, style='custom.Horizontal.TProgressbar', orient='horizontal', length=450, mode='determinate')

    # 创建并配置进度条样式
    style = ttk.Style()
    # 定义进度条的样式(这里以设置背景色为例)
    style.configure('custom.Horizontal.TProgressbar', background='blue')
    progress.grid(column=0, row=4, padx=0, pady=10)
    progress_label = tk.Label(progress_frame, text="已完成0%")
    progress_label.grid(column=1, row=4,padx=10, pady=10,sticky="w")
    controller_frame=tk.Frame(root)
    controller_frame.grid(column=0, row=3, padx=0, pady=10)

    # 创建按钮用于执行脚本   
    execute_button = tk.Button(controller_frame, text="执行脚本", command=lambda:start_deception_pdf_thread(root,input_file_path_var.get(),progress,progress_label,input_crack_dic_var.get()))





    execute_button.grid(column=0, row=3, padx=10, pady=10)





    exit_button = tk.Button(controller_frame,text="取消并退出",command=lambda:closeApp(root))





    exit_button.grid(column=1, row=3, padx=10, pady=10)     


    # 运行Tkinter事件循环     
    root.mainloop()     

if __name__ == "__main__":     
    main()

操作方法

  1. 对于能够打开,但是无法编辑的PDF文件,只需要选择文件,不需要选择密码字典,点击“执行脚本”即可完成破解。
  2. 对于有打开密码的文件,需要使用字典爆破,只需要点击选择密码库的“选择文件” 按钮即可,点击执行脚本后,程序会自动遍历字典库中的密码,下方会显示在选中的密码库中,当前执行的进度,中途可以点击“取消并退出”来打断程序的执行。

使用多线程的方式实现,提高效率

针对简单的数字密码或弱口令的破解,速度挺快,但是毕竟只是单线程的破解程序,针对一些较为复杂的密码,需要选择更高级的字典库,下面的版本使用了多线程的方式,通过一个生成器,将文件分成不同的块,通过多线程访问不同的块,来提升遍历的效率。

from shlex import join
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from uu import Error
#pdf的读取方法
from PyPDF2 import PdfReader
#pdf的写入方法
from PyPDF2 import PdfWriter
#高加密的方法
from Crypto.Cipher import AES
from tkinter import ttk
import concurrent.futures
import queue

# 定义一个类在线程中共享信息
class SharedState:
    def __init__(self):
        self.state = True
        self.lock = threading.Lock()

    def set_state(self, new_state):
        with self.lock:
            self.state = new_state

    def get_state(self):
        with self.lock:
            return self.state

# 创建一个线程间信息通信对象
thread_staus = SharedState()

# 切分密码库文件的生成器
# 生成器
def chunks_generator(filename, chunk_size):
    with open(filename, 'r', encoding='utf-8') as file:
        chunk = []
        for line in file:
            line = line.strip()
            chunk.append(line)
            if len(chunk) == chunk_size:  # 修改这里
                yield chunk
                chunk = []  # 清空chunk以便收集下一个数据块
        if chunk:  # 处理文件末尾不完整的块
            yield chunk

# 更新进度条的方法
def check_progress(root,progress, progress_label, progress_queue, total_tasks):
    current_state = thread_staus.get_state()
    if current_state:
        if not progress_queue.empty():
            # 阻塞调用,如果队列为空,则等待
            completed_work = progress_queue.get(block=True)
            current_value = progress['value'] + completed_work
            progress['value'] = current_value
            progress_label.config(text=f"Progress: {current_value / total_tasks * 100:.1f}%")
            print("刷新进度条{}".format(current_value))
            if current_value > total_tasks:
                current_state = False
                thread_staus.set_state(False)
            root.update_idletasks()
    else:
        root.update_idletasks()

# 统计密码库总共有多少行
def count_lines_in_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return sum(1 for _ in file)

# 选择要处理的文件
def select_input_file(input_file_path_var):
    input_file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.PDF;*.pdf")])
    if input_file_path:
        input_file_path_var.delete(0, tk.END)
        input_file_path_var.insert(0, input_file_path)
    return input_file_path

# 选择用于破解的密码字典
def select_cracker_dic(input_crack_dic_var):
    crack_dic = filedialog.askopenfilename(filetypes=[("TXT file","*.txt;*.TXT")])
    if crack_dic:
        input_crack_dic_var.delete(0,tk.END)
        input_crack_dic_var.insert(0,crack_dic)
        return crack_dic

# 定义一个函数来读取PDF文件
def get_reader(filename,password=None):
    try:
        old_file = open(filename, 'rb')
    except Exception as err:
        return print('文件打开失败!' + str(err))
    #如果使用python2需要将PdfReader改为PdfFileReader
    pdf_reader = PdfReader(old_file, strict=False)

    # 如果使用python2需要将is_encrypted改为isEncrypted
    # 执行解密操作
    if pdf_reader.is_encrypted:
        if password is None:
            print('文件被加密,需要密码!--{}'.format(filename))
            return pdf_reader
        else:
            success = pdf_reader.decrypt(password)
            if not success :
                #return print('密码不正确!--{}'.format(filename))
                return
            else:
                #print("解密成功,密码为{}".format(password))
                return pdf_reader
    elif old_file in locals():
        old_file.close()
        # 返回结果
    return pdf_reader

# 真正处理PDF的方法
def deception_pdf(root,filename,progress_queue=None,count_lines=None,chunk=None) :
    current_state = thread_staus.get_state()
    if current_state:
        # 如果用户没有选择破解字典库
        if chunk is None or chunk == '' or count_lines is None or count_lines == '' or progress_queue == None:
            print("==没有选择破解字典库==")
            try:
                pdf_reader = get_reader(filename)
                if pdf_reader is None:
                    return print("无内容读取")
                # 如果使用python2需要将is_encrypted改为isEncrypted
                elif not pdf_reader.is_encrypted:
                    return print('文件没有被加密,无需操作')
                # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                pdf_writer = PdfWriter()
                #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                pdf_writer.append_pages_from_reader(pdf_reader)
                #创建解密后的pdf文件和展示文件的路径
                decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                print("解密文件已生成:{}".format(decrypted_filename))
                # 写入新文件
                pdf_writer.write(open(decrypted_filename, 'wb'))
                current_state = False
                thread_staus.set_state(False)
                root.destroy()
            except Exception as err:
                print("文件设置了打开密码,无法读取PDF,请尝试选择字典库进行爆破")
                current_state = False
                thread_staus.set_state(False)
                #messagebox.showinfo("解密失败", "文件设置了打开密码,无法读取PDF,请尝试选择字典库进行爆破")
                root.destroy()
        else :
            print("选择了破解字典库==>{}".format(count_lines))
            count = 0
            for password in chunk:
                    if current_state:
                        count = count + 1
                        if count % 50 == 0:
                            # 同步信息至队列
                            progress_queue.put(count)
                        pdf_reader = get_reader(filename, password)
                        # 如果破解成功
                        if(pdf_reader):
                            # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                            pdf_writer = PdfWriter()
                            #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                            pdf_writer.append_pages_from_reader(pdf_reader)
                            #创建解密后的pdf文件和展示文件的路径
                            decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                            # 写入新文件
                            pdf_writer.write(open(decrypted_filename, 'wb'))
                            thread_staus.set_state(False)
                            current_state = False
                            #messagebox.showinfo("解密完成", "您的PDF文件已破解完成,密码为{}".format(password))
                            break
                        else:
                            if(count<count_lines):
                                #current_thread = threading.current_thread()  # 获取当前线程实例
                                #thread_name = current_thread.getName()       # 获取当前线程名字
                                #print(("线程{}正在解析密码==>{}".format(thread_name,password)))
                                pass
                            else:
                                current_state = False
                                thread_staus.set_state(False)
                                #messagebox.showinfo("标题", "抱歉,未找到匹配的密码,请换个字典库再试一下吧")
                                print("抱歉,未找到匹配的密码,请换个字典库再试一下吧")
                                break
    else:
        root.destroy()

# 取消破解,退出应用
def closeApp(root):
    thread_staus.set_state(False)
    root.destroy()

# 开启线程,开始破解
def start_deception_pdf_thread(root, filename, progress, progress_label,crack_dic=None):
    # 创建一个线程安全的队列来传递进度信息
    progress_queue = queue.Queue()
    # 重置终止标志
    thread_staus.set_state(True)
    print(crack_dic)

    if crack_dic == None or crack_dic == "":
        deception_pdf(root,filename, progress,progress_label)
    else:
        # 获取字典库总行数,并传到函数里面,用于显示进度条
        count_lines = count_lines_in_file(crack_dic)
        chunks = chunks_generator(crack_dic,1000)
        # 使用 ThreadPoolExecutor 管理线程池
        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
            # 将生成器的每一行作为任务提交到线程池
            futures = [executor.submit(deception_pdf,root,filename, progress_queue,count_lines,chunk)for chunk in chunks]
            # 等待所有任务完成(可选)使用这个会导致在遍历完后,程序卡死
            #concurrent.futures.wait(futures)
            # 确保所有进度更新都已完成
            progress_queue.join()
            root.after(20,lambda:check_progress(root,progress, progress_label, progress_queue,count_lines))

# 创建主函数
def main():
    # 创建主窗口
    root = tk.Tk()
    root.title("PDF解密小工具")
    root.geometry("600x300")
    root.resizable(False, False)
    # 创建一个Tkinter变量来存储文件路径
    #input_file_path_var = tk.StringVar()
    # 输入文件框
    input_frame = tk.Frame(root)
    input_frame.grid(column=0, row=0, padx=0, pady=10,sticky="w")
    input_label = tk.Label(input_frame, text="输入文件:")
    input_label.grid(column=0, row=0, padx=17, pady=10,sticky="w")
    input_file_path_var = tk.Entry(input_frame, width=50)
    input_file_path_var.grid(column=1, row=0, padx=10, pady=10,sticky="e")
    input_button = tk.Button(input_frame, text="选择文件", command=lambda:select_input_file(input_file_path_var))
    input_button.grid(column=2, row=0, padx=6, pady=10,sticky="e")
    # 选择密码字典
    dic_frame = tk.Frame(root)
    dic_frame.grid(column=0, row=1, padx=0, pady=10,sticky="w")
    dic_frame_label = tk.Label(dic_frame, text="请选择密码字典:")
    dic_frame_label.grid(column=1, row=1, padx=0, pady=10,sticky="w")
    input_crack_dic_var = tk.Entry(dic_frame, width=50)
    input_crack_dic_var.grid(column=2, row=1, padx=10, pady=10,sticky="e")
    dic_frame_button = tk.Button(dic_frame, text="选择文件", command=lambda:select_cracker_dic(input_crack_dic_var))
    dic_frame_button.grid(column=3, row=1, padx=5, pady=10,sticky="e")
    # 创建进度条
    progress_frame= tk.Frame(root)
    progress_frame.grid(column=0, row=4, padx=5, pady=10,sticky="w")
    progress = ttk.Progressbar(progress_frame, style='custom.Horizontal.TProgressbar', orient='horizontal', length=450, mode='determinate')
    # 创建并配置进度条样式
    style = ttk.Style()
    # 定义进度条的样式(这里以设置背景色为例)
    style.configure('custom.Horizontal.TProgressbar', background='blue')
    progress.grid(column=0, row=4, padx=0, pady=10)
    progress_label = tk.Label(progress_frame, text="已完成0%")
    progress_label.grid(column=1, row=4,padx=10, pady=10,sticky="w")
    controller_frame=tk.Frame(root) 
    controller_frame.grid(column=0, row=3, padx=0, pady=10)
    # 创建按钮用于执行脚本 
    execute_button = tk.Button(controller_frame, text="执行脚本", command=lambda:start_deception_pdf_thread(root,input_file_path_var.get(),progress,progress_label,input_crack_dic_var.get()))

    execute_button.grid(column=0, row=3, padx=10, pady=10)

    exit_button = tk.Button(controller_frame,text="取消并退出",command=lambda:closeApp(root))

    exit_button.grid(column=1, row=3, padx=10, pady=10) 
    # 运行Tkinter事件循环  
    root.mainloop() 

if __name__ == "__main__": 
    main()

使用多线程,提高密码爆破效率

终于,解决了线程阻塞的问题。

# PDFDecryper-4.5
# Auther:tianwenxiao
# mail:tianwenxiao@wo.cn
# 主要功能:
#        2.0:针对没有设置打开密码的PDF文件,只是设置了打印、编辑等限制的密码,可以使用该脚本进行解密
#        3.5: 新增:针对设置了打开密码的文件,增加了密码字典库爆破功能,只需要选择需要破解的PDF文件,再选择用于破解的字典库,点击执行脚本即可开始破解
#        4.0:新增:针对需要使用密码字典库进行爆破的操作,新增了多线程的方式,通过生成器将字典文件分成块,使用多线程分别遍历,极大的提高了破解效率,但是
#            目前仍然有个问题,那就是目前GUI上的进度条目前无法更新,后续版本即将修复。
#        4.5: 修复:针对4.0版本中遇到的两个问题:①多线程运行时进程阻塞导致无法中途停止的问题②多线程运行时由于进程阻塞导致的进度条不更新的问题,
#                   这两个问题目前通通解决啦,现在可以爽快的进行密码字典爆破,并且可以显示进度,随时打断
#             新增:针对程序运行中出现的异常信息,增加了一个标签显示
from shlex import join
from socket import timeout
import threading
import tkinter as tk
from tkinter import filedialog
#pdf的读取方法
from PyPDF2 import PdfReader
#pdf的写入方法
from PyPDF2 import PdfWriter
#高加密的方法
from Crypto.Cipher import AES
from tkinter import ttk
import concurrent.futures
import queue

from pandas import value_counts


# 定义一个类在线程中共享信息
class SharedState:
    def __init__(self):
        self.state = True
        self.lock = threading.Lock()

    def set_state(self, new_state):
        with self.lock:
            self.state = new_state

    def get_state(self):
        with self.lock:
            return self.state

# 创建一个线程间信息通信对象
thread_staus = SharedState()
# 创建一堆全局变量,用于控制杂七杂八的进程
complete_num = 0
comput_status = True
total_tasks = 0
input_crack_dic_var = None
progress_status = True
# 切分密码库文件的生成器
def chunks_generator(filename, chunk_size):
    with open(filename, 'r', encoding='utf-8') as file:
        chunk = []
        for line in file:
            line = line.strip()
            chunk.append(line)
            if len(chunk) == chunk_size:  # 修改这里
                yield chunk
                chunk = []  # 清空chunk以便收集下一个数据块
        if chunk:  # 处理文件末尾不完整的块
            yield chunk

# 更新进度条的方法
def check_progress(root,progress, progress_label, info_label, progress_queue, info_queue,input_crack_dic_var):
    global complete_num
    global total_tasks
    global comput_status
    global progress_status
    # 实时监控选中的字典路径
    crack_dir = input_crack_dic_var.get()
    try:
        current_status = thread_staus
        if current_status:
            # 如果用户没有选择密码字典,就什么也不做
            if crack_dir == None or crack_dir == "":
                pass
            else:
                # 获取用户选择的字典中总共有多少条密码,这个只会获取一次
                if comput_status:
                    total_tasks = count_lines_in_file(crack_dir)
                    comput_status = False
                progress['max'] = total_tasks
                # print("total_tasks:{}".format(total_tasks))
                # print("共需遍历{}条数据".format(total_tasks))
                # 只有当队列中有数据时,才读取并更新进度条
                if not progress_queue.empty():
                    # print("complete_num==>{}".format(complete_num))
                    if complete_num < total_tasks:
                        completed_work = progress_queue.get(block=True, timeout=5)
                        complete_num = complete_num + completed_work
                        progress['value'] = complete_num
                        progress_label['text'] = "已完成:{:.1f}%".format((progress["value"]/total_tasks)*100)
                    else:
                        info_label['text'] = "遍历了{}条密码,未找到匹配的密码,请换个密码字典试一试吧!".format(total_tasks)
                        progress_status = False
                    root.update_idletasks()
                    progress_queue.task_done()
            if not info_queue.empty():
                info_label['text'] = info_queue.get(block=True, timeout=5)
    except Exception as e:
        info_label['text'] = "出现了错误,请重新运行程序"
        pass
    finally:
        if progress_status:
            root.after(2000,lambda:check_progress(root,progress, progress_label, info_label,progress_queue,info_queue,input_crack_dic_var))

# 统计密码库总共有多少行
def count_lines_in_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return sum(1 for _ in file)

# 选择要处理的文件
def select_input_file(input_file_path_var):
    input_file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.PDF;*.pdf")])
    if input_file_path:
        input_file_path_var.delete(0, tk.END)
        input_file_path_var.insert(0, input_file_path)
    return input_file_path

# 选择用于破解的密码字典
def select_cracker_dic(input_crack_dic_var):
    crack_dic = filedialog.askopenfilename(filetypes=[("TXT file","*.txt;*.TXT")])
    if crack_dic:
        input_crack_dic_var.delete(0,tk.END)
        input_crack_dic_var.insert(0,crack_dic)
        return crack_dic

# 定义一个函数来读取PDF文件
def get_reader(filename,password=None):
    try:
        old_file = open(filename, 'rb')
    except Exception as err:
        return print('文件打开失败!' + str(err))
    #如果使用python2需要将PdfReader改为PdfFileReader
    pdf_reader = PdfReader(old_file, strict=False)
    # 执行解密操作
    if pdf_reader.is_encrypted:
        if password is None:
            return pdf_reader
        else:
            success = pdf_reader.decrypt(password)
            if not success :
                #return print('密码不正确!--{}'.format(filename))
                return
            else:
                #print("解密成功,密码为{}".format(password))
                return pdf_reader
    elif old_file in locals():
        old_file.close()
        # 返回结果
    return pdf_reader

# 真正处理PDF的方法
def deception_pdf(root,filename,progress_queue,info_queue,count_lines=None,chunk=None) :

    # 如果用户没有选择破解字典库
    if chunk is None or chunk == '' or count_lines is None or count_lines == '' or progress_queue == None:
        #print("==没有选择破解字典库==")
        try:
            pdf_reader = get_reader(filename)
            if pdf_reader is None:
                info_queue.put("无法读取PDF内容,请重试!")
            # 如果使用python2需要将is_encrypted改为isEncrypted
            elif not pdf_reader.is_encrypted:
                info_queue.put("文件没有被加密,无需操作!")
            else:
                # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                pdf_writer = PdfWriter()
                #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                pdf_writer.append_pages_from_reader(pdf_reader)
                #创建解密后的pdf文件和展示文件的路径
                decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                info_queue.put("解密文件已生成:{}".format(decrypted_filename))
                # 写入新文件
                pdf_writer.write(open(decrypted_filename, 'wb'))
                current_state = False
                thread_staus.set_state(False)
        except Exception as err:
            info_queue.put("文件设置了打开密码,无法读取PDF,请尝试选择字典库进行爆破")
            current_state = False
            thread_staus.set_state(False)
            root.destroy()
    else :
        current_thread = threading.current_thread()
        thread_name = current_thread.name
        count = 0
        complete_num = 0
        for password in chunk:
                current_state = thread_staus.get_state()
                if current_state:
                    pdf_reader = get_reader(filename, password)
                    count = count + 1
                    complete_num = complete_num + 1
                    if count >= 500:
                        progress_queue.put(count)
                        count = 0
                    # 如果破解成功
                    if(pdf_reader):
                        # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                        pdf_writer = PdfWriter()
                        #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                        pdf_writer.append_pages_from_reader(pdf_reader)
                        #创建解密后的pdf文件和展示文件的路径
                        decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                        # 写入新文件
                        pdf_writer.write(open(decrypted_filename, 'wb'))
                        thread_staus.set_state(False)
                        current_state = False
                        info_queue.put("解密完成", "您的PDF文件已破解完成,密码为{}".format(password))
                        break
                    else:
                        if(complete_num <= count_lines):
                            #print(("线程{}正在解析密码==>{}".format(thread_name,password)))
                            pass
                        else:
                            progress_queue.put(count)
                            info_queue.put("抱歉,未找到匹配的密码,请换个字典库再试一下吧")
                            current_state = False
                            thread_staus.set_state(False)
                            break
        progress_queue.put(count)
        count = 0

# 取消破解,退出应用
def closeApp(root):
    thread_staus.set_state(False)
    root.destroy()

# 开启线程,开始破解
def start_deception_pdf_thread(root, filename,progress_queue,info_queue,count_lines,chunks):
        # 使用 ThreadPoolExecutor 管理线程池
        with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:
            # 将生成器的每一行作为任务提交到线程池
            futures = [executor.submit(deception_pdf,root,filename, progress_queue,info_queue,count_lines,chunk)for chunk in chunks]
            # 等待所有任务完成(可选)使用这个会导致在遍历完后,程序卡死
            #concurrent.futures.wait(futures)
            # 确保所有进度更新都已完成
            progress_queue.join()

def start_back_threads(root, filename,progress_queue,info_queue,crack_dic=None):
    # 重置终止标志
    thread_staus.set_state(True)
    if crack_dic == None or crack_dic == "":
        deception_pdf(root,filename,progress_queue,info_queue)
    else:
        # 获取字典库总行数,并传到函数里面,用于显示进度条
        count_lines = count_lines_in_file(crack_dic)
        chunks = chunks_generator(crack_dic,1000)
        decryp_pdf_thread = threading.Thread(target=start_deception_pdf_thread,args=(root, filename, progress_queue,info_queue,count_lines,chunks))
        decryp_pdf_thread.start()

# 创建主函数
def main():
    global input_crack_dic_var
    # 创建主窗口
    root = tk.Tk()
    root.title("PDF解密小工具")
    root.geometry("600x300")
    root.resizable(False, False)
    # 创建一个线程安全的队列来传递进度信息
    progress_queue = queue.Queue()
    # 创建一个用于传递状态信息的队列
    info_queue = queue.Queue()
    # 输入文件框
    input_frame = tk.Frame(root)
    input_frame.grid(column=0, row=0, padx=0, pady=10,sticky="w")
    input_label = tk.Label(input_frame, text="输入文件:")
    input_label.grid(column=0, row=0, padx=17, pady=10,sticky="w")
    input_file_path_var = tk.Entry(input_frame, width=50)
    input_file_path_var.grid(column=1, row=0, padx=10, pady=10,sticky="e")
    input_button = tk.Button(input_frame, text="选择文件", command=lambda:select_input_file(input_file_path_var))
    input_button.grid(column=2, row=0, padx=6, pady=10,sticky="e")
    # 选择密码字典
    dic_frame = tk.Frame(root)
    dic_frame.grid(column=0, row=1, padx=0, pady=10,sticky="w")
    dic_frame_label = tk.Label(dic_frame, text="请选择密码字典:")
    dic_frame_label.grid(column=1, row=1, padx=0, pady=10,sticky="w")
    input_crack_dic_var = tk.Entry(dic_frame, width=50)
    input_crack_dic_var.grid(column=2, row=1, padx=10, pady=10,sticky="e")
    dic_frame_button = tk.Button(dic_frame, text="选择文件", command=lambda:select_cracker_dic(input_crack_dic_var))
    dic_frame_button.grid(column=3, row=1, padx=5, pady=10,sticky="e")
    # 创建进度条
    progress_frame= tk.Frame(root)
    progress_frame.grid(column=0, row=3, padx=5, pady=10,sticky="w")
    progress = ttk.Progressbar(progress_frame, style='custom.Horizontal.TProgressbar', orient='horizontal', length=460, mode='determinate')

    # 创建并配置进度条样式
    style = ttk.Style()
    # 定义进度条的样式(这里以设置背景色为例)
    style.configure('custom.Horizontal.TProgressbar', background='blue')
    progress.grid(column=0, row=3, padx=0, pady=10)
    progress_label = tk.Label(progress_frame, text="已完成0%")
    progress_label.grid(column=1, row=3,padx=10, pady=10,sticky="w")
    controller_frame=tk.Frame(root)
    controller_frame.grid(column=0, row=5, padx=0, pady=10)
    info_frame = tk.Frame(root)
    info_frame.grid(column=0, row=4, padx=5, pady=5)
    info_label = tk.Label(info_frame, text="欢迎使用PDFDecryper")
    info_label.grid(column=0, row=4,padx=5, pady=5)

    # 创建按钮用于执行脚本
    execute_button = tk.Button(controller_frame, text="执行脚本", command=lambda:start_back_threads(root,input_file_path_var.get(),progress_queue,info_queue,input_crack_dic_var.get()))
    execute_button.grid(column=0, row=5, padx=10, pady=10)


    exit_button = tk.Button(controller_frame,text="取消并退出",command=lambda:closeApp(root))


    exit_button.grid(column=1, row=5, padx=10, pady=10)  
    # 运行Tkinter事件循环  
    root.after(1000,lambda:check_progress(root,progress, progress_label, info_label,progress_queue,info_queue,input_crack_dic_var))


    root.mainloop()  

if __name__ == "__main__":  
    main()

项目地址

https://gitcode.com/weixin_44803446/UsefulPythonUtils/tree/main/PDFDecryper

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凉拌糖醋鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值