设为首页收藏本站

 找回密码
 立即注册
搜索
查看: 33|回复: 9

【更新v1.3添加界面】PDF权限密码解密

[复制链接]
累计签到:7 天
连续签到:1 天
灌水成绩
10
7
1127
主题
帖子
积分

等级头衔

ID : 637

测量学徒

积分成就 测量币 : 1127
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-8

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-4 19:19:00 | 显示全部楼层 |阅读模式 IP:香港
PDF权限密码解密

用途

批量或单个对权限加密的PDF举行解密,同时可以去除签名。
权限加密指的是禁止编辑、禁止打印等,无法对打开文件需要密码的情况解密。符合上述情况可无需密码直接解密
下载

v1.3.1
修复了特定PDF书签丢失。部分PDF处置惩罚中控制台会输出大量信息,请忽略(通常表明PDF非标)。
制作了界面,简化利用过程。支持书签。修复了一些bug。
v1.3:
制作了安装包,添加了右键菜单启动
v1.1 https://flt.lanzouo.com/idU6e30aav6
v1.0: https://flt.lanzouo.com/iGZvA309qcyf
用法

如果临时利用,建议v1.3版本的界面版。
报毒说明:经反馈,360报毒,好像是因为nuitka打包导致,具体见查毒报告
您可以review 代码,并利用nuitka/pyinstaller和inno setup自行打包利用,或利用同类软件。
界面版


将文件或文件夹拖入界面即可。输出文件在同目录下文件名_decrypt.pdf处。
可以同时拖入多个文件,也可以点击界面打开恣意文件。
安装版


  • 选中PDF文件后右键点击移除权限

  • 或者在文件夹空白处右键点击移除PDF权限
  • 或者右键文件夹移除PDF权限或者将文件(多个文件也可以)或文件夹拖到v1.0.exe上即可,将在文件同一目录下生成_decrypt.pdf​文件。

效果图



说明

因为代码简略,通过复制文件实现,因此文件较大时速率痴钝。
声明:ui代码由AI生成,颠末审慎的检测和测试,修复了所有发现的bug,并完成了代码的完全review,可以正常运行。
开源地址:https://github.com/flt6/tools/tree/master/pdf_unlock
精密测量技术论坛免责声明
重要声明:以上内容仅代表该作者观点,不代表本站精密测量技术论坛立场。
如有涉及侵权请尽快告知,我们将会在第一时间处理。作者原创内容未经允许不得转载!
站长联系邮箱:1339305021@qq.com
站长联系微信:dddnnbbb
累计签到:4 天
连续签到:1 天
灌水成绩
1
104
359
主题
帖子
积分

等级头衔

ID : 833

测量员

积分成就 测量币 : 359
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-8-2 23:53:00 | 显示全部楼层 IP:香港
烦请所有遇到闪退的坛友,先认真阅读帖子内容,确实使用方法正确后,带版本号回帖。
回复

使用道具 举报

累计签到:6 天
连续签到:1 天
灌水成绩
2
121
510
主题
帖子
积分

等级头衔

ID : 893

测量学徒

积分成就 测量币 : 510
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-10-29 17:47:00 | 显示全部楼层 IP:香港
目前这个版本,在我这里已经是极限了。22.3Mb,用的还是Nuitka打包的
https://wwpw.lanzoub.com/iSIyg39mtiif
回复

使用道具 举报

累计签到:6 天
连续签到:1 天
灌水成绩
2
121
510
主题
帖子
积分

等级头衔

ID : 893

测量学徒

积分成就 测量币 : 510
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-4 22:45:00 | 显示全部楼层 IP:香港
我记得有两款软件分别是PDF Shaper和Advanced PDF Password Recovery可以破解加密且可以正常查看的PDF文件的。
回复

使用道具 举报

累计签到:4 天
连续签到:1 天
灌水成绩
1
104
359
主题
帖子
积分

等级头衔

ID : 833

测量员

积分成就 测量币 : 359
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-5 08:42:00 | 显示全部楼层 IP:香港
PDF中维护了一个表,记录权限、权限密码、所有者密码。如果设置所有者密码,文件内容就被对等加密了,所以不可能解密。而如果只设置权限密码,那直接删除权限密码并将权限设为全部允许就可以了。
这种权限密码是靠软件自觉遵守ISO规范实现的。
回复

使用道具 举报

累计签到:7 天
连续签到:1 天
灌水成绩
4
117
716
主题
帖子
积分

等级头衔

ID : 851

测量学徒

积分成就 测量币 : 716
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-8

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-4 21:06:00 | 显示全部楼层 IP:香港

实测 能够去掉了密码和签名。

增加2个bat文件实现程序右键菜单调用!


①用来添加右键菜单的bat,名称随意,我这里是【00_Add右键.bat
[Asm] 纯文本查看 复制代码@Echo Offrem HJ合集A名称设置,可修改set "HJ=Max小工具"set "np=PDF移除密码签名"set "p=%cd%\%np%.bat"Reg.exe add "HKEY_CLASSES_ROOT\*\shell\%HJ%" /f /v "SubCommands" /t REG_SZ /d ""Reg.exe add "HKEY_CLASSES_ROOT\*\shell\%HJ%" /f /v "Icon" /t REG_SZ /d "imageres.dll,-5310"Reg.exe add "HKEY_CLASSES_ROOT\*\shell\%HJ%" /v "Position" /t REG_SZ /d "Top" /fReg.exe add "HKEY_CLASSES_ROOT\*\shell\%HJ%\shell\%np%\command" /ve /t REG_SZ /d "cmd /c \"\"%p%\" \"%%V\"\"" /fReg.exe add "HKCR\Directory\shell\%HJ%" /f /v "SubCommands" /t REG_SZ /d ""Reg.exe add "HKCR\Directory\shell\%HJ%" /f /v "Icon" /t REG_SZ /d "imageres.dll,-5310"Reg.exe add "HKCR\Directory\shell\%HJ%" /v "Position" /t REG_SZ /d "Top" /fReg.exe add "HKCR\Directory\shell\%HJ%\shell\%np%\command" /ve /t REG_SZ /d "cmd /c \"\"%p%\" \"%%V\"\"" /f



② 【PDF移除密码签名.bat】名称不要改,否则要改上面代码第7行set "np=PDF移除密码签名"
[Asm] 纯文本查看 复制代码@echo off"%~dp0PDF权限密码解密v1.0.exe" "%~nx1"


我这里程序名称由【v1.0.exe】改为【PDF权限密码解密v1.0.exe】


功能:文件夹上右键,可调用;PDF文件右键可调用
弄不明白的,可以下载成品 ↓↓↓

链接:https://pan.quark.cn/s/d5b84a3b3249
回复

使用道具 举报

累计签到:6 天
连续签到:3 天
灌水成绩
3
116
760
主题
帖子
积分

等级头衔

ID : 879

测量学徒

积分成就 测量币 : 760
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-8-11 16:05:00 | 显示全部楼层 IP:香港
增加个图形界面的代码:
[Python] 纯文本查看 复制代码import tkinter as tkfrom tkinter import ttk, filedialog, messageboximport PyPDF2from pathlib import Pathimport threadingclass PDFUnlockerGUI:    def __init__(self, root):        self.root = root        self.root.title("PDF解锁工具")        self.root.geometry("600x400")        self.root.resizable(True, True)                # 创建界面        self.create_widgets()                # 处理状态        self.is_processing = False            def create_widgets(self):        """        创建界面组件        """        # 主框架        main_frame = ttk.Frame(self.root, padding="10")        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))                # 配置网格权重        self.root.columnconfigure(0, weight=1)        self.root.rowconfigure(0, weight=1)        main_frame.columnconfigure(1, weight=1)                # 输入文件/目录选择        ttk.Label(main_frame, text="选择文件或目录:").grid(row=0, column=0, sticky=tk.W, pady=5)                self.path_var = tk.StringVar()        self.path_entry = ttk.Entry(main_frame, textvariable=self.path_var)        self.path_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 5), pady=5)                self.browse_button = ttk.Button(main_frame, text="浏览...", command=self.browse_path)        self.browse_button.grid(row=0, column=2, padx=(5, 0), pady=5)                # 进度条        self.progress_var = tk.DoubleVar()        self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)        self.progress_bar.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)                # 状态标签        self.status_var = tk.StringVar(value="就绪")        self.status_label = ttk.Label(main_frame, textvariable=self.status_var)        self.status_label.grid(row=2, column=0, columnspan=3, sticky=tk.W, pady=5)                # 结果文本框        self.result_text = tk.Text(main_frame, height=15, width=70)        self.result_text.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)                # 滚动条        scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.result_text.yview)        scrollbar.grid(row=3, column=3, sticky=(tk.N, tk.S))        self.result_text.configure(yscrollcommand=scrollbar.set)                # 开始按钮        self.start_button = ttk.Button(main_frame, text="开始处理", command=self.start_processing)        self.start_button.grid(row=4, column=1, pady=10)                # 配置结果文本框的网格权重        main_frame.rowconfigure(3, weight=1)            def browse_path(self):        """        浏览文件或目录        """        # 创建一个选择对话框        file_path = filedialog.askopenfilename(            title="选择PDF文件",            filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")]        )                if not file_path:            # 如果没有选择文件,则尝试选择目录            dir_path = filedialog.askdirectory(title="选择包含PDF文件的目录")            if dir_path:                self.path_var.set(dir_path)        else:            self.path_var.set(file_path)                def copy_pdf_pages(self, input_path: str, output_path: str) -> bool:        """        移除PDF文件的所有限制                Args:            input_path: 输入PDF文件路径            output_path: 输出PDF文件路径                    Returns:            是否成功移除限制        """        try:            with open(input_path, 'rb') as input_file:                reader = PyPDF2.PdfReader(input_file)                                writer = PyPDF2.PdfWriter()                                # 复制所有页面                for page in reader.pages:                    writer.add_page(page)                                # 写入新文件(不设置任何加密或限制)                with open(output_path, 'wb') as output_file:                    writer.write(output_file)                                return True                        except Exception as e:            self.result_text.insert(tk.END, f"处理文件 '{input_path}' 时发生错误: {e}\n")            return False        def process_files(self):        """        在后台线程中处理文件        """        input_path = self.path_var.get().strip()                if not input_path:            messagebox.showerror("错误", "请选择要处理的文件或目录")            self.is_processing = False            return                    path_obj = Path(input_path)                if not path_obj.exists():            messagebox.showerror("错误", "指定的文件或目录不存在")            self.is_processing = False            return                    try:            if path_obj.is_dir():                files = list(path_obj.glob("**/*.pdf"))            else:                files = [path_obj]                            total = len(files)            if total == 0:                self.result_text.insert(tk.END, "未找到PDF文件\n")                self.status_var.set("未找到PDF文件")                self.is_processing = False                return                            self.result_text.insert(tk.END, f"找到 {total} 个PDF文件\n")            self.result_text.insert(tk.END, "=" * 50 + "\n")                        success_count = 0            for i, pdf_file in enumerate(files, start=1):                if not self.is_processing:  # 用户取消操作                    break                                    # 更新进度                progress = (i / total) * 100                self.progress_var.set(progress)                self.status_var.set(f"正在处理: {pdf_file.name} ({i}/{total})")                self.root.update_idletasks()                                if not pdf_file.is_file():                    self.result_text.insert(tk.END, f"跳过非PDF文件: {pdf_file}\n")                    continue                                    output_file = pdf_file.with_name(f"{pdf_file.stem}_decrypt.pdf")                success = self.copy_pdf_pages(pdf_file, output_file)                                if success:                    success_count += 1                    self.result_text.insert(tk.END, f"✓ {pdf_file.name} 处理成功\n")                else:                    self.result_text.insert(tk.END, f"✗ {pdf_file.name} 处理失败\n")                                    self.result_text.see(tk.END)                self.root.update_idletasks()                            # 处理完成            self.progress_var.set(100)            self.status_var.set(f"处理完成: {success_count}/{total} 个文件成功处理")            self.result_text.insert(tk.END, "=" * 50 + "\n")            self.result_text.insert(tk.END, f"处理完成: {success_count}/{total} 个文件成功处理\n")                    except Exception as e:            self.result_text.insert(tk.END, f"处理过程中发生错误: {e}\n")            self.status_var.set("处理出错")                    finally:            self.is_processing = False            self.start_button.config(text="开始处理", state=tk.NORMAL)                def start_processing(self):        """        开始或停止处理        """        if self.is_processing:            # 停止处理            self.is_processing = False            self.start_button.config(text="开始处理", state=tk.NORMAL)            self.status_var.set("处理已停止")        else:            # 开始处理            self.is_processing = True            self.start_button.config(text="停止处理", state=tk.NORMAL)            self.result_text.delete(1.0, tk.END)            self.progress_var.set(0)                        # 在新线程中处理文件,避免阻塞GUI            thread = threading.Thread(target=self.process_files)            thread.daemon = True            thread.start()def main():    root = tk.Tk()    app = PDFUnlockerGUI(root)    root.mainloop()if __name__ == "__main__":    main()
回复

使用道具 举报

累计签到:3 天
连续签到:1 天
灌水成绩
2
94
376
主题
帖子
积分

等级头衔

ID : 888

测量员

积分成就 测量币 : 376
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-9 10:42:00 | 显示全部楼层 IP:香港
你这里逻辑不对
```
HKEY_CLASSES_ROOT\*\shell\
```
是向所有文件右键菜单加选项,应该只向pdf后缀加。

```
HKEY_CLASSES_ROOT\SystemFileAssociations\.pdf\shell
```
或者
```
HKEY_CLASSES_ROOT\.pdf\shell
```

还是推荐用我发的1.1版本安装包,避免卸载残留:lol
回复

使用道具 举报

累计签到:6 天
连续签到:1 天
灌水成绩
1
119
381
主题
帖子
积分

等级头衔

ID : 831

测量员

积分成就 测量币 : 381
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-4 19:35:00 | 显示全部楼层 IP:香港
非常感谢
回复

使用道具 举报

累计签到:4 天
连续签到:1 天
灌水成绩
2
118
266
主题
帖子
积分

等级头衔

ID : 853

测量员

积分成就 测量币 : 266
在线时间 : 0 小时
注册时间 : 2026-4-6
最后登录 : 2026-5-9

勋章
UID勋章测量学徒测量员
联系方式
发表于 2025-7-4 20:21:00 | 显示全部楼层 IP:香港
还是挺需要这种小工具的。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|精密测量技术论坛 ( 桂ICP备2026007449号-1 )|网站地图

GMT+8, 2026-5-9 11:42 , Processed in 0.106664 second(s), 31 queries .

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