几年前接过一个单子,客户发来一份加密的PDF报价单,说密码忘了,里面是给甲方的重要报价,第二天一早就要用。我第一反应是问他:密码是你自己设的吗?得到肯定回答后,才开始动手。这个场景在今天依然频繁发生——电子合同、发票、标书、课程讲义,随手设个密码,三个月后自己也想不起来。
这篇文章就是把这些经验整理出来:PDF的密码保护到底是个什么原理,哪些情况根本不用暴力破解,哪些情况才需要上字典和掩码,以及真正的暴力破解怎么跑能最快出结果。适合看过一点命令行、手里正好有个忘掉密码的PDF、又想自己动手解决问题的人。我会尽量把每一步讲到“拿着命令就能复现”的程度,包括我在实际项目里踩过的坑。
1. 先把“密码”这件事掰开揉碎:PDF加密到底在保护什么
很多人一拿到加密PDF就想上爆破工具,其实先搞清楚加密类型,往往能少走好几个小时的弯路。PDF的密码保护分两类,混淆它们的代价非常大。
1.1 打开密码与权限密码:两把完全不同的锁
第一类叫用户密码(User Password),也就是打开文件时弹出的“请输入密码”。这种加密作用于文件内容本身,没有密码就解不开数据流,属于真正的加密,暴力破解时目标就是它。
第二类叫所有者密码(Owner Password),对应的是“限制打印、限制复制、限制编辑”这类权限控制。关键点来了:PDF的权限控制并不是强加密,它更像一道“君子协定”。PDF阅读器打开文件后,会读取权限标记来禁止相应操作,但这些标记本身并没有被高强度加密保护。也就是说,权限密码根本不需要让暴力破解器去“猜”,用工具直接移除限制就行。
我在实际中遇到至少三分之一的人搞混这两者。客户说“PDF有密码,打不开”,发过来一看,文件能正常打开,只是打印和复制被禁了。这种场景下,你连字典都不用准备,一条命令就结束了。
1.2 加密算法的演变:为什么有的PDF秒破,有的跑几天
另一个影响破解难度的核心因素是PDF的加密算法版本。PDF规范几十年下来,加密方式经历了几个阶段:
| PDF版本 | 算法 | hashcat模式 | 破解速度参考 |
|---|---|---|---|
| PDF 1.1 - 1.3(Acrobat 2-4) | RC4 40bit | 10700 | 极快,GPU下每秒数亿次 |
| PDF 1.4 - 1.6(Acrobat 5-8) | RC4 128bit / AES-128 | 10400 | 每秒几十万到几百万次 |
| PDF 1.7 Level 3(Acrobat 9) | AES-128 | 10500 | 约几十万次 |
| PDF 1.7 Level 8(Acrobat 10-11) | AES-256 | 10600 | 大幅下降,视迭代次数而定 |
为什么同样叫“暴力破解”,速度差距这么大?因为PDF的密钥派生过程会反复做哈希运算,比如AES-256要迭代几十次,这就让每一次密码尝试都变得非常“昂贵”。便宜密码一秒能试一个亿,贵的密码一秒只能试几千次,直接决定了你这单能不能按时交付。
所以拿到PDF后第一件事,不是跑工具,而是用pdfinfo或者qpdf看一眼加密信息,确认版本,再决定策略。
2. 恢复工具怎么选:四类工具的定位与适用范围
PDF口令恢复的生态其实相当成熟,关键是用对工具。下面这几个是我测试下来最顺手的,大致可以分成“秒杀型”“CPU均衡型”“GPU暴力型”和“轻量专属型”四类。
2.1 最快路径:qpdf,30秒干掉权限密码
qpdf是一个处理PDF结构的瑞士军刀,我最早用它是因为它在服务器上处理PDF非常干净,不依赖GUI。它可以解密、拆分、合并、检查PDF,对权限密码的处理尤其干脆。
# 查看加密信息 qpdf --show-encryption encrypted.pdf # 移除权限限制(owner password直接跳过) qpdf --decrypt encrypted.pdf decrypted.pdf如果你用--decrypt时没有被要求输入密码,说明这个PDF只有权限密码或者根本没有打开密码,输出文件就是完整可用的明文件了。这里有个细节:如果PDF同时设了打开密码和权限密码,--decrypt会提示密码错误,因为qpdf必须先用用户密码解开数据流。所以这条命令本身就是个二分判断:能不能解开,一眼知道是哪类锁。
2.2 暴力破解主力:John the Ripper与hashcat的分工
真正需要暴力破解时,我用得最多的是John the Ripper(JtR)和hashcat。两者思路一样:先把PDF转换成一个标准的“密码哈希”文件,再用字典、掩码或纯暴力方式去碰撞这个哈希。
John the Ripper的好处是CPU就能跑,安装简单,自带pdf2john.pl脚本,适合快速试水。hashcat则强在GPU加速,一块像样的N卡能把速度再拉高一两个数量级,适合真正需要大规模跑的场合。
这里必须提一个关键认知:PDF的哈希提取不是解密码,而是计算出一种“校验指纹”。PDF里存的不是密码本身,而是通过密码和文件数据计算出的一个验证值。破解器要做的是对字典里的每个候选密码做同样的计算,看结果是否匹配。这就解释了为什么PDF破解比很多其他格式慢——每次尝试都要重复整套密钥派生流程。
2.3 pdfcrack:轻量场景的另一种选择
pdfcrack算是个小众但实用的工具,它内置了暴力模式和字典模式,命令行非常简洁:
# 字典模式 pdfcrack -f encrypted.pdf -w rockyou.txt # 纯暴力模式(尝试所有 6 位小写字母) pdfcrack -f encrypted.pdf -c abcdefghijklmnopqrstuvwxyz -m 6 -n 6它的优势是安装包小、依赖少,在老旧Linux服务器上也能编译运行。缺点是速度上限不如hashcat,遇到高版本AES-256的大字典会显得力不从心。我的用法是:临时环境、不想装一堆依赖时,pdfcrack顶上来先跑一轮字典,命中就算捡到,不命中再换hashcat。
工具选型给个参考表:
| 工具 | 适合场景 | 攻击模式 | 上手难度 |
|---|---|---|---|
| qpdf | 权限密码移除、加密信息检查 | 无需破解 | 极低 |
| John the Ripper | CPU快速字典测试 | 字典、增量、规则 | 较低 |
| hashcat | GPU大规模碰撞 | 字典、掩码、纯暴力、规则 | 中 |
| pdfcrack | 轻量环境、简单密码 | 字典、纯暴力 | 低 |
3. 完整实操:从加密PDF到明文口令的恢复链路
下面这整套流程是我处理文件时比较固定的一套打法,每一步都有明确目的。你照着走一遍,基本能覆盖九成以上的“忘了密码”场景。
3.1 第一步:确认加密类型并提取哈希
拿到加密PDF后,不要急着跑爆破,先确认文件类型。我一般用两个命令交叉验证:
# 用qpdf看加密参数 qpdf --show-encryption locked.pdf # 用pdfinfo(poppler-utils)看更详细的信息 pdfinfo locked.pdfqpdf --show-encryption会输出类似这样的信息:
R = 4 P = -3904 O = 8c4... U = a6d... Encrypted using AES-128R值代表PDF安全版本,P是权限标志位,O和U分别是对应所有者密码和用户密码的加密校验值。看到R = 4就知道这是PDF 1.5版本左右的AES-128加密,hashcat对应模式是10400或10500,需要进一步用hash提取工具确认。
提取哈希我习惯用John自带的脚本:
# Kali / Parrot 或自己装好John以后 pdf2john.pl locked.pdf > locked.hash生成的locked.hash形如:
locked.pdf:$pdf$2*3*128*-4*1*16*8c4f...*a6d...以$pdf$开头,说明格式对。注意:如果提示无法识别,大概率是PDF版本太新或者用了PDF 2.0加密标准,这时候可以用hashcat的辅助脚本pdf2hashcat.py,或者直接升级工具链。
3.2 第二步:字典跑一遍,多数密码根本不用“暴力”
很多人一提到破解就想从头到尾枚举每一个字符组合,这是误解。真实世界的密码分布极度不均匀,最常见的密码就是那几千个。所以效率最高的永远是先上字典。
我常用的字典是rockyou.txt,Kali里自带,路径在/usr/share/wordlists/rockyou.txt.gz,先解压再用:
gunzip /usr/share/wordlists/rockyou.txt.gz # John跑字典 john --wordlist=/usr/share/wordlists/rockyou.txt locked.hash # 或者hashcat跑字典 hashcat -m 10400 locked.hash /usr/share/wordlists/rockyou.txt有一次帮同事恢复一个老报价单,文件是AES-128加密,我一上来跑了6位纯数字暴力,折腾了快40分钟没结果。后来换了个思路,先用rockyou.txt字典跑,30秒就出来了——密码是abc123。因为设密码的人就是他本人,习惯性用了个“看着像密码的弱密码”。字典攻击命中弱密码概率之高,远超想象。
跑完以后用以下命令看结果:
john --show locked.hash如果字典没命中,别急着换暴力。先想想设密码的人的性格和习惯,这个环节我把它叫“社工字典构造”:公司简称+年份、姓名拼音+生日、手机后六位、项目代号+通讯录里的常用数字,把这些组合成一个自定义字典,往往比通用字典更有效。
# 快速构造一个自定义字典 cat > mydict.txt << EOF abc123 123456 password taobao2024 zhangsan1988 13800138000 qwe123456 EOF hashcat -m 10400 locked.hash mydict.txt3.3 第三步:掩码攻击命中“已知片段密码”
字典跑完没中,但你对密码的pattern有印象,比如“我记得好像是8位,前面两个字母后面六个数字”,那就要用掩码攻击了。掩码(mask)的概念是用占位符描述密码结构,让破解器只枚举符合结构的候选密码,大幅缩小搜索空间。
hashcat的掩码规则很直观:
?d代表数字 0-9?l代表小写字母 a-z?u代表大写字母 A-Z?s代表特殊符号?a代表以上所有可打印字符
假设密码结构是“两个小写字母+六个数字”,命令就是:
hashcat -a 3 -m 10400 locked.hash '?l?l?d?d?d?d?d?d'-a 3表示掩码攻击模式。这种模式下候选空间是26的平方乘上10的6次方,也就是6.76亿种组合,AES-128加密用一块中端GPU大约几分钟就可以跑完,比我之前傻乎乎的纯暴力高效得多。
再举个例子:如果你记得密码是“8位,首字母大写,后面跟生日年月日”,可以写?u?d?d?d?d?d?d?d,或者更精确一点用?u?d?d?d?d?d?d加年份限制。掩码攻击的精髓就是“用你对密码的记忆缩小空间”,不要试图覆盖所有可能,那会指数级增加耗时。
3.4 第四步:纯暴力模式与预估耗时
掩码也覆盖不了,说明密码完全没有规律,才需要上纯暴力。所谓纯暴力就是让破解器枚举指定长度、指定字符集内的所有组合。
# 尝试所有 6 位纯数字 hashcat -a 3 -m 10400 locked.hash '?d?d?d?d?d?d' # 尝试所有 7 位小写字母+数字 hashcat -a 3 -m 10400 locked.hash '?l?l?l?l?l?l?l?d'这里有个残酷的数学规律:字符集和长度每增加一点,候选空间都是指数级膨胀。我根据自己GTX 1660的实测数据给个参考(AES-128,PDF 1.6),让你心里有个底:
| 密码结构 | 候选数量 | 参考耗时 |
|---|---|---|
| 6位纯数字 | 100万 | 秒级 |
| 8位纯数字 | 1亿 | 分钟级 |
| 6位纯小写 | 3亿 | 分钟级 |
| 8位纯小写 | 2080亿 | 数小时 |
| 8位混合大小写+数字 | 2.8万亿 | 数天 |
| 10位混合字符 | 万亿级别 | 不现实 |
所以如果你的目标是纯暴力跑一个10位以上的随机密码,基本可以放弃这条路,去考虑兜底方案。
4. 提速、兜底与合规:真实项目中必须想清楚的三件事
4.1 GPU加速前后耗时对比,以及掩码、字典构造技巧
hashcat只有在GPU上才能发挥实力,CPU跑和GPU跑的差距通常在几十倍。用--opencl-device-types指定GPU设备:
hashcat -I # 列出可用设备 hashcat -m 10400 -d 1 locked.hash wordlist.txt # 指定设备1我自己的经验,一块GTX 1660 Super跑AES-128字典,速度大概是每秒50万次;同样的任务在8核CPU上跑,大概只有每秒5万次。差了十倍。如果你只有CPU,建议直接用John,它针对CPU做了不少优化,比hashcat在CPU上跑更快。
GPU跑的时候注意显存占用和温度,长任务建议用--runtime设个时间上限,跑不完就先存进度:
hashcat --restore # 恢复上次任务字典这块我多说一句:不要迷信大字典,质量比数量重要。rockyou.txt有1400万条,里面大量是废话;我一般会先用--show看字典命中的分布,再自己做一个“高频密码+社工信息”的组合字典,十次里有八次比直接上rockyou更快出结果。
4.2 破解不了时的兜底方案:OCR、留存版本与在线服务
如果密码强度真的很高,暴力破解在可接受时间内无法完成,别死磕。我通常按这个顺序找替代路径:
先看这个PDF是不是“扫描件”。很多所谓的加密PDF内容其实是图片,文件本身被加密了,但如果你能拿到一页预览图,直接OCR往往能提取全部文字。这不是绕过密码,而是利用了“PDF里嵌入的图片预览”在某些渲染器里会先显示再校验的窗口。
再看有没有源文件。PDF常常是由Word、WPS、LaTeX或设计软件导出的。如果源文件还在,或者企业网盘/邮箱里还有没过期的版本,直接重新导出一份比破解快得多。我自己就碰到过客户把加密PDF发过来,结果在他公司网盘的最近编辑记录里找到了未加密的原始文档。
最后才考虑在线服务。现在有些在线PDF解密平台,背后其实就是一批虚拟机在跑hashcat,也不排除它们保存了你上传的文档。如果文件涉及商业机密或隐私,千万别传。本地工具慢归慢,至少可控。
4.3 技术边界与合规意识:什么能跑,什么不能跑
工具本身是中性的,但如果用错场景就会出问题。我接这类恢复需求前,一定会确认文件来源:是你自己生成的?是公司内部授权的旧合同?还是离职同事留下的资料?如果答案含糊,我会拒绝。
这点圈内人应该深有体会。暴力破解本身在安全测试、取证分析和密码找回场景里是常规手段,但未授权情况下对他人PDF进行口令猜测,轻则违反平台规则,重则触犯法律。所以这篇里的命令,只建议用在“自己拥有合法访问权限”的文档上,别拿它去测试同事、客户或陌生人的文件。
4.4 我踩过的几个坑:编码、版本和“假破解成功”
最后分享几个实战中遇到的怪问题,帮你少走弯路。
中文密码的编码坑:有一次PDF密码是中文,我用hashcat跑字典没中,后来才发现是编码问题。PDF创建时用的是UTF-8编码的中文密码,但我的字典文件是GBK编码。两个编码体系下同一个汉字对应的字节完全不同,怎么跑都匹配不上。解决方法是把字典统一转成PDF创建者使用的编码,或者在hashcat里用--encoding=utf-8明确指定。
版本判断错误的坑:pdf2john.pl提取出来的哈希如果格式错位,hashcat会直接报Token length exception。这时候别怀疑hashcat坏了,先看看R值是几,对应好模式。我见过有人拿着AES-256的hash去跑10400模式,白跑了一晚上。
“假破解成功”的坑:John有时候会输出一个结果,但你用这个密码去打开PDF却不对。原因通常是John命中了$pdf$哈希里嵌着的U值校验段,而不是完整的用户密码验证,或者工具提取哈希时不完整。这时候回退到qpdf --decrypt,用密码实测一下,实测能打开才算真成功。
这套流程下来,大部分“忘密码”的场景都可以在两个小时内解决。遇到实在破不了的,也别太执着——我曾经花了一个周末去跑一个6位纯大写密码,跑出来发现是客户生日,自己要是早点想到就不用熬那个夜了。下次再遇到加密PDF,先问问自己:这密码到底是谁设的,他习惯用什么样的组合方式?这个思路比任何工具都重要。