mirror of
https://github.com/apprenticeharper/DeDRM_tools
synced 2024-12-28 09:58:56 +01:00
ineptpdf 8.2 fileopen
This commit is contained in:
parent
c5ed30d8c8
commit
92dafd94b2
1 changed files with 298 additions and 9 deletions
307
ineptpdf8.pyw
307
ineptpdf8.pyw
|
@ -1,12 +1,17 @@
|
||||||
#! /usr/bin/python
|
#! /usr/bin/python
|
||||||
|
|
||||||
# ineptpdf74.pyw
|
# ineptpdf8.pyw
|
||||||
# ineptpdf, version 7.4
|
# ineptpdf, version 8.2
|
||||||
|
|
||||||
# To run this program install Python 2.6 from http://www.python.org/download/
|
# To run this program install Python 2.6 from http://www.python.org/download/
|
||||||
# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
|
# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
|
||||||
# (make sure to install the version for Python 2.6). Save this script file as
|
# (make sure to install the version for Python 2.6).
|
||||||
# ineptpdf.pyw and double-click on it to run it.
|
#
|
||||||
|
# Always use the 32-Bit Windows Python version - even with 64-bit
|
||||||
|
# windows systems.
|
||||||
|
#
|
||||||
|
# Save this script file as
|
||||||
|
# ineptpdf82.pyw and double-click on it to run it.
|
||||||
|
|
||||||
# Revision history:
|
# Revision history:
|
||||||
# 1 - Initial release
|
# 1 - Initial release
|
||||||
|
@ -27,6 +32,10 @@
|
||||||
# 7.4 - Force all generation numbers in output file to be 0, like in v6.
|
# 7.4 - Force all generation numbers in output file to be 0, like in v6.
|
||||||
# Fallback code for wrong xref improved (search till last trailer
|
# Fallback code for wrong xref improved (search till last trailer
|
||||||
# instead of first)
|
# instead of first)
|
||||||
|
# 8 - fileopen user machine identifier support (Tetrachroma)
|
||||||
|
# 8.1 - fileopen user cookies support (Tetrachroma)
|
||||||
|
# 8.2 - fileopen user name/password support (Tetrachroma)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Decrypt Adobe ADEPT-encrypted PDF files.
|
Decrypt Adobe ADEPT-encrypted PDF files.
|
||||||
"""
|
"""
|
||||||
|
@ -47,6 +56,16 @@ import Tkinter
|
||||||
import Tkconstants
|
import Tkconstants
|
||||||
import tkFileDialog
|
import tkFileDialog
|
||||||
import tkMessageBox
|
import tkMessageBox
|
||||||
|
# added for fileopen support
|
||||||
|
import urllib
|
||||||
|
import urlparse
|
||||||
|
import time
|
||||||
|
import ctypes
|
||||||
|
import socket
|
||||||
|
import string
|
||||||
|
import uuid
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from Crypto.Cipher import ARC4
|
from Crypto.Cipher import ARC4
|
||||||
|
@ -63,6 +82,9 @@ except ImportError:
|
||||||
class ADEPTError(Exception):
|
class ADEPTError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# global variable (needed for fileopen)
|
||||||
|
INPUTFILEPATH = ''
|
||||||
|
|
||||||
# Do we generate cross reference streams on output?
|
# Do we generate cross reference streams on output?
|
||||||
# 0 = never
|
# 0 = never
|
||||||
# 1 = only if present in input
|
# 1 = only if present in input
|
||||||
|
@ -911,6 +933,7 @@ class PDFStream(PDFObject):
|
||||||
if 'Filter' not in self.dic:
|
if 'Filter' not in self.dic:
|
||||||
self.data = data
|
self.data = data
|
||||||
self.rawdata = None
|
self.rawdata = None
|
||||||
|
print self.dict
|
||||||
return
|
return
|
||||||
filters = self.dic['Filter']
|
filters = self.dic['Filter']
|
||||||
if not isinstance(filters, list):
|
if not isinstance(filters, list):
|
||||||
|
@ -1140,6 +1163,9 @@ class PDFDocument(object):
|
||||||
self.parser = None
|
self.parser = None
|
||||||
self.encryption = None
|
self.encryption = None
|
||||||
self.decipher = None
|
self.decipher = None
|
||||||
|
# dictionaries for fileopen
|
||||||
|
self.fileopen = {}
|
||||||
|
self.urlresult = {}
|
||||||
self.ready = False
|
self.ready = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1186,7 +1212,6 @@ class PDFDocument(object):
|
||||||
if STRICT:
|
if STRICT:
|
||||||
raise PDFSyntaxError('Catalog not found!')
|
raise PDFSyntaxError('Catalog not found!')
|
||||||
return
|
return
|
||||||
|
|
||||||
# initialize(password='')
|
# initialize(password='')
|
||||||
# Perform the initialization with a given password.
|
# Perform the initialization with a given password.
|
||||||
# This step is mandatory even if there's no password associated
|
# This step is mandatory even if there's no password associated
|
||||||
|
@ -1202,8 +1227,145 @@ class PDFDocument(object):
|
||||||
return self.initialize_standard(password, docid, param)
|
return self.initialize_standard(password, docid, param)
|
||||||
if type == 'EBX_HANDLER':
|
if type == 'EBX_HANDLER':
|
||||||
return self.initialize_ebx(password, docid, param)
|
return self.initialize_ebx(password, docid, param)
|
||||||
|
if type == 'FOPN_foweb':
|
||||||
|
return self.initialize_fopn(password, docid, param)
|
||||||
raise PDFEncryptionError('Unknown filter: param=%r' % param)
|
raise PDFEncryptionError('Unknown filter: param=%r' % param)
|
||||||
|
|
||||||
|
# experimental fileopen support
|
||||||
|
def initialize_fopn(self, password, docid, param):
|
||||||
|
self.is_printable = self.is_modifiable = self.is_extractable = True
|
||||||
|
# get parameters and add it to the fo dictionary
|
||||||
|
self.fileopen['Length'] = int_value(param.get('Length', 0)) / 8
|
||||||
|
self.fileopen['VEID'] = str_value(param.get('VEID'))
|
||||||
|
self.fileopen['BUILD'] = str_value(param.get('BUILD'))
|
||||||
|
self.fileopen['SVID'] = str_value(param.get('SVID'))
|
||||||
|
self.fileopen['DUID'] = str_value(param.get('DUID'))
|
||||||
|
self.fileopen['V'] = int_value(param.get('V',2))
|
||||||
|
# crypt base
|
||||||
|
rights = str_value(param.get('INFO')).decode('base64')
|
||||||
|
rights = self.genkey_fileopeninfo(rights)
|
||||||
|
# print rights
|
||||||
|
for pair in rights.split(';'):
|
||||||
|
try:
|
||||||
|
key, value = pair.split('=')
|
||||||
|
self.fileopen[key] = value
|
||||||
|
# fix for some misconfigured INFO variables
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
kattr = { 'SVID': 'ServiceID', 'DUID': 'DocumentID', 'I3ID': 'Ident3ID', \
|
||||||
|
'I4ID': 'Ident4ID', 'VERS': 'EncrVer', 'PRID': 'USR'}
|
||||||
|
for keys in kattr:
|
||||||
|
try:
|
||||||
|
self.fileopen[kattr[keys]] = self.fileopen[keys]
|
||||||
|
del self.fileopen[keys]
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
# add static arguments for http/https request
|
||||||
|
self.fo_setattributes()
|
||||||
|
# add hardware specific arguments for http/https request
|
||||||
|
self.fo_sethwids()
|
||||||
|
# print self.fileopen
|
||||||
|
try:
|
||||||
|
buildurl = self.fileopen['UURL']
|
||||||
|
except:
|
||||||
|
buildurl = self.fileopen['PURL']
|
||||||
|
buildurl = buildurl + self.fileopen['DPRM'] + '?'
|
||||||
|
buildurl = buildurl + 'Request=DocPerm'
|
||||||
|
# check for reversed offline handler
|
||||||
|
try:
|
||||||
|
test = self.fileopen['IdentID4']
|
||||||
|
raise ADEPTError('Reversed offline handler not supported yet!')
|
||||||
|
# great IdentID4 not present, let's keep on moving
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# is it a user/pw pdf?
|
||||||
|
try:
|
||||||
|
test = self.fileopen['Ident3ID']
|
||||||
|
except:
|
||||||
|
self.pwtk = Tkinter.Tk()
|
||||||
|
self.pwtk.title('Ineptpdf8')
|
||||||
|
self.pwtk.minsize(150, 0)
|
||||||
|
self.label1 = Tkinter.Label(self.pwtk, text="Username")
|
||||||
|
self.un_entry = Tkinter.Entry(self.pwtk)
|
||||||
|
# cursor here
|
||||||
|
self.un_entry.focus()
|
||||||
|
self.label2 = Tkinter.Label(self.pwtk, text="Password")
|
||||||
|
self.pw_entry = Tkinter.Entry(self.pwtk, show="*")
|
||||||
|
self.button = Tkinter.Button(self.pwtk, text='Go for it!', command=self.fo_save_values)
|
||||||
|
# widget layout, stack vertical
|
||||||
|
self.label1.pack()
|
||||||
|
self.un_entry.pack()
|
||||||
|
self.label2.pack()
|
||||||
|
self.pw_entry.pack()
|
||||||
|
self.button.pack()
|
||||||
|
self.pwtk.update()
|
||||||
|
# start the event loop
|
||||||
|
self.pwtk.mainloop()
|
||||||
|
# drive through tupple for building the url
|
||||||
|
burl = ( 'Stamp', 'Mode', 'USR', 'ServiceID', 'DocumentID',\
|
||||||
|
'Ident3ID', 'Ident4ID','DocStrFmt', 'OSType', 'Language',\
|
||||||
|
'LngLCID', 'LngRFC1766', 'LngISO4Char', 'Build', 'ProdVer', 'EncrVer',\
|
||||||
|
'Machine', 'Disk', 'Uuid', 'User', 'SaUser', 'SaSID',\
|
||||||
|
'FormHFT', 'UserName', 'UserPass',\
|
||||||
|
'SelServer', 'AcroVersion', 'AcroProduct', 'AcroReader',\
|
||||||
|
'AcroCanEdit', 'AcroPrefIDib', 'InBrowser', 'CliAppName',\
|
||||||
|
'DocIsLocal', 'DocPathUrl', 'VolName', 'VolType', 'VolSN',\
|
||||||
|
'FSName', 'FowpKbd', 'OSBuild', 'RequestSchema')
|
||||||
|
for keys in burl:
|
||||||
|
try:
|
||||||
|
buildurl = buildurl + '&' + keys + '=' + self.fileopen[keys]
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
# print 'url:'
|
||||||
|
# print buildurl
|
||||||
|
# custom user agent identification?
|
||||||
|
try:
|
||||||
|
useragent = self.fileopen['AGEN']
|
||||||
|
urllib.URLopener.version = useragent
|
||||||
|
# attribute doesn't exist - take the default user agent
|
||||||
|
except:
|
||||||
|
urllib.URLopener.version = 'Windows NT 6.0'
|
||||||
|
# try to open the url
|
||||||
|
try:
|
||||||
|
u = urllib.urlopen(buildurl)
|
||||||
|
u.geturl()
|
||||||
|
result = u.read()
|
||||||
|
except:
|
||||||
|
raise ADEPTError('No internet connection or a blocking firewall!')
|
||||||
|
# print result
|
||||||
|
if result[0:8] == 'RetVal=1':
|
||||||
|
for pair in result.split('&') :
|
||||||
|
key, value = pair.split('=')
|
||||||
|
self.urlresult[key] = value
|
||||||
|
else:
|
||||||
|
raise ADEPTError(result)
|
||||||
|
# print result
|
||||||
|
try:
|
||||||
|
self.decrypt_key = self.urlresult['Code']
|
||||||
|
except:
|
||||||
|
raise ADEPTError('Cannot find decryption key')
|
||||||
|
self.genkey = self.genkey_fo
|
||||||
|
self.decipher = self.decrypt_rc4
|
||||||
|
self.ready = True
|
||||||
|
return
|
||||||
|
|
||||||
|
# user/password dialog
|
||||||
|
def fo_save_values(self):
|
||||||
|
getout = 0
|
||||||
|
username = self.un_entry.get()
|
||||||
|
password = self.pw_entry.get()
|
||||||
|
un_length = len(username)
|
||||||
|
pw_length = len(password)
|
||||||
|
if (un_length != 0) and (pw_length != 0):
|
||||||
|
getout = 1
|
||||||
|
if getout == 1:
|
||||||
|
self.fileopen['UserName'] = urllib.quote(username)
|
||||||
|
self.fileopen['UserPass'] = urllib.quote(password)
|
||||||
|
# doesn't always close the password window, who
|
||||||
|
# knows why (Tkinter secrets ;=))
|
||||||
|
self.pwtk.quit()
|
||||||
|
|
||||||
|
|
||||||
def initialize_ebx(self, password, docid, param):
|
def initialize_ebx(self, password, docid, param):
|
||||||
self.is_printable = self.is_modifiable = self.is_extractable = True
|
self.is_printable = self.is_modifiable = self.is_extractable = True
|
||||||
with open(password, 'rb') as f:
|
with open(password, 'rb') as f:
|
||||||
|
@ -1313,11 +1475,135 @@ class PDFDocument(object):
|
||||||
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
|
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
def genkey_fo(self, objid, genno):
|
||||||
|
objid = struct.pack('<L', objid)[:3]
|
||||||
|
genno = struct.pack('<L', genno)[:2]
|
||||||
|
key = self.decrypt_key + objid + genno
|
||||||
|
hash = hashlib.md5(key)
|
||||||
|
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
|
||||||
|
return key
|
||||||
|
|
||||||
def decrypt_rc4(self, objid, genno, data):
|
def decrypt_rc4(self, objid, genno, data):
|
||||||
key = self.genkey(objid, genno)
|
key = self.genkey(objid, genno)
|
||||||
return ARC4.new(key).decrypt(data)
|
return ARC4.new(key).decrypt(data)
|
||||||
|
|
||||||
|
def fo_setattributes(self):
|
||||||
|
self.fileopen['Request']='DocPerm'
|
||||||
|
self.fileopen['Mode']='CNR'
|
||||||
|
self.fileopen['DocStrFmt']='ASCII'
|
||||||
|
self.fileopen['OSType']='Windows'
|
||||||
|
self.fileopen['Language']='ENU'
|
||||||
|
self.fileopen['LngLCID']='ENU'
|
||||||
|
self.fileopen['LngRFC1766']='en'
|
||||||
|
self.fileopen['LngISO4Char']='en-us'
|
||||||
|
self.fileopen['Build']='879'
|
||||||
|
self.fileopen['ProdVer']='1.8.7.9'
|
||||||
|
# Machine, Disk, Uuid,
|
||||||
|
self.fileopen['FormHFT']='Yes'
|
||||||
|
self.fileopen['SelServer']='Yes'
|
||||||
|
self.fileopen['AcroVersion']='9.256'
|
||||||
|
self.fileopen['AcroProduct']='Exchange-Pro'
|
||||||
|
self.fileopen['AcroReader']='No'
|
||||||
|
self.fileopen['AcroCanEdit']='Yes'
|
||||||
|
self.fileopen['AcroPrefIDib']='Yes'
|
||||||
|
self.fileopen['InBrowser']='Unk'
|
||||||
|
self.fileopen['CliAppName']=''
|
||||||
|
self.fileopen['DocIsLocal']='Yes'
|
||||||
|
self.fileopen['FSName']='NTFS'
|
||||||
|
self.fileopen['FowpKbd']='No'
|
||||||
|
self.fileopen['OSBuild']='7600'
|
||||||
|
self.fileopen['RequestSchema']='Default'
|
||||||
|
self.fileopen['VolType']='Fixed'
|
||||||
|
|
||||||
|
# get nic mac address
|
||||||
|
def get_win_macaddress(self):
|
||||||
|
p = subprocess.Popen('ipconfig /all', shell = True, stdout=subprocess.PIPE)
|
||||||
|
p.wait()
|
||||||
|
rawtxt = p.stdout.read()
|
||||||
|
return re.findall(r'\s([0-9A-F-]{17})\s',rawtxt)[0].replace('-','')
|
||||||
|
|
||||||
|
# custom conversion 5 bytes to 8 chars method
|
||||||
|
def fo_convert5to8(self, edisk):
|
||||||
|
# byte to number/char mapping table
|
||||||
|
darray=[0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,\
|
||||||
|
0x46,0x47,0x48,0x4A,0x4B,0x4C,0x4D,0x4E,0x50,0x51,0x52,0x53,0x54,\
|
||||||
|
0x55,0x56,0x57,0x58,0x59,0x5A]
|
||||||
|
pdid = struct.pack('<I', int(edisk[0:4].encode("hex"),16))
|
||||||
|
pdid = int(pdid.encode("hex"),16)
|
||||||
|
outputhw = ''
|
||||||
|
# disk id processing
|
||||||
|
for i in range(0,6):
|
||||||
|
index = pdid & 0x1f
|
||||||
|
# shift the disk id 5 bits to the right
|
||||||
|
pdid = pdid >> 5
|
||||||
|
outputhw = outputhw + chr(darray[index])
|
||||||
|
pdid = (ord(edisk[4]) << 2)|pdid
|
||||||
|
# get the last 2 bits from the hwid + low part of the cpuid
|
||||||
|
for i in range(0,2):
|
||||||
|
index = pdid & 0x1f
|
||||||
|
# shift the disk id 5 bits to the right
|
||||||
|
pdid = pdid >> 5
|
||||||
|
outputhw = outputhw + chr(darray[index])
|
||||||
|
return outputhw
|
||||||
|
|
||||||
|
def fo_sethwids(self):
|
||||||
|
# write hardware keys
|
||||||
|
hwkey = 0
|
||||||
|
# get the os type and save it in ostype
|
||||||
|
ostype = os.name
|
||||||
|
# if ostype is Windows
|
||||||
|
if ostype=='nt':
|
||||||
|
import win32api
|
||||||
|
v0 = win32api.GetVolumeInformation('C:\\')
|
||||||
|
v1 = win32api.GetSystemInfo()[6]
|
||||||
|
volserial = v0[1]
|
||||||
|
lowcpu = v1 & 255
|
||||||
|
highcpu = (v1 >> 8) & 255
|
||||||
|
volserial = struct.pack('<L', int(volserial))
|
||||||
|
lowcpu = struct.pack('B', lowcpu)
|
||||||
|
highcpu = struct.pack('B',highcpu)
|
||||||
|
# save it to the fo dictionary
|
||||||
|
encrypteddisk = volserial + lowcpu + highcpu
|
||||||
|
self.fileopen['Disk'] = self.fo_convert5to8(encrypteddisk)
|
||||||
|
# get primary used default mac address
|
||||||
|
pmac = self.get_win_macaddress().decode("hex");
|
||||||
|
self.fileopen['Machine'] = self.fo_convert5to8(pmac[1:])
|
||||||
|
# get uuid
|
||||||
|
self.fileopen['Uuid'] = str(uuid.uuid1())
|
||||||
|
# get time stamp
|
||||||
|
self.fileopen['Stamp'] = str(time.time())[:-3]
|
||||||
|
# get fileopen input pdf name + path
|
||||||
|
self.fileopen['DocPathUrl'] = 'file%3a%2f%2f%2f' + INPUTFILEPATH
|
||||||
|
# get volume name (urllib quote necessairy?)
|
||||||
|
self.fileopen['VolName'] = urllib.quote(win32api.GetVolumeInformation("C:\\")[0])
|
||||||
|
# get volume serial number
|
||||||
|
self.fileopen['VolSN'] = str(win32api.GetVolumeInformation("C:\\")[1])
|
||||||
|
elif ostype=='linux':
|
||||||
|
adeptout = 'Linux is not supported, yet.\n'
|
||||||
|
raise ADEPTError(adeptout)
|
||||||
|
else:
|
||||||
|
adeptout = adeptout + 'Due to various privacy violations from Apple\n'
|
||||||
|
adeptout = adeptout + 'Mac OS X support is disabled by default.'
|
||||||
|
raise ADEPTError(adeptout)
|
||||||
|
return
|
||||||
|
|
||||||
|
# decryption routine for the INFO area
|
||||||
|
def genkey_fileopeninfo(self, data):
|
||||||
|
input1 = struct.pack('L', 0xa4da49de)
|
||||||
|
seed = struct.pack('B', 0x82)
|
||||||
|
key = input1[3] + input1[2] +input1[1] +input1[0] + seed
|
||||||
|
hash = hashlib.md5()
|
||||||
|
key = hash.update(key)
|
||||||
|
spointer4 = struct.pack('<L', 0xec8d6c58)
|
||||||
|
seed = struct.pack('B', 0x07)
|
||||||
|
key = spointer4[3] + spointer4[2] + spointer4[1] + spointer4[0] + seed
|
||||||
|
key = hash.update(key)
|
||||||
|
md5 = hash.digest()
|
||||||
|
key = md5[0:10]
|
||||||
|
return ARC4.new(key).decrypt(data)
|
||||||
|
|
||||||
KEYWORD_OBJ = PSKeywordTable.intern('obj')
|
KEYWORD_OBJ = PSKeywordTable.intern('obj')
|
||||||
|
|
||||||
def getobj(self, objid):
|
def getobj(self, objid):
|
||||||
if not self.ready:
|
if not self.ready:
|
||||||
raise PDFException('PDFDocument not initialized')
|
raise PDFException('PDFDocument not initialized')
|
||||||
|
@ -1803,7 +2089,8 @@ def cli_main(argv=sys.argv):
|
||||||
class DecryptionDialog(Tkinter.Frame):
|
class DecryptionDialog(Tkinter.Frame):
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
Tkinter.Frame.__init__(self, root, border=5)
|
Tkinter.Frame.__init__(self, root, border=5)
|
||||||
self.status = Tkinter.Label(self, text='Select files for decryption')
|
ltext='Select file for decryption\n(Ignore Key file option for Fileopen PDFs)'
|
||||||
|
self.status = Tkinter.Label(self, text=ltext)
|
||||||
self.status.pack(fill=Tkconstants.X, expand=1)
|
self.status.pack(fill=Tkconstants.X, expand=1)
|
||||||
body = Tkinter.Frame(self)
|
body = Tkinter.Frame(self)
|
||||||
body.pack(fill=Tkconstants.X, expand=1)
|
body.pack(fill=Tkconstants.X, expand=1)
|
||||||
|
@ -1870,6 +2157,7 @@ class DecryptionDialog(Tkinter.Frame):
|
||||||
return
|
return
|
||||||
|
|
||||||
def decrypt(self):
|
def decrypt(self):
|
||||||
|
global INPUTFILEPATH
|
||||||
keypath = self.keypath.get()
|
keypath = self.keypath.get()
|
||||||
inpath = self.inpath.get()
|
inpath = self.inpath.get()
|
||||||
outpath = self.outpath.get()
|
outpath = self.outpath.get()
|
||||||
|
@ -1885,8 +2173,9 @@ class DecryptionDialog(Tkinter.Frame):
|
||||||
if inpath == outpath:
|
if inpath == outpath:
|
||||||
self.status['text'] = 'Must have different input and output files'
|
self.status['text'] = 'Must have different input and output files'
|
||||||
return
|
return
|
||||||
|
INPUTFILEPATH = urllib.quote(inpath)
|
||||||
argv = [sys.argv[0], keypath, inpath, outpath]
|
argv = [sys.argv[0], keypath, inpath, outpath]
|
||||||
self.status['text'] = 'Decrypting...'
|
self.status['text'] = 'Processing ...'
|
||||||
try:
|
try:
|
||||||
cli_main(argv)
|
cli_main(argv)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -1903,9 +2192,9 @@ def gui_main():
|
||||||
"This script requires PyCrypto, which must be installed "
|
"This script requires PyCrypto, which must be installed "
|
||||||
"separately. Read the top-of-script comment for details.")
|
"separately. Read the top-of-script comment for details.")
|
||||||
return 1
|
return 1
|
||||||
root.title('INEPT PDF Decrypter')
|
root.title('INEPT PDF Decrypter 8.2 (FileOpen Support)')
|
||||||
root.resizable(True, False)
|
root.resizable(True, False)
|
||||||
root.minsize(300, 0)
|
root.minsize(310, 0)
|
||||||
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
return 0
|
return 0
|
||||||
|
|
Loading…
Reference in a new issue