-
Notifications
You must be signed in to change notification settings - Fork 11
/
pdfcracker.py
72 lines (58 loc) · 2 KB
/
pdfcracker.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/python
# Copyright 2012 Alex Reece
from abc import ABCMeta, abstractmethod
import string, subprocess, time
class NoEncryptionError(Exception):
pass
class PDFCracker(object):
__metaclass__ = ABCMeta
def auth_user(self, password):
pass
def auth_owner(self, password):
pass
@classmethod
def parse_pdf_security_data(self, filename):
def parse_int(line):
return int(line[string.find(line, ":")+2:].strip())
def parse_hex_string(line):
return line[string.find(line, ":")+2:].strip().decode("hex")
# TODO(awreece) There is a better way to do this.
p = subprocess.Popen(["pdfcrack", filename, "--minpw=2", "--maxpw=1"],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
warning_line = p.stdout.readline()
try:
version_line = p.stdout.readline()
handler_line = p.stdout.readline()
V = parse_int(p.stdout.readline())
R = parse_int(p.stdout.readline())
P = parse_int(p.stdout.readline())
Length = parse_int(p.stdout.readline())
enc_meta_line = p.stdout.readline()
# TODO(awreece) Actually, I think FileID can be any string. But if that
# were the case, why does converting it to raw bytes work?
FileID = parse_hex_string(p.stdout.readline())
U = parse_hex_string(p.stdout.readline())
O = parse_hex_string(p.stdout.readline())
p.kill()
except:
print warning_line
raise NoEncryptionError
return {"V": V, "R": R, "P": P, "Length": Length,
"FileID": FileID, "U": U, "O": O}
def __init__(self, data=None, filename=None):
if filename is not None:
data = PDFCracker.parse_pdf_security_data(filename)
self.V = data['V']
self.R = data['R']
self.P = data['P']
self.Length = data['Length']
self.FileID = data['FileID']
self.U = data['U']
self.O = data['O']
self.verbose = False
@abstractmethod
def auth_users(self, passwords):
pass
@abstractmethod
def auth_owners(self, passwords, userpass=None):
pass