summaryrefslogtreecommitdiffstats
path: root/Other_Tools
diff options
context:
space:
mode:
authorApprentice Harper <[email protected]>2018-12-02 11:37:07 +0000
committerApprentice Harper <[email protected]>2018-12-02 11:37:07 +0000
commit3f21bd9f5afaf93d67917a3c829bc0389877bf24 (patch)
tree40429f29422ce9121f36ab4ee0d117eb2b3172ab /Other_Tools
parent9b001bfaf35949b47055708c927023cbc5dc7aef (diff)
Move to new positions
Diffstat (limited to 'Other_Tools')
-rw-r--r--Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js26
-rw-r--r--Other_Tools/B_and_N_Download_Helper/BN-Dload.user_ReadMe.txt32
-rw-r--r--Other_Tools/DRM_Key_Scripts/Adobe_Digital_Editions/adobekey.pyw603
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw336
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw258
-rw-r--r--Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw332
-rw-r--r--Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw468
-rw-r--r--Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw1727
-rw-r--r--Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw275
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/A_Patching_Experience.txt50
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/ReadMe_K4Android.txt50
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/kindle3.0.1.70.patch100
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/ReadMe_K4Android.txt74
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/kindle3.7.0.108.patch155
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_4.0.2.1/kindle4.0.2.1.patch238
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/Notes on the Patch.txt11
-rw-r--r--Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/kindle4.8.1.10.patch157
-rwxr-xr-xOther_Tools/Kobo/obok.py751
-rw-r--r--Other_Tools/Rocket_ebooks/rebhack.zipbin206771 -> 0 bytes
-rw-r--r--Other_Tools/Rocket_ebooks/rebhack_ReadMe.txt8
-rw-r--r--Other_Tools/Scuolabook_DRM/Scuolabook_ReadMe.txt4
-rw-r--r--Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51.pyw3160
-rw-r--r--Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51_ReadMe.txt8
23 files changed, 0 insertions, 8823 deletions
diff --git a/Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js b/Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js
deleted file mode 100644
index 6bb028f..0000000
--- a/Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// ==UserScript==
-// @name BN-Dload
-// @namespace http://www.mailinator.com/J-man
-// @include https://mynook.barnesandnoble.com/library.html*
-// @grant none
-// @version 20121119
-// ==/UserScript==
-
-function doIt() {
- if ($('#adl1').length == 0) {
- $('[action$="deleteItem"]').each(function(index) {
- if ($(this).parent().find('[action$="EDSDeliverItem.aspx"]').length == 0) {
- var delid = $(this).find('input').attr('value');
- $(this).after('<span class="vb2"></span><form id="adl' + index + '" action="https://edelivery.barnesandnoble.com/EDS/EDSDeliverItem.aspx" class="download"><input value="' + delid + '" type="hidden" name="delid"><input type="hidden" value="Browser" name="clienttype"><input type="hidden" value="browser" name="deviceinfo"><button class="download "name="download">Alternative Download</button></form>');
- }
- });
-
- }
-
- setTimeout (function() {
- doIt();
- }, 3000 );
-}
-
-doIt();
-
diff --git a/Other_Tools/B_and_N_Download_Helper/BN-Dload.user_ReadMe.txt b/Other_Tools/B_and_N_Download_Helper/BN-Dload.user_ReadMe.txt
deleted file mode 100644
index ab66f1c..0000000
--- a/Other_Tools/B_and_N_Download_Helper/BN-Dload.user_ReadMe.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-INTRODUCTION
-============
-
-To obtain unencrypted content from the B&N, you have to download it directly from the website. Unrooted Nook devices will not let you save your content to your PC.
-
-If the downloaded file is encrypted, install and configure the ignoble plugin in Calibre to decrypt that.
-
-
-DOWNLOAD HIDDEN FILES FROM B&N
-------------------------------
-
-Some content is not downloadable from the B&N website, notably magazines. A Greasemonkey script (details below) modifies the myNook page of the Barnes and Noble website to show a download button for normally non-downloadable content. This will work until Barnes & Noble changes their website.
-
-Prerequisites
--------------
-1) Firefox: http://www.getfirefox.com
-2) Greasemokey extension: https://addons.mozilla.org/nl/firefox/addon/greasemonkey/
-
-One time installation
----------------------
-1) Install Firefox if not already done so
-2) Follow the above link to GreaseMonkey and click Add to Firefox
-3) Restart Firefox
-4) Go to http://userscripts.org/scripts/source/152985.user.js
-5) A popup should appear, stating you are about to install a GreaseMonkey user script.
-6) Click on install
-
-Use
----
-1) Log in into your B&N account
-2) Go to MyNook
-3) An “Alternative download” should appear next to normally non-downloadable content. Note that this will not work for content such as Nook applications, and some children books.
diff --git a/Other_Tools/DRM_Key_Scripts/Adobe_Digital_Editions/adobekey.pyw b/Other_Tools/DRM_Key_Scripts/Adobe_Digital_Editions/adobekey.pyw
deleted file mode 100644
index 7fbd516..0000000
--- a/Other_Tools/DRM_Key_Scripts/Adobe_Digital_Editions/adobekey.pyw
+++ /dev/null
@@ -1,603 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# adobekey.pyw, version 6.0
-# Copyright © 2009-2010 i♥cabbages
-
-# Released under the terms of the GNU General Public Licence, version 3
-# <http://www.gnu.org/licenses/>
-
-# Modified 2010–2016 by several people
-
-# Windows users: Before running this program, you must first install Python.
-# We recommend ActiveState Python 2.7.X for Windows (x86) from
-# http://www.activestate.com/activepython/downloads.
-# You must also install PyCrypto from
-# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-# (make certain to install the version for Python 2.7).
-# Then save this script file as adobekey.pyw and double-click on it to run it.
-# It will create a file named adobekey_1.der in in the same directory as the script.
-# This is your Adobe Digital Editions user key.
-#
-# Mac OS X users: Save this script file as adobekey.pyw. You can run this
-# program from the command line (python adobekey.pyw) or by double-clicking
-# it when it has been associated with PythonLauncher. It will create a file
-# named adobekey_1.der in the same directory as the script.
-# This is your Adobe Digital Editions user key.
-
-# Revision history:
-# 1 - Initial release, for Adobe Digital Editions 1.7
-# 2 - Better algorithm for finding pLK; improved error handling
-# 3 - Rename to INEPT
-# 4 - Series of changes by joblack (and others?) --
-# 4.1 - quick beta fix for ADE 1.7.2 (anon)
-# 4.2 - added old 1.7.1 processing
-# 4.3 - better key search
-# 4.4 - Make it working on 64-bit Python
-# 5 - Clean up and improve 4.x changes;
-# Clean up and merge OS X support by unknown
-# 5.1 - add support for using OpenSSL on Windows in place of PyCrypto
-# 5.2 - added support for output of key to a particular file
-# 5.3 - On Windows try PyCrypto first, OpenSSL next
-# 5.4 - Modify interface to allow use of import
-# 5.5 - Fix for potential problem with PyCrypto
-# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
-# 5.7 - Unicode support added, renamed adobekey from ineptkey
-# 5.8 - Added getkey interface for Windows DeDRM application
-# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
-# 6.0 - Work if TkInter is missing
-
-"""
-Retrieve Adobe ADEPT user key.
-"""
-
-__license__ = 'GPL v3'
-__version__ = '6.0'
-
-import sys, os, struct, getopt
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"adobekey.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-class ADEPTError(Exception):
- pass
-
-if iswindows:
- from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
- create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
- string_at, Structure, c_void_p, cast, c_size_t, memmove, CDLL, c_int, \
- c_long, c_ulong
-
- from ctypes.wintypes import LPVOID, DWORD, BOOL
- import _winreg as winreg
-
- def _load_crypto_libcrypto():
- from ctypes.util import find_library
- libcrypto = find_library('libeay32')
- if libcrypto is None:
- raise ADEPTError('libcrypto not found')
- libcrypto = CDLL(libcrypto)
- AES_MAXNR = 14
- c_char_pp = POINTER(c_char_p)
- c_int_p = POINTER(c_int)
- class AES_KEY(Structure):
- _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
- ('rounds', c_int)]
- AES_KEY_p = POINTER(AES_KEY)
-
- def F(restype, name, argtypes):
- func = getattr(libcrypto, name)
- func.restype = restype
- func.argtypes = argtypes
- return func
-
- AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
- [c_char_p, c_int, AES_KEY_p])
- AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
- [c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
- c_int])
- class AES(object):
- def __init__(self, userkey):
- self._blocksize = len(userkey)
- if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
- raise ADEPTError('AES improper key used')
- key = self._key = AES_KEY()
- rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
- if rv < 0:
- raise ADEPTError('Failed to initialize AES key')
- def decrypt(self, data):
- out = create_string_buffer(len(data))
- iv = ("\x00" * self._blocksize)
- rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
- if rv == 0:
- raise ADEPTError('AES decryption failed')
- return out.raw
- return AES
-
- def _load_crypto_pycrypto():
- from Crypto.Cipher import AES as _AES
- class AES(object):
- def __init__(self, key):
- self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
- def decrypt(self, data):
- return self._aes.decrypt(data)
- return AES
-
- def _load_crypto():
- AES = None
- for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto):
- try:
- AES = loader()
- break
- except (ImportError, ADEPTError):
- pass
- return AES
-
- AES = _load_crypto()
-
-
- DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
- PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
-
- MAX_PATH = 255
-
- kernel32 = windll.kernel32
- advapi32 = windll.advapi32
- crypt32 = windll.crypt32
-
- def GetSystemDirectory():
- GetSystemDirectoryW = kernel32.GetSystemDirectoryW
- GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
- GetSystemDirectoryW.restype = c_uint
- def GetSystemDirectory():
- buffer = create_unicode_buffer(MAX_PATH + 1)
- GetSystemDirectoryW(buffer, len(buffer))
- return buffer.value
- return GetSystemDirectory
- GetSystemDirectory = GetSystemDirectory()
-
- def GetVolumeSerialNumber():
- GetVolumeInformationW = kernel32.GetVolumeInformationW
- GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
- POINTER(c_uint), POINTER(c_uint),
- POINTER(c_uint), c_wchar_p, c_uint]
- GetVolumeInformationW.restype = c_uint
- def GetVolumeSerialNumber(path):
- vsn = c_uint(0)
- GetVolumeInformationW(
- path, None, 0, byref(vsn), None, None, None, 0)
- return vsn.value
- return GetVolumeSerialNumber
- GetVolumeSerialNumber = GetVolumeSerialNumber()
-
- def GetUserName():
- GetUserNameW = advapi32.GetUserNameW
- GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
- GetUserNameW.restype = c_uint
- def GetUserName():
- buffer = create_unicode_buffer(32)
- size = c_uint(len(buffer))
- while not GetUserNameW(buffer, byref(size)):
- buffer = create_unicode_buffer(len(buffer) * 2)
- size.value = len(buffer)
- return buffer.value.encode('utf-16-le')[::2]
- return GetUserName
- GetUserName = GetUserName()
-
- PAGE_EXECUTE_READWRITE = 0x40
- MEM_COMMIT = 0x1000
- MEM_RESERVE = 0x2000
-
- def VirtualAlloc():
- _VirtualAlloc = kernel32.VirtualAlloc
- _VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
- _VirtualAlloc.restype = LPVOID
- def VirtualAlloc(addr, size, alloctype=(MEM_COMMIT | MEM_RESERVE),
- protect=PAGE_EXECUTE_READWRITE):
- return _VirtualAlloc(addr, size, alloctype, protect)
- return VirtualAlloc
- VirtualAlloc = VirtualAlloc()
-
- MEM_RELEASE = 0x8000
-
- def VirtualFree():
- _VirtualFree = kernel32.VirtualFree
- _VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
- _VirtualFree.restype = BOOL
- def VirtualFree(addr, size=0, freetype=MEM_RELEASE):
- return _VirtualFree(addr, size, freetype)
- return VirtualFree
- VirtualFree = VirtualFree()
-
- class NativeFunction(object):
- def __init__(self, restype, argtypes, insns):
- self._buf = buf = VirtualAlloc(None, len(insns))
- memmove(buf, insns, len(insns))
- ftype = CFUNCTYPE(restype, *argtypes)
- self._native = ftype(buf)
-
- def __call__(self, *args):
- return self._native(*args)
-
- def __del__(self):
- if self._buf is not None:
- VirtualFree(self._buf)
- self._buf = None
-
- if struct.calcsize("P") == 4:
- CPUID0_INSNS = (
- "\x53" # push %ebx
- "\x31\xc0" # xor %eax,%eax
- "\x0f\xa2" # cpuid
- "\x8b\x44\x24\x08" # mov 0x8(%esp),%eax
- "\x89\x18" # mov %ebx,0x0(%eax)
- "\x89\x50\x04" # mov %edx,0x4(%eax)
- "\x89\x48\x08" # mov %ecx,0x8(%eax)
- "\x5b" # pop %ebx
- "\xc3" # ret
- )
- CPUID1_INSNS = (
- "\x53" # push %ebx
- "\x31\xc0" # xor %eax,%eax
- "\x40" # inc %eax
- "\x0f\xa2" # cpuid
- "\x5b" # pop %ebx
- "\xc3" # ret
- )
- else:
- CPUID0_INSNS = (
- "\x49\x89\xd8" # mov %rbx,%r8
- "\x49\x89\xc9" # mov %rcx,%r9
- "\x48\x31\xc0" # xor %rax,%rax
- "\x0f\xa2" # cpuid
- "\x4c\x89\xc8" # mov %r9,%rax
- "\x89\x18" # mov %ebx,0x0(%rax)
- "\x89\x50\x04" # mov %edx,0x4(%rax)
- "\x89\x48\x08" # mov %ecx,0x8(%rax)
- "\x4c\x89\xc3" # mov %r8,%rbx
- "\xc3" # retq
- )
- CPUID1_INSNS = (
- "\x53" # push %rbx
- "\x48\x31\xc0" # xor %rax,%rax
- "\x48\xff\xc0" # inc %rax
- "\x0f\xa2" # cpuid
- "\x5b" # pop %rbx
- "\xc3" # retq
- )
-
- def cpuid0():
- _cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
- buf = create_string_buffer(12)
- def cpuid0():
- _cpuid0(buf)
- return buf.raw
- return cpuid0
- cpuid0 = cpuid0()
-
- cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
-
- class DataBlob(Structure):
- _fields_ = [('cbData', c_uint),
- ('pbData', c_void_p)]
- DataBlob_p = POINTER(DataBlob)
-
- def CryptUnprotectData():
- _CryptUnprotectData = crypt32.CryptUnprotectData
- _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
- c_void_p, c_void_p, c_uint, DataBlob_p]
- _CryptUnprotectData.restype = c_uint
- def CryptUnprotectData(indata, entropy):
- indatab = create_string_buffer(indata)
- indata = DataBlob(len(indata), cast(indatab, c_void_p))
- entropyb = create_string_buffer(entropy)
- entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
- outdata = DataBlob()
- if not _CryptUnprotectData(byref(indata), None, byref(entropy),
- None, None, 0, byref(outdata)):
- raise ADEPTError("Failed to decrypt user key key (sic)")
- return string_at(outdata.pbData, outdata.cbData)
- return CryptUnprotectData
- CryptUnprotectData = CryptUnprotectData()
-
- def adeptkeys():
- if AES is None:
- raise ADEPTError("PyCrypto or OpenSSL must be installed")
- root = GetSystemDirectory().split('\\')[0] + '\\'
- serial = GetVolumeSerialNumber(root)
- vendor = cpuid0()
- signature = struct.pack('>I', cpuid1())[1:]
- user = GetUserName()
- entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
- cuser = winreg.HKEY_CURRENT_USER
- try:
- regkey = winreg.OpenKey(cuser, DEVICE_KEY_PATH)
- device = winreg.QueryValueEx(regkey, 'key')[0]
- except WindowsError:
- raise ADEPTError("Adobe Digital Editions not activated")
- keykey = CryptUnprotectData(device, entropy)
- userkey = None
- keys = []
- try:
- plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
- except WindowsError:
- raise ADEPTError("Could not locate ADE activation")
- for i in xrange(0, 16):
- try:
- plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
- except WindowsError:
- break
- ktype = winreg.QueryValueEx(plkparent, None)[0]
- if ktype != 'credentials':
- continue
- for j in xrange(0, 16):
- try:
- plkkey = winreg.OpenKey(plkparent, "%04d" % (j,))
- except WindowsError:
- break
- ktype = winreg.QueryValueEx(plkkey, None)[0]
- if ktype != 'privateLicenseKey':
- continue
- userkey = winreg.QueryValueEx(plkkey, 'value')[0]
- userkey = userkey.decode('base64')
- aes = AES(keykey)
- userkey = aes.decrypt(userkey)
- userkey = userkey[26:-ord(userkey[-1])]
- #print "found key:",userkey.encode('hex')
- keys.append(userkey)
- if len(keys) == 0:
- raise ADEPTError('Could not locate privateLicenseKey')
- print u"Found {0:d} keys".format(len(keys))
- return keys
-
-
-elif isosx:
- import xml.etree.ElementTree as etree
- import subprocess
-
- NSMAP = {'adept': 'http://ns.adobe.com/adept',
- 'enc': 'http://www.w3.org/2001/04/xmlenc#'}
-
- def findActivationDat():
- import warnings
- warnings.filterwarnings('ignore', category=FutureWarning)
-
- home = os.getenv('HOME')
- cmdline = 'find "' + home + '/Library/Application Support/Adobe/Digital Editions" -name "activation.dat"'
- cmdline = cmdline.encode(sys.getfilesystemencoding())
- p2 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
- out1, out2 = p2.communicate()
- reslst = out1.split('\n')
- cnt = len(reslst)
- ActDatPath = "activation.dat"
- for j in xrange(cnt):
- resline = reslst[j]
- pp = resline.find('activation.dat')
- if pp >= 0:
- ActDatPath = resline
- break
- if os.path.exists(ActDatPath):
- return ActDatPath
- return None
-
- def adeptkeys():
- actpath = findActivationDat()
- if actpath is None:
- raise ADEPTError("Could not find ADE activation.dat file.")
- tree = etree.parse(actpath)
- adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
- expr = '//%s/%s' % (adept('credentials'), adept('privateLicenseKey'))
- userkey = tree.findtext(expr)
- userkey = userkey.decode('base64')
- userkey = userkey[26:]
- return [userkey]
-
-else:
- def adeptkeys():
- raise ADEPTError("This script only supports Windows and Mac OS X.")
- return []
-
-# interface for Python DeDRM
-def getkey(outpath):
- keys = adeptkeys()
- if len(keys) > 0:
- if not os.path.isdir(outpath):
- outfile = outpath
- with file(outfile, 'wb') as keyfileout:
- keyfileout.write(keys[0])
- print u"Saved a key to {0}".format(outfile)
- else:
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(outpath,u"adobekey_{0:d}.der".format(keycount))
- if not os.path.exists(outfile):
- break
- with file(outfile, 'wb') as keyfileout:
- keyfileout.write(key)
- print u"Saved a key to {0}".format(outfile)
- return True
- return False
-
-def usage(progname):
- print u"Finds, decrypts and saves the default Adobe Adept encryption key(s)."
- print u"Keys are saved to the current directory, or a specified output directory."
- print u"If a file name is passed instead of a directory, only the first key is saved, in that file."
- print u"Usage:"
- print u" {0:s} [-h] [<outpath>]".format(progname)
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2009-2013 i♥cabbages and Apprentice Alf".format(progname,__version__)
-
- try:
- opts, args = getopt.getopt(argv[1:], "h")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
- usage(progname)
- sys.exit(2)
-
- for o, a in opts:
- if o == "-h":
- usage(progname)
- sys.exit(0)
-
- if len(args) > 1:
- usage(progname)
- sys.exit(2)
-
- if len(args) == 1:
- # save to the specified file or directory
- outpath = args[0]
- if not os.path.isabs(outpath):
- outpath = os.path.abspath(outpath)
- else:
- # save to the same directory as the script
- outpath = os.path.dirname(argv[0])
-
- # make sure the outpath is the
- outpath = os.path.realpath(os.path.normpath(outpath))
-
- keys = adeptkeys()
- if len(keys) > 0:
- if not os.path.isdir(outpath):
- outfile = outpath
- with file(outfile, 'wb') as keyfileout:
- keyfileout.write(keys[0])
- print u"Saved a key to {0}".format(outfile)
- else:
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(outpath,u"adobekey_{0:d}.der".format(keycount))
- if not os.path.exists(outfile):
- break
- with file(outfile, 'wb') as keyfileout:
- keyfileout.write(key)
- print u"Saved a key to {0}".format(outfile)
- else:
- print u"Could not retrieve Adobe Adept key."
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- import traceback
- except:
- return cli_main()
-
- class ExceptionDialog(Tkinter.Frame):
- def __init__(self, root, text):
- Tkinter.Frame.__init__(self, root, border=5)
- label = Tkinter.Label(self, text=u"Unexpected error:",
- anchor=Tkconstants.W, justify=Tkconstants.LEFT)
- label.pack(fill=Tkconstants.X, expand=0)
- self.text = Tkinter.Text(self)
- self.text.pack(fill=Tkconstants.BOTH, expand=1)
-
- self.text.insert(Tkconstants.END, text)
-
-
- argv=unicode_argv()
- root = Tkinter.Tk()
- root.withdraw()
- progpath, progname = os.path.split(argv[0])
- success = False
- try:
- keys = adeptkeys()
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(progpath,u"adobekey_{0:d}.der".format(keycount))
- if not os.path.exists(outfile):
- break
-
- with file(outfile, 'wb') as keyfileout:
- keyfileout.write(key)
- success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except ADEPTError, e:
- tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
- except Exception:
- root.wm_state('normal')
- root.title(progname)
- text = traceback.format_exc()
- ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
- root.mainloop()
- if not success:
- return 1
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
deleted file mode 100644
index dbadc5d..0000000
--- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw
+++ /dev/null
@@ -1,336 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# ignoblekey.py
-# Copyright © 2015 Apprentice Alf and Apprentice Harper
-
-# Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf
-
-# Released under the terms of the GNU General Public Licence, version 3
-# <http://www.gnu.org/licenses/>
-
-# Revision history:
-# 1.0 - Initial release
-# 1.1 - remove duplicates and return last key as single key
-
-"""
-Get Barnes & Noble EPUB user key from nook Studio log file
-"""
-
-__license__ = 'GPL v3'
-__version__ = "1.1"
-
-import sys
-import os
-import hashlib
-import getopt
-import re
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"ignoblekey.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-class DrmException(Exception):
- pass
-
-# Locate all of the nookStudy/nook for PC/Mac log file and return as list
-def getNookLogFiles():
- logFiles = []
- found = False
- if iswindows:
- import _winreg as winreg
-
- # some 64 bit machines do not have the proper registry key for some reason
- # or the python interface to the 32 vs 64 bit registry is broken
- paths = set()
- if 'LOCALAPPDATA' in os.environ.keys():
- # Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
- if os.path.isdir(path):
- paths.add(path)
- if 'USERPROFILE' in os.environ.keys():
- # Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Local"
- if os.path.isdir(path):
- paths.add(path)
- path = winreg.ExpandEnvironmentStrings(u"%USERPROFILE%")+u"\\AppData\\Roaming"
- if os.path.isdir(path):
- paths.add(path)
- # User Shell Folders show take precedent over Shell Folders if present
- try:
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
- if os.path.isdir(path):
- paths.add(path)
- except WindowsError:
- pass
- try:
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'AppData')[0]
- if os.path.isdir(path):
- paths.add(path)
- except WindowsError:
- pass
- try:
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
- if os.path.isdir(path):
- paths.add(path)
- except WindowsError:
- pass
- try:
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'AppData')[0]
- if os.path.isdir(path):
- paths.add(path)
- except WindowsError:
- pass
-
- for path in paths:
- # look for nookStudy log file
- logpath = path +'\\Barnes & Noble\\NOOKstudy\\logs\\BNClientLog.txt'
- if os.path.isfile(logpath):
- found = True
- print('Found nookStudy log file: ' + logpath.encode('ascii','ignore'))
- logFiles.append(logpath)
- else:
- home = os.getenv('HOME')
- # check for BNClientLog.txt in various locations
- testpath = home + '/Library/Application Support/Barnes & Noble/DesktopReader/logs/BNClientLog.txt'
- if os.path.isfile(testpath):
- logFiles.append(testpath)
- print('Found nookStudy log file: ' + testpath)
- found = True
- testpath = home + '/Library/Application Support/Barnes & Noble/DesktopReader/indices/BNClientLog.txt'
- if os.path.isfile(testpath):
- logFiles.append(testpath)
- print('Found nookStudy log file: ' + testpath)
- found = True
- testpath = home + '/Library/Application Support/Barnes & Noble/BNDesktopReader/logs/BNClientLog.txt'
- if os.path.isfile(testpath):
- logFiles.append(testpath)
- print('Found nookStudy log file: ' + testpath)
- found = True
- testpath = home + '/Library/Application Support/Barnes & Noble/BNDesktopReader/indices/BNClientLog.txt'
- if os.path.isfile(testpath):
- logFiles.append(testpath)
- print('Found nookStudy log file: ' + testpath)
- found = True
-
- if not found:
- print('No nook Study log files have been found.')
- return logFiles
-
-
-# Extract CCHash key(s) from log file
-def getKeysFromLog(kLogFile):
- keys = []
- regex = re.compile("ccHash: \"(.{28})\"");
- for line in open(kLogFile):
- for m in regex.findall(line):
- keys.append(m)
- return keys
-
-# interface for calibre plugin
-def nookkeys(files = []):
- keys = []
- if files == []:
- files = getNookLogFiles()
- for file in files:
- fileKeys = getKeysFromLog(file)
- if fileKeys:
- print u"Found {0} keys in the Nook Study log files".format(len(fileKeys))
- keys.extend(fileKeys)
- return list(set(keys))
-
-# interface for Python DeDRM
-# returns single key or multiple keys, depending on path or file passed in
-def getkey(outpath, files=[]):
- keys = nookkeys(files)
- if len(keys) > 0:
- if not os.path.isdir(outpath):
- outfile = outpath
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(keys[-1])
- print u"Saved a key to {0}".format(outfile)
- else:
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(outpath,u"nookkey{0:d}.b64".format(keycount))
- if not os.path.exists(outfile):
- break
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(key)
- print u"Saved a key to {0}".format(outfile)
- return True
- return False
-
-def usage(progname):
- print u"Finds the nook Study encryption keys."
- print u"Keys are saved to the current directory, or a specified output directory."
- print u"If a file name is passed instead of a directory, only the first key is saved, in that file."
- print u"Usage:"
- print u" {0:s} [-h] [-k <logFile>] [<outpath>]".format(progname)
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2015 Apprentice Alf".format(progname,__version__)
-
- try:
- opts, args = getopt.getopt(argv[1:], "hk:")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
- usage(progname)
- sys.exit(2)
-
- files = []
- for o, a in opts:
- if o == "-h":
- usage(progname)
- sys.exit(0)
- if o == "-k":
- files = [a]
-
- if len(args) > 1:
- usage(progname)
- sys.exit(2)
-
- if len(args) == 1:
- # save to the specified file or directory
- outpath = args[0]
- if not os.path.isabs(outpath):
- outpath = os.path.abspath(outpath)
- else:
- # save to the same directory as the script
- outpath = os.path.dirname(argv[0])
-
- # make sure the outpath is the
- outpath = os.path.realpath(os.path.normpath(outpath))
-
- if not getkey(outpath, files):
- print u"Could not retrieve nook Study key."
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- import traceback
- except:
- return cli_main()
-
- class ExceptionDialog(Tkinter.Frame):
- def __init__(self, root, text):
- Tkinter.Frame.__init__(self, root, border=5)
- label = Tkinter.Label(self, text=u"Unexpected error:",
- anchor=Tkconstants.W, justify=Tkconstants.LEFT)
- label.pack(fill=Tkconstants.X, expand=0)
- self.text = Tkinter.Text(self)
- self.text.pack(fill=Tkconstants.BOTH, expand=1)
-
- self.text.insert(Tkconstants.END, text)
-
-
- argv=unicode_argv()
- root = Tkinter.Tk()
- root.withdraw()
- progpath, progname = os.path.split(argv[0])
- success = False
- try:
- keys = nookkeys()
- keycount = 0
- for key in keys:
- print key
- while True:
- keycount += 1
- outfile = os.path.join(progpath,u"nookkey{0:d}.b64".format(keycount))
- if not os.path.exists(outfile):
- break
-
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(key)
- success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except DrmException, e:
- tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
- except Exception:
- root.wm_state('normal')
- root.title(progname)
- text = traceback.format_exc()
- ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
- root.mainloop()
- if not success:
- return 1
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw
deleted file mode 100644
index e9637a1..0000000
--- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw
+++ /dev/null
@@ -1,258 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# ignoblekeyfetch.pyw, version 1.1
-# Copyright © 2015 Apprentice Harper
-
-# Released under the terms of the GNU General Public Licence, version 3
-# <http://www.gnu.org/licenses/>
-
-# Based on discoveries by "Nobody You Know"
-# Code partly based on ignoblekeygen.py by several people.
-
-# Windows users: Before running this program, you must first install Python.
-# We recommend ActiveState Python 2.7.X for Windows from
-# http://www.activestate.com/activepython/downloads.
-# Then save this script file as ignoblekeyfetch.pyw and double-click on it to run it.
-#
-# Mac OS X users: Save this script file as ignoblekeyfetch.pyw. You can run this
-# program from the command line (python ignoblekeyfetch.pyw) or by double-clicking
-# it when it has been associated with PythonLauncher.
-
-# Revision history:
-# 1.0 - Initial version
-# 1.1 - Try second URL if first one fails
-
-"""
-Fetch Barnes & Noble EPUB user key from B&N servers using email and password
-"""
-
-__license__ = 'GPL v3'
-__version__ = "1.1"
-
-import sys
-import os
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"ignoblekeyfetch.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-
-class IGNOBLEError(Exception):
- pass
-
-def fetch_key(email, password):
- # change email and password to utf-8 if unicode
- if type(email)==unicode:
- email = email.encode('utf-8')
- if type(password)==unicode:
- password = password.encode('utf-8')
-
- import random
- random = "%030x" % random.randrange(16**30)
-
- import urllib, urllib2, re
-
- # try the URL from nook for PC
- fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
- fetch_url += urllib.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress="
- fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
- #print fetch_url
-
- found = ''
- try:
- req = urllib2.Request(fetch_url)
- response = urllib2.urlopen(req)
- the_page = response.read()
- #print the_page
- found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
- except:
- found = ''
- if len(found)!=28:
- # try the URL from android devices
- fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
- fetch_url += urllib.quote(password,'')+"&devID=hobbes_9.3.50818_"+random+"&emailAddress="
- fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
- #print fetch_url
-
- found = ''
- try:
- req = urllib2.Request(fetch_url)
- response = urllib2.urlopen(req)
- the_page = response.read()
- #print the_page
- found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
- except:
- found = ''
-
- return found
-
-
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- if len(argv) != 4:
- print u"usage: {0} <email> <password> <keyfileout.b64>".format(progname)
- return 1
- email, password, keypath = argv[1:]
- userkey = fetch_key(email, password)
- if len(userkey) == 28:
- open(keypath,'wb').write(userkey)
- return 0
- print u"Failed to fetch key."
- return 1
-
-
-def gui_main():
- try:
- import Tkinter
- import tkFileDialog
- import Tkconstants
- import tkMessageBox
- import traceback
- except:
- return cli_main()
-
- class DecryptionDialog(Tkinter.Frame):
- def __init__(self, root):
- Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Enter parameters")
- self.status.pack(fill=Tkconstants.X, expand=1)
- body = Tkinter.Frame(self)
- body.pack(fill=Tkconstants.X, expand=1)
- sticky = Tkconstants.E + Tkconstants.W
- body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Account email address").grid(row=0)
- self.name = Tkinter.Entry(body, width=40)
- self.name.grid(row=0, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Account password").grid(row=1)
- self.ccn = Tkinter.Entry(body, width=40)
- self.ccn.grid(row=1, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
- self.keypath = Tkinter.Entry(body, width=40)
- self.keypath.grid(row=2, column=1, sticky=sticky)
- self.keypath.insert(2, u"bnepubkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
- button.grid(row=2, column=2)
- buttons = Tkinter.Frame(self)
- buttons.pack()
- botton = Tkinter.Button(
- buttons, text=u"Fetch", width=10, command=self.generate)
- botton.pack(side=Tkconstants.LEFT)
- Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
- button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
- button.pack(side=Tkconstants.RIGHT)
-
- def get_keypath(self):
- keypath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select B&N ePub key file to produce",
- defaultextension=u".b64",
- filetypes=[('base64-encoded files', '.b64'),
- ('All Files', '.*')])
- if keypath:
- keypath = os.path.normpath(keypath)
- self.keypath.delete(0, Tkconstants.END)
- self.keypath.insert(0, keypath)
- return
-
- def generate(self):
- email = self.name.get()
- password = self.ccn.get()
- keypath = self.keypath.get()
- if not email:
- self.status['text'] = u"Email address not given"
- return
- if not password:
- self.status['text'] = u"Account password not given"
- return
- if not keypath:
- self.status['text'] = u"Output keyfile path not set"
- return
- self.status['text'] = u"Fetching..."
- try:
- userkey = fetch_key(email, password)
- except Exception, e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
- return
- if len(userkey) == 28:
- open(keypath,'wb').write(userkey)
- self.status['text'] = u"Keyfile fetched successfully"
- else:
- self.status['text'] = u"Keyfile fetch failed."
-
- root = Tkinter.Tk()
- root.title(u"Barnes & Noble ePub Keyfile Fetch v.{0}".format(__version__))
- root.resizable(True, False)
- root.minsize(300, 0)
- DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
- root.mainloop()
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw
deleted file mode 100644
index d2917c7..0000000
--- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw
+++ /dev/null
@@ -1,332 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# ignoblekeygen.pyw, version 2.5
-# Copyright © 2009-2010 i♥cabbages
-
-# Released under the terms of the GNU General Public Licence, version 3
-# <http://www.gnu.org/licenses/>
-
-# Modified 2010–2013 by some_updates, DiapDealer and Apprentice Alf
-
-# Windows users: Before running this program, you must first install Python.
-# We recommend ActiveState Python 2.7.X for Windows (x86) from
-# http://www.activestate.com/activepython/downloads.
-# You must also install PyCrypto from
-# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-# (make certain to install the version for Python 2.7).
-# Then save this script file as ignoblekeygen.pyw and double-click on it to run it.
-#
-# Mac OS X users: Save this script file as ignoblekeygen.pyw. You can run this
-# program from the command line (python ignoblekeygen.pyw) or by double-clicking
-# it when it has been associated with PythonLauncher.
-
-# Revision history:
-# 1 - Initial release
-# 2 - Add OS X support by using OpenSSL when available (taken/modified from ineptepub v5)
-# 2.1 - Allow Windows versions of libcrypto to be found
-# 2.2 - On Windows try PyCrypto first and then OpenSSL next
-# 2.3 - Modify interface to allow use of import
-# 2.4 - Improvements to UI and now works in plugins
-# 2.5 - Additional improvement for unicode and plugin support
-# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
-# 2.7 - Work if TkInter is missing
-# 2.8 - Fix bug in stand-alone use (import tkFileDialog)
-
-"""
-Generate Barnes & Noble EPUB user key from name and credit card number.
-"""
-
-__license__ = 'GPL v3'
-__version__ = "2.8"
-
-import sys
-import os
-import hashlib
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"ignoblekeygen.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-
-class IGNOBLEError(Exception):
- pass
-
-def _load_crypto_libcrypto():
- from ctypes import CDLL, POINTER, c_void_p, c_char_p, c_int, c_long, \
- Structure, c_ulong, create_string_buffer, cast
- from ctypes.util import find_library
-
- if iswindows:
- libcrypto = find_library('libeay32')
- else:
- libcrypto = find_library('crypto')
-
- if libcrypto is None:
- raise IGNOBLEError('libcrypto not found')
- libcrypto = CDLL(libcrypto)
-
- AES_MAXNR = 14
-
- c_char_pp = POINTER(c_char_p)
- c_int_p = POINTER(c_int)
-
- class AES_KEY(Structure):
- _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
- ('rounds', c_int)]
- AES_KEY_p = POINTER(AES_KEY)
-
- def F(restype, name, argtypes):
- func = getattr(libcrypto, name)
- func.restype = restype
- func.argtypes = argtypes
- return func
-
- AES_set_encrypt_key = F(c_int, 'AES_set_encrypt_key',
- [c_char_p, c_int, AES_KEY_p])
- AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
- [c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
- c_int])
-
- class AES(object):
- def __init__(self, userkey, iv):
- self._blocksize = len(userkey)
- self._iv = iv
- key = self._key = AES_KEY()
- rv = AES_set_encrypt_key(userkey, len(userkey) * 8, key)
- if rv < 0:
- raise IGNOBLEError('Failed to initialize AES Encrypt key')
-
- def encrypt(self, data):
- out = create_string_buffer(len(data))
- rv = AES_cbc_encrypt(data, out, len(data), self._key, self._iv, 1)
- if rv == 0:
- raise IGNOBLEError('AES encryption failed')
- return out.raw
-
- return AES
-
-def _load_crypto_pycrypto():
- from Crypto.Cipher import AES as _AES
-
- class AES(object):
- def __init__(self, key, iv):
- self._aes = _AES.new(key, _AES.MODE_CBC, iv)
-
- def encrypt(self, data):
- return self._aes.encrypt(data)
-
- return AES
-
-def _load_crypto():
- AES = None
- cryptolist = (_load_crypto_libcrypto, _load_crypto_pycrypto)
- if sys.platform.startswith('win'):
- cryptolist = (_load_crypto_pycrypto, _load_crypto_libcrypto)
- for loader in cryptolist:
- try:
- AES = loader()
- break
- except (ImportError, IGNOBLEError):
- pass
- return AES
-
-AES = _load_crypto()
-
-def normalize_name(name):
- return ''.join(x for x in name.lower() if x != ' ')
-
-
-def generate_key(name, ccn):
- # remove spaces and case from name and CC numbers.
- if type(name)==unicode:
- name = name.encode('utf-8')
- if type(ccn)==unicode:
- ccn = ccn.encode('utf-8')
-
- name = normalize_name(name) + '\x00'
- ccn = normalize_name(ccn) + '\x00'
-
- name_sha = hashlib.sha1(name).digest()[:16]
- ccn_sha = hashlib.sha1(ccn).digest()[:16]
- both_sha = hashlib.sha1(name + ccn).digest()
- aes = AES(ccn_sha, name_sha)
- crypt = aes.encrypt(both_sha + ('\x0c' * 0x0c))
- userkey = hashlib.sha1(crypt).digest()
- return userkey.encode('base64')
-
-
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- if AES is None:
- print "%s: This script requires OpenSSL or PyCrypto, which must be installed " \
- "separately. Read the top-of-script comment for details." % \
- (progname,)
- return 1
- if len(argv) != 4:
- print u"usage: {0} <Name> <CC#> <keyfileout.b64>".format(progname)
- return 1
- name, ccn, keypath = argv[1:]
- userkey = generate_key(name, ccn)
- open(keypath,'wb').write(userkey)
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- import tkFileDialog
- import traceback
- except:
- return cli_main()
-
- class DecryptionDialog(Tkinter.Frame):
- def __init__(self, root):
- Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Enter parameters")
- self.status.pack(fill=Tkconstants.X, expand=1)
- body = Tkinter.Frame(self)
- body.pack(fill=Tkconstants.X, expand=1)
- sticky = Tkconstants.E + Tkconstants.W
- body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Account Name").grid(row=0)
- self.name = Tkinter.Entry(body, width=40)
- self.name.grid(row=0, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"CC#").grid(row=1)
- self.ccn = Tkinter.Entry(body, width=40)
- self.ccn.grid(row=1, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"Output file").grid(row=2)
- self.keypath = Tkinter.Entry(body, width=40)
- self.keypath.grid(row=2, column=1, sticky=sticky)
- self.keypath.insert(2, u"bnepubkey.b64")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
- button.grid(row=2, column=2)
- buttons = Tkinter.Frame(self)
- buttons.pack()
- botton = Tkinter.Button(
- buttons, text=u"Generate", width=10, command=self.generate)
- botton.pack(side=Tkconstants.LEFT)
- Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
- button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
- button.pack(side=Tkconstants.RIGHT)
-
- def get_keypath(self):
- keypath = tkFileDialog.asksaveasfilename(
- parent=None, title=u"Select B&N ePub key file to produce",
- defaultextension=u".b64",
- filetypes=[('base64-encoded files', '.b64'),
- ('All Files', '.*')])
- if keypath:
- keypath = os.path.normpath(keypath)
- self.keypath.delete(0, Tkconstants.END)
- self.keypath.insert(0, keypath)
- return
-
- def generate(self):
- name = self.name.get()
- ccn = self.ccn.get()
- keypath = self.keypath.get()
- if not name:
- self.status['text'] = u"Name not specified"
- return
- if not ccn:
- self.status['text'] = u"Credit card number not specified"
- return
- if not keypath:
- self.status['text'] = u"Output keyfile path not specified"
- return
- self.status['text'] = u"Generating..."
- try:
- userkey = generate_key(name, ccn)
- except Exception, e:
- self.status['text'] = u"Error: (0}".format(e.args[0])
- return
- open(keypath,'wb').write(userkey)
- self.status['text'] = u"Keyfile successfully generated"
-
- root = Tkinter.Tk()
- if AES is None:
- root.withdraw()
- tkMessageBox.showerror(
- "Ignoble EPUB Keyfile Generator",
- "This script requires OpenSSL or PyCrypto, which must be installed "
- "separately. Read the top-of-script comment for details.")
- return 1
- root.title(u"Barnes & Noble ePub Keyfile Generator v.{0}".format(__version__))
- root.resizable(True, False)
- root.minsize(300, 0)
- DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
- root.mainloop()
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw
deleted file mode 100644
index ff8d1ee..0000000
--- a/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw
+++ /dev/null
@@ -1,468 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# androidkindlekey.py
-# Copyright © 2013-15 by Thom and Apprentice Harper
-# Some portions Copyright © 2010-15 by some_updates and Apprentice Alf
-#
-
-# Revision history:
-# 1.0 - AmazonSecureStorage.xml decryption to serial number
-# 1.1 - map_data_storage.db decryption to serial number
-# 1.2 - Changed to be callable from AppleScript by returning only serial number
-# - and changed name to androidkindlekey.py
-# - and added in unicode command line support
-# 1.3 - added in TkInter interface, output to a file
-# 1.4 - Fix some problems identified by Aldo Bleeker
-# 1.5 - Fix another problem identified by Aldo Bleeker
-
-"""
-Retrieve Kindle for Android Serial Number.
-"""
-
-__license__ = 'GPL v3'
-__version__ = '1.5'
-
-import os
-import sys
-import traceback
-import getopt
-import tempfile
-import zlib
-import tarfile
-from hashlib import md5
-from cStringIO import StringIO
-from binascii import a2b_hex, b2a_hex
-
-# Routines common to Mac and PC
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"kindlekey.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-class DrmException(Exception):
- pass
-
-STORAGE = u"backup.ab"
-STORAGE1 = u"AmazonSecureStorage.xml"
-STORAGE2 = u"map_data_storage.db"
-
-class AndroidObfuscation(object):
- '''AndroidObfuscation
- For the key, it's written in java, and run in android dalvikvm
- '''
-
- key = a2b_hex('0176e04c9408b1702d90be333fd53523')
-
- def encrypt(self, plaintext):
- cipher = self._get_cipher()
- padding = len(self.key) - len(plaintext) % len(self.key)
- plaintext += chr(padding) * padding
- return b2a_hex(cipher.encrypt(plaintext))
-
- def decrypt(self, ciphertext):
- cipher = self._get_cipher()
- plaintext = cipher.decrypt(a2b_hex(ciphertext))
- return plaintext[:-ord(plaintext[-1])]
-
- def _get_cipher(self):
- try:
- from Crypto.Cipher import AES
- return AES.new(self.key)
- except ImportError:
- from aescbc import AES, noPadding
- return AES(self.key, padding=noPadding())
-
-class AndroidObfuscationV2(AndroidObfuscation):
- '''AndroidObfuscationV2
- '''
-
- count = 503
- password = 'Thomsun was here!'
-
- def __init__(self, salt):
- key = self.password + salt
- for _ in range(self.count):
- key = md5(key).digest()
- self.key = key[:8]
- self.iv = key[8:16]
-
- def _get_cipher(self):
- try :
- from Crypto.Cipher import DES
- return DES.new(self.key, DES.MODE_CBC, self.iv)
- except ImportError:
- from python_des import Des, CBC
- return Des(self.key, CBC, self.iv)
-
-def parse_preference(path):
- ''' parse android's shared preference xml '''
- storage = {}
- read = open(path)
- for line in read:
- line = line.strip()
- # <string name="key">value</string>
- if line.startswith('<string name="'):
- index = line.find('"', 14)
- key = line[14:index]
- value = line[index+2:-9]
- storage[key] = value
- read.close()
- return storage
-
-def get_serials1(path=STORAGE1):
- ''' get serials from android's shared preference xml '''
-
- if not os.path.isfile(path):
- return []
-
- storage = parse_preference(path)
- salt = storage.get('AmazonSaltKey')
- if salt and len(salt) == 16:
- obfuscation = AndroidObfuscationV2(a2b_hex(salt))
- else:
- obfuscation = AndroidObfuscation()
-
- def get_value(key):
- encrypted_key = obfuscation.encrypt(key)
- encrypted_value = storage.get(encrypted_key)
- if encrypted_value:
- return obfuscation.decrypt(encrypted_value)
- return ''
-
- # also see getK4Pids in kgenpids.py
- try:
- dsnid = get_value('DsnId')
- except:
- sys.stderr.write('cannot get DsnId\n')
- return []
-
- try:
- tokens = set(get_value('kindle.account.tokens').split(','))
- except:
- return []
-
- serials = []
- if dsnid:
- serials.append(dsnid)
- for token in tokens:
- if token:
- serials.append('%s%s' % (dsnid, token))
- serials.append(token)
- return serials
-
-def get_serials2(path=STORAGE2):
- ''' get serials from android's sql database '''
- if not os.path.isfile(path):
- return []
-
- import sqlite3
- connection = sqlite3.connect(path)
- cursor = connection.cursor()
- cursor.execute('''select userdata_value from userdata where userdata_key like '%/%token.device.deviceserialname%' ''')
- userdata_keys = cursor.fetchall()
- dsns = []
- for userdata_row in userdata_keys:
- try:
- if userdata_row and userdata_row[0]:
- userdata_utf8 = userdata_row[0].encode('utf8')
- if len(userdata_utf8) > 0:
- dsns.append(userdata_utf8)
- except:
- print "Error getting one of the device serial name keys"
- traceback.print_exc()
- pass
- dsns = list(set(dsns))
-
- cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
- userdata_keys = cursor.fetchall()
- tokens = []
- for userdata_row in userdata_keys:
- try:
- if userdata_row and userdata_row[0]:
- userdata_utf8 = userdata_row[0].encode('utf8')
- if len(userdata_utf8) > 0:
- tokens.append(userdata_utf8)
- except:
- print "Error getting one of the account token keys"
- traceback.print_exc()
- pass
- tokens = list(set(tokens))
-
- serials = []
- for x in dsns:
- serials.append(x)
- for y in tokens:
- serials.append('%s%s' % (x, y))
- for y in tokens:
- serials.append(y)
- return serials
-
-def get_serials(path=STORAGE):
- '''get serials from files in from android backup.ab
- backup.ab can be get using adb command:
- shell> adb backup com.amazon.kindle
- or from individual files if they're passed.
- '''
- if not os.path.isfile(path):
- return []
-
- basename = os.path.basename(path)
- if basename == STORAGE1:
- return get_serials1(path)
- elif basename == STORAGE2:
- return get_serials2(path)
-
- output = None
- try :
- read = open(path, 'rb')
- head = read.read(24)
- if head[:14] == 'ANDROID BACKUP':
- output = StringIO(zlib.decompress(read.read()))
- except Exception:
- pass
- finally:
- read.close()
-
- if not output:
- return []
-
- serials = []
- tar = tarfile.open(fileobj=output)
- for member in tar.getmembers():
- if member.name.strip().endswith(STORAGE1):
- write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- write.write(tar.extractfile(member).read())
- write.close()
- write_path = os.path.abspath(write.name)
- serials.extend(get_serials1(write_path))
- os.remove(write_path)
- elif member.name.strip().endswith(STORAGE2):
- write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- write.write(tar.extractfile(member).read())
- write.close()
- write_path = os.path.abspath(write.name)
- serials.extend(get_serials2(write_path))
- os.remove(write_path)
- return list(set(serials))
-
-__all__ = [ 'get_serials', 'getkey']
-
-# procedure for CLI and GUI interfaces
-# returns single or multiple keys (one per line) in the specified file
-def getkey(outfile, inpath):
- keys = get_serials(inpath)
- if len(keys) > 0:
- with file(outfile, 'w') as keyfileout:
- for key in keys:
- keyfileout.write(key)
- keyfileout.write("\n")
- return True
- return False
-
-
-def usage(progname):
- print u"Decrypts the serial number(s) of Kindle For Android from Android backup or file"
- print u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+."
- print u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml"
- print u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db"
- print u""
- print u"Usage:"
- print u" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname)
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)
-
- try:
- opts, args = getopt.getopt(argv[1:], "hb:")
- except getopt.GetoptError, err:
- usage(progname)
- print u"\nError in options or arguments: {0}".format(err.args[0])
- return 2
-
- inpath = ""
- for o, a in opts:
- if o == "-h":
- usage(progname)
- return 0
- if o == "-b":
- inpath = a
-
- if len(args) > 1:
- usage(progname)
- return 2
-
- if len(args) == 1:
- # save to the specified file or directory
- outfile = args[0]
- if not os.path.isabs(outfile):
- outfile = os.path.join(os.path.dirname(argv[0]),outfile)
- outfile = os.path.abspath(outfile)
- if os.path.isdir(outfile):
- outfile = os.path.join(os.path.dirname(argv[0]),"androidkindlekey.k4a")
- else:
- # save to the same directory as the script
- outfile = os.path.join(os.path.dirname(argv[0]),"androidkindlekey.k4a")
-
- # make sure the outpath is OK
- outfile = os.path.realpath(os.path.normpath(outfile))
-
- if not os.path.isfile(inpath):
- usage(progname)
- print u"\n{0:s} file not found".format(inpath)
- return 2
-
- if getkey(outfile, inpath):
- print u"\nSaved Kindle for Android key to {0}".format(outfile)
- else:
- print u"\nCould not retrieve Kindle for Android key."
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- import tkFileDialog
- except:
- print "Tkinter not installed"
- return cli_main()
-
- class DecryptionDialog(Tkinter.Frame):
- def __init__(self, root):
- Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Select backup.ab file")
- self.status.pack(fill=Tkconstants.X, expand=1)
- body = Tkinter.Frame(self)
- body.pack(fill=Tkconstants.X, expand=1)
- sticky = Tkconstants.E + Tkconstants.W
- body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0)
- self.keypath = Tkinter.Entry(body, width=40)
- self.keypath.grid(row=0, column=1, sticky=sticky)
- self.keypath.insert(2, u"backup.ab")
- button = Tkinter.Button(body, text=u"...", command=self.get_keypath)
- button.grid(row=0, column=2)
- buttons = Tkinter.Frame(self)
- buttons.pack()
- button2 = Tkinter.Button(
- buttons, text=u"Extract", width=10, command=self.generate)
- button2.pack(side=Tkconstants.LEFT)
- Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
- button3 = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
- button3.pack(side=Tkconstants.RIGHT)
-
- def get_keypath(self):
- keypath = tkFileDialog.askopenfilename(
- parent=None, title=u"Select backup.ab file",
- defaultextension=u".ab",
- filetypes=[('adb backup com.amazon.kindle', '.ab'),
- ('All Files', '.*')])
- if keypath:
- keypath = os.path.normpath(keypath)
- self.keypath.delete(0, Tkconstants.END)
- self.keypath.insert(0, keypath)
- return
-
- def generate(self):
- inpath = self.keypath.get()
- self.status['text'] = u"Getting key..."
- try:
- keys = get_serials(inpath)
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount))
- if not os.path.exists(outfile):
- break
-
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(key)
- success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except Exception, e:
- self.status['text'] = u"Error: {0}".format(e.args[0])
- return
- self.status['text'] = u"Select backup.ab file"
-
- argv=unicode_argv()
- progpath, progname = os.path.split(argv[0])
- root = Tkinter.Tk()
- root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__))
- root.resizable(True, False)
- root.minsize(300, 0)
- DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
- root.mainloop()
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw
deleted file mode 100644
index e20b7c9..0000000
--- a/Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw
+++ /dev/null
@@ -1,1727 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# kindlekey.py
-# Copyright © 2008-2017 Apprentice Harper et al.
-
-__license__ = 'GPL v3'
-__version__ = '2.5'
-
-# Revision history:
-# 1.0 - Kindle info file decryption, extracted from k4mobidedrm, etc.
-# 1.1 - Added Tkinter to match adobekey.py
-# 1.2 - Fixed testing of successful retrieval on Mac
-# 1.3 - Added getkey interface for Windows DeDRM application
-# Simplified some of the Kindle for Mac code.
-# 1.4 - Remove dependency on alfcrypto
-# 1.5 - moved unicode_argv call inside main for Windows DeDRM compatibility
-# 1.6 - Fixed a problem getting the disk serial numbers
-# 1.7 - Work if TkInter is missing
-# 1.8 - Fixes for Kindle for Mac, and non-ascii in Windows user names
-# 1.9 - Fixes for Unicode in Windows user names
-# 2.0 - Added comments and extra fix for non-ascii Windows user names
-# 2.1 - Fixed Kindle for PC encryption changes March 2016
-# 2.2 - Fixes for Macs with bonded ethernet ports
-# Also removed old .kinfo file support (pre-2011)
-# 2.3 - Added more field names thanks to concavegit's KFX code.
-# 2.4 - Fix for complex Mac disk setups, thanks to Tibs
-# 2.5 - Final Fix for Windows user names with non-ascii characters, thanks to oneofusoneofus
-
-
-"""
-Retrieve Kindle for PC/Mac user key.
-"""
-
-import sys, os, re
-from struct import pack, unpack, unpack_from
-import json
-import getopt
-
-# Routines common to Mac and PC
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-try:
- from calibre.constants import iswindows, isosx
-except:
- iswindows = sys.platform.startswith('win')
- isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
- # as a list of Unicode strings and encode them as utf-8
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"kindlekey.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-class DrmException(Exception):
- pass
-
-# crypto digestroutines
-import hashlib
-
-def MD5(message):
- ctx = hashlib.md5()
- ctx.update(message)
- return ctx.digest()
-
-def SHA1(message):
- ctx = hashlib.sha1()
- ctx.update(message)
- return ctx.digest()
-
-def SHA256(message):
- ctx = hashlib.sha256()
- ctx.update(message)
- return ctx.digest()
-
-# For K4M/PC 1.6.X and later
-# generate table of prime number less than or equal to int n
-def primes(n):
- if n==2: return [2]
- elif n<2: return []
- s=range(3,n+1,2)
- mroot = n ** 0.5
- half=(n+1)/2-1
- i=0
- m=3
- while m <= mroot:
- if s[i]:
- j=(m*m-3)/2
- s[j]=0
- while j<half:
- s[j]=0
- j+=m
- i=i+1
- m=2*i+3
- return [2]+[x for x in s if x]
-
-# Encode the bytes in data with the characters in map
-def encode(data, map):
- result = ''
- for char in data:
- value = ord(char)
- Q = (value ^ 0x80) // len(map)
- R = value % len(map)
- result += map[Q]
- result += map[R]
- return result
-
-# Hash the bytes in data and then encode the digest with the characters in map
-def encodeHash(data,map):
- return encode(MD5(data),map)
-
-# Decode the string in data with the characters in map. Returns the decoded bytes
-def decode(data,map):
- result = ''
- for i in range (0,len(data)-1,2):
- high = map.find(data[i])
- low = map.find(data[i+1])
- if (high == -1) or (low == -1) :
- break
- value = (((high * len(map)) ^ 0x80) & 0xFF) + low
- result += pack('B',value)
- return result
-
-# Routines unique to Mac and PC
-if iswindows:
- from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
- create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
- string_at, Structure, c_void_p, cast
-
- import _winreg as winreg
- MAX_PATH = 255
- kernel32 = windll.kernel32
- advapi32 = windll.advapi32
- crypt32 = windll.crypt32
-
- try:
- # try to get fast routines from alfcrypto
- from alfcrypto import AES_CBC, KeyIVGen
- except:
- # alfcrypto not available, so use python implementations
- """
- Routines for doing AES CBC in one file
-
- Modified by some_updates to extract
- and combine only those parts needed for AES CBC
- into one simple to add python file
-
- Original Version
- Copyright (c) 2002 by Paul A. Lambert
- Under:
- CryptoPy Artisitic License Version 1.0
- See the wonderful pure python package cryptopy-1.2.5
- and read its LICENSE.txt for complete license details.
- """
-
- class CryptoError(Exception):
- """ Base class for crypto exceptions """
- def __init__(self,errorMessage='Error!'):
- self.message = errorMessage
- def __str__(self):
- return self.message
-
- class InitCryptoError(CryptoError):
- """ Crypto errors during algorithm initialization """
- class BadKeySizeError(InitCryptoError):
- """ Bad key size error """
- class EncryptError(CryptoError):
- """ Error in encryption processing """
- class DecryptError(CryptoError):
- """ Error in decryption processing """
- class DecryptNotBlockAlignedError(DecryptError):
- """ Error in decryption processing """
-
- def xorS(a,b):
- """ XOR two strings """
- assert len(a)==len(b)
- x = []
- for i in range(len(a)):
- x.append( chr(ord(a[i])^ord(b[i])))
- return ''.join(x)
-
- def xor(a,b):
- """ XOR two strings """
- x = []
- for i in range(min(len(a),len(b))):
- x.append( chr(ord(a[i])^ord(b[i])))
- return ''.join(x)
-
- """
- Base 'BlockCipher' and Pad classes for cipher instances.
- BlockCipher supports automatic padding and type conversion. The BlockCipher
- class was written to make the actual algorithm code more readable and
- not for performance.
- """
-
- class BlockCipher:
- """ Block ciphers """
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.resetEncrypt()
- self.resetDecrypt()
- def resetEncrypt(self):
- self.encryptBlockCount = 0
- self.bytesToEncrypt = ''
- def resetDecrypt(self):
- self.decryptBlockCount = 0
- self.bytesToDecrypt = ''
-
- def encrypt(self, plainText, more = None):
- """ Encrypt a string and return a binary string """
- self.bytesToEncrypt += plainText # append plainText to any bytes from prior encrypt
- numBlocks, numExtraBytes = divmod(len(self.bytesToEncrypt), self.blockSize)
- cipherText = ''
- for i in range(numBlocks):
- bStart = i*self.blockSize
- ctBlock = self.encryptBlock(self.bytesToEncrypt[bStart:bStart+self.blockSize])
- self.encryptBlockCount += 1
- cipherText += ctBlock
- if numExtraBytes > 0: # save any bytes that are not block aligned
- self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:]
- else:
- self.bytesToEncrypt = ''
-
- if more == None: # no more data expected from caller
- finalBytes = self.padding.addPad(self.bytesToEncrypt,self.blockSize)
- if len(finalBytes) > 0:
- ctBlock = self.encryptBlock(finalBytes)
- self.encryptBlockCount += 1
- cipherText += ctBlock
- self.resetEncrypt()
- return cipherText
-
- def decrypt(self, cipherText, more = None):
- """ Decrypt a string and return a string """
- self.bytesToDecrypt += cipherText # append to any bytes from prior decrypt
-
- numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
- if more == None: # no more calls to decrypt, should have all the data
- if numExtraBytes != 0:
- raise DecryptNotBlockAlignedError, 'Data not block aligned on decrypt'
-
- # hold back some bytes in case last decrypt has zero len
- if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
- numBlocks -= 1
- numExtraBytes = self.blockSize
-
- plainText = ''
- for i in range(numBlocks):
- bStart = i*self.blockSize
- ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize])
- self.decryptBlockCount += 1
- plainText += ptBlock
-
- if numExtraBytes > 0: # save any bytes that are not block aligned
- self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:]
- else:
- self.bytesToEncrypt = ''
-
- if more == None: # last decrypt remove padding
- plainText = self.padding.removePad(plainText, self.blockSize)
- self.resetDecrypt()
- return plainText
-
-
- class Pad:
- def __init__(self):
- pass # eventually could put in calculation of min and max size extension
-
- class padWithPadLen(Pad):
- """ Pad a binary string with the length of the padding """
-
- def addPad(self, extraBytes, blockSize):
- """ Add padding to a binary string to make it an even multiple
- of the block size """
- blocks, numExtraBytes = divmod(len(extraBytes), blockSize)
- padLength = blockSize - numExtraBytes
- return extraBytes + padLength*chr(padLength)
-
- def removePad(self, paddedBinaryString, blockSize):
- """ Remove padding from a binary string """
- if not(0<len(paddedBinaryString)):
- raise DecryptNotBlockAlignedError, 'Expected More Data'
- return paddedBinaryString[:-ord(paddedBinaryString[-1])]
-
- class noPadding(Pad):
- """ No padding. Use this to get ECB behavior from encrypt/decrypt """
-
- def addPad(self, extraBytes, blockSize):
- """ Add no padding """
- return extraBytes
-
- def removePad(self, paddedBinaryString, blockSize):
- """ Remove no padding """
- return paddedBinaryString
-
- """
- Rijndael encryption algorithm
- This byte oriented implementation is intended to closely
- match FIPS specification for readability. It is not implemented
- for performance.
- """
-
- class Rijndael(BlockCipher):
- """ Rijndael encryption algorithm """
- def __init__(self, key = None, padding = padWithPadLen(), keySize=16, blockSize=16 ):
- self.name = 'RIJNDAEL'
- self.keySize = keySize
- self.strength = keySize*8
- self.blockSize = blockSize # blockSize is in bytes
- self.padding = padding # change default to noPadding() to get normal ECB behavior
-
- assert( keySize%4==0 and NrTable[4].has_key(keySize/4)),'key size must be 16,20,24,29 or 32 bytes'
- assert( blockSize%4==0 and NrTable.has_key(blockSize/4)), 'block size must be 16,20,24,29 or 32 bytes'
-
- self.Nb = self.blockSize/4 # Nb is number of columns of 32 bit words
- self.Nk = keySize/4 # Nk is the key length in 32-bit words
- self.Nr = NrTable[self.Nb][self.Nk] # The number of rounds (Nr) is a function of
- # the block (Nb) and key (Nk) sizes.
- if key != None:
- self.setKey(key)
-
- def setKey(self, key):
- """ Set a key and generate the expanded key """
- assert( len(key) == (self.Nk*4) ), 'Key length must be same as keySize parameter'
- self.__expandedKey = keyExpansion(self, key)
- self.reset() # BlockCipher.reset()
-
- def encryptBlock(self, plainTextBlock):
- """ Encrypt a block, plainTextBlock must be a array of bytes [Nb by 4] """
- self.state = self._toBlock(plainTextBlock)
- AddRoundKey(self, self.__expandedKey[0:self.Nb])
- for round in range(1,self.Nr): #for round = 1 step 1 to Nr
- SubBytes(self)
- ShiftRows(self)
- MixColumns(self)
- AddRoundKey(self, self.__expandedKey[round*self.Nb:(round+1)*self.Nb])
- SubBytes(self)
- ShiftRows(self)
- AddRoundKey(self, self.__expandedKey[self.Nr*self.Nb:(self.Nr+1)*self.Nb])
- return self._toBString(self.state)
-
-
- def decryptBlock(self, encryptedBlock):
- """ decrypt a block (array of bytes) """
- self.state = self._toBlock(encryptedBlock)
- AddRoundKey(self, self.__expandedKey[self.Nr*self.Nb:(self.Nr+1)*self.Nb])
- for round in range(self.Nr-1,0,-1):
- InvShiftRows(self)
- InvSubBytes(self)
- AddRoundKey(self, self.__expandedKey[round*self.Nb:(round+1)*self.Nb])
- InvMixColumns(self)
- InvShiftRows(self)
- InvSubBytes(self)
- AddRoundKey(self, self.__expandedKey[0:self.Nb])
- return self._toBString(self.state)
-
- def _toBlock(self, bs):
- """ Convert binary string to array of bytes, state[col][row]"""
- assert ( len(bs) == 4*self.Nb ), 'Rijndarl blocks must be of size blockSize'
- return [[ord(bs[4*i]),ord(bs[4*i+1]),ord(bs[4*i+2]),ord(bs[4*i+3])] for i in range(self.Nb)]
-
- def _toBString(self, block):
- """ Convert block (array of bytes) to binary string """
- l = []
- for col in block:
- for rowElement in col:
- l.append(chr(rowElement))
- return ''.join(l)
- #-------------------------------------
- """ Number of rounds Nr = NrTable[Nb][Nk]
-
- Nb Nk=4 Nk=5 Nk=6 Nk=7 Nk=8
- ------------------------------------- """
- NrTable = {4: {4:10, 5:11, 6:12, 7:13, 8:14},
- 5: {4:11, 5:11, 6:12, 7:13, 8:14},
- 6: {4:12, 5:12, 6:12, 7:13, 8:14},
- 7: {4:13, 5:13, 6:13, 7:13, 8:14},
- 8: {4:14, 5:14, 6:14, 7:14, 8:14}}
- #-------------------------------------
- def keyExpansion(algInstance, keyString):
- """ Expand a string of size keySize into a larger array """
- Nk, Nb, Nr = algInstance.Nk, algInstance.Nb, algInstance.Nr # for readability
- key = [ord(byte) for byte in keyString] # convert string to list
- w = [[key[4*i],key[4*i+1],key[4*i+2],key[4*i+3]] for i in range(Nk)]
- for i in range(Nk,Nb*(Nr+1)):
- temp = w[i-1] # a four byte column
- if (i%Nk) == 0 :
- temp = temp[1:]+[temp[0]] # RotWord(temp)
- temp = [ Sbox[byte] for byte in temp ]
- temp[0] ^= Rcon[i/Nk]
- elif Nk > 6 and i%Nk == 4 :
- temp = [ Sbox[byte] for byte in temp ] # SubWord(temp)
- w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] )
- return w
-
- Rcon = (0,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36, # note extra '0' !!!
- 0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,
- 0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91)
-
- #-------------------------------------
- def AddRoundKey(algInstance, keyBlock):
- """ XOR the algorithm state with a block of key material """
- for column in range(algInstance.Nb):
- for row in range(4):
- algInstance.state[column][row] ^= keyBlock[column][row]
- #-------------------------------------
-
- def SubBytes(algInstance):
- for column in range(algInstance.Nb):
- for row in range(4):
- algInstance.state[column][row] = Sbox[algInstance.state[column][row]]
-
- def InvSubBytes(algInstance):
- for column in range(algInstance.Nb):
- for row in range(4):
- algInstance.state[column][row] = InvSbox[algInstance.state[column][row]]
-
- Sbox = (0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,
- 0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
- 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,
- 0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
- 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,
- 0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
- 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,
- 0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
- 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,
- 0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
- 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,
- 0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
- 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,
- 0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
- 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,
- 0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
- 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,
- 0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
- 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,
- 0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
- 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,
- 0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
- 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,
- 0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
- 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,
- 0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
- 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,
- 0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
- 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,
- 0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
- 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,
- 0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16)
-
- InvSbox = (0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,
- 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
- 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,
- 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
- 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,
- 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
- 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,
- 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
- 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,
- 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
- 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,
- 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
- 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,
- 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
- 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,
- 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
- 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,
- 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
- 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,
- 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
- 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,
- 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
- 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,
- 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
- 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,
- 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
- 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,
- 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
- 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,
- 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
- 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,
- 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d)
-
- #-------------------------------------
- """ For each block size (Nb), the ShiftRow operation shifts row i
- by the amount Ci. Note that row 0 is not shifted.
- Nb C1 C2 C3
- ------------------- """
- shiftOffset = { 4 : ( 0, 1, 2, 3),
- 5 : ( 0, 1, 2, 3),
- 6 : ( 0, 1, 2, 3),
- 7 : ( 0, 1, 2, 4),
- 8 : ( 0, 1, 3, 4) }
- def ShiftRows(algInstance):
- tmp = [0]*algInstance.Nb # list of size Nb
- for r in range(1,4): # row 0 reamains unchanged and can be skipped
- for c in range(algInstance.Nb):
- tmp[c] = algInstance.state[(c+shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r]
- for c in range(algInstance.Nb):
- algInstance.state[c][r] = tmp[c]
- def InvShiftRows(algInstance):
- tmp = [0]*algInstance.Nb # list of size Nb
- for r in range(1,4): # row 0 reamains unchanged and can be skipped
- for c in range(algInstance.Nb):
- tmp[c] = algInstance.state[(c+algInstance.Nb-shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r]
- for c in range(algInstance.Nb):
- algInstance.state[c][r] = tmp[c]
- #-------------------------------------
- def MixColumns(a):
- Sprime = [0,0,0,0]
- for j in range(a.Nb): # for each column
- Sprime[0] = mul(2,a.state[j][0])^mul(3,a.state[j][1])^mul(1,a.state[j][2])^mul(1,a.state[j][3])
- Sprime[1] = mul(1,a.state[j][0])^mul(2,a.state[j][1])^mul(3,a.state[j][2])^mul(1,a.state[j][3])
- Sprime[2] = mul(1,a.state[j][0])^mul(1,a.state[j][1])^mul(2,a.state[j][2])^mul(3,a.state[j][3])
- Sprime[3] = mul(3,a.state[j][0])^mul(1,a.state[j][1])^mul(1,a.state[j][2])^mul(2,a.state[j][3])
- for i in range(4):
- a.state[j][i] = Sprime[i]
-
- def InvMixColumns(a):
- """ Mix the four bytes of every column in a linear way
- This is the opposite operation of Mixcolumn """
- Sprime = [0,0,0,0]
- for j in range(a.Nb): # for each column
- Sprime[0] = mul(0x0E,a.state[j][0])^mul(0x0B,a.state[j][1])^mul(0x0D,a.state[j][2])^mul(0x09,a.state[j][3])
- Sprime[1] = mul(0x09,a.state[j][0])^mul(0x0E,a.state[j][1])^mul(0x0B,a.state[j][2])^mul(0x0D,a.state[j][3])
- Sprime[2] = mul(0x0D,a.state[j][0])^mul(0x09,a.state[j][1])^mul(0x0E,a.state[j][2])^mul(0x0B,a.state[j][3])
- Sprime[3] = mul(0x0B,a.state[j][0])^mul(0x0D,a.state[j][1])^mul(0x09,a.state[j][2])^mul(0x0E,a.state[j][3])
- for i in range(4):
- a.state[j][i] = Sprime[i]
-
- #-------------------------------------
- def mul(a, b):
- """ Multiply two elements of GF(2^m)
- needed for MixColumn and InvMixColumn """
- if (a !=0 and b!=0):
- return Alogtable[(Logtable[a] + Logtable[b])%255]
- else:
- return 0
-
- Logtable = ( 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
- 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193,
- 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120,
- 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142,
- 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56,
- 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16,
- 126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186,
- 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87,
- 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232,
- 44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160,
- 127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183,
- 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157,
- 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209,
- 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171,
- 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165,
- 103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7)
-
- Alogtable= ( 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
- 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
- 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
- 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
- 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
- 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
- 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
- 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
- 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
- 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
- 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
- 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
- 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
- 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
- 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
- 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1)
-
-
-
-
- """
- AES Encryption Algorithm
- The AES algorithm is just Rijndael algorithm restricted to the default
- blockSize of 128 bits.
- """
-
- class AES(Rijndael):
- """ The AES algorithm is the Rijndael block cipher restricted to block
- sizes of 128 bits and key sizes of 128, 192 or 256 bits
- """
- def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
- """ Initialize AES, keySize is in bytes """
- if not (keySize == 16 or keySize == 24 or keySize == 32) :
- raise BadKeySizeError, 'Illegal AES key size, must be 16, 24, or 32 bytes'
-
- Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
-
- self.name = 'AES'
-
-
- """
- CBC mode of encryption for block ciphers.
- This algorithm mode wraps any BlockCipher to make a
- Cipher Block Chaining mode.
- """
- from random import Random # should change to crypto.random!!!
-
-
- class CBC(BlockCipher):
- """ The CBC class wraps block ciphers to make cipher block chaining (CBC) mode
- algorithms. The initialization (IV) is automatic if set to None. Padding
- is also automatic based on the Pad class used to initialize the algorithm
- """
- def __init__(self, blockCipherInstance, padding = padWithPadLen()):
- """ CBC algorithms are created by initializing with a BlockCipher instance """
- self.baseCipher = blockCipherInstance
- self.name = self.baseCipher.name + '_CBC'
- self.blockSize = self.baseCipher.blockSize
- self.keySize = self.baseCipher.keySize
- self.padding = padding
- self.baseCipher.padding = noPadding() # baseCipher should NOT pad!!
- self.r = Random() # for IV generation, currently uses
- # mediocre standard distro version <----------------
- import time
- newSeed = time.ctime()+str(self.r) # seed with instance location
- self.r.seed(newSeed) # to make unique
- self.reset()
-
- def setKey(self, key):
- self.baseCipher.setKey(key)
-
- # Overload to reset both CBC state and the wrapped baseCipher
- def resetEncrypt(self):
- BlockCipher.resetEncrypt(self) # reset CBC encrypt state (super class)
- self.baseCipher.resetEncrypt() # reset base cipher encrypt state
-
- def resetDecrypt(self):
- BlockCipher.resetDecrypt(self) # reset CBC state (super class)
- self.baseCipher.resetDecrypt() # reset base cipher decrypt state
-
- def encrypt(self, plainText, iv=None, more=None):
- """ CBC encryption - overloads baseCipher to allow optional explicit IV
- when iv=None, iv is auto generated!
- """
- if self.encryptBlockCount == 0:
- self.iv = iv
- else:
- assert(iv==None), 'IV used only on first call to encrypt'
-
- return BlockCipher.encrypt(self,plainText, more=more)
-
- def decrypt(self, cipherText, iv=None, more=None):
- """ CBC decryption - overloads baseCipher to allow optional explicit IV
- when iv=None, iv is auto generated!
- """
- if self.decryptBlockCount == 0:
- self.iv = iv
- else:
- assert(iv==None), 'IV used only on first call to decrypt'
-
- return BlockCipher.decrypt(self, cipherText, more=more)
-
- def encryptBlock(self, plainTextBlock):
- """ CBC block encryption, IV is set with 'encrypt' """
- auto_IV = ''
- if self.encryptBlockCount == 0:
- if self.iv == None:
- # generate IV and use
- self.iv = ''.join([chr(self.r.randrange(256)) for i in range(self.blockSize)])
- self.prior_encr_CT_block = self.iv
- auto_IV = self.prior_encr_CT_block # prepend IV if it's automatic
- else: # application provided IV
- assert(len(self.iv) == self.blockSize ),'IV must be same length as block'
- self.prior_encr_CT_block = self.iv
- """ encrypt the prior CT XORed with the PT """
- ct = self.baseCipher.encryptBlock( xor(self.prior_encr_CT_block, plainTextBlock) )
- self.prior_encr_CT_block = ct
- return auto_IV+ct
-
- def decryptBlock(self, encryptedBlock):
- """ Decrypt a single block """
-
- if self.decryptBlockCount == 0: # first call, process IV
- if self.iv == None: # auto decrypt IV?
- self.prior_CT_block = encryptedBlock
- return ''
- else:
- assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption"
- self.prior_CT_block = self.iv
-
- dct = self.baseCipher.decryptBlock(encryptedBlock)
- """ XOR the prior decrypted CT with the prior CT """
- dct_XOR_priorCT = xor( self.prior_CT_block, dct )
-
- self.prior_CT_block = encryptedBlock
-
- return dct_XOR_priorCT
-
-
- """
- AES_CBC Encryption Algorithm
- """
-
- class aescbc_AES_CBC(CBC):
- """ AES encryption in CBC feedback mode """
- def __init__(self, key=None, padding=padWithPadLen(), keySize=16):
- CBC.__init__( self, AES(key, noPadding(), keySize), padding)
- self.name = 'AES_CBC'
-
- class AES_CBC(object):
- def __init__(self):
- self._key = None
- self._iv = None
- self.aes = None
-
- def set_decrypt_key(self, userkey, iv):
- self._key = userkey
- self._iv = iv
- self.aes = aescbc_AES_CBC(userkey, noPadding(), len(userkey))
-
- def decrypt(self, data):
- iv = self._iv
- cleartext = self.aes.decrypt(iv + data)
- return cleartext
-
- import hmac
-
- class KeyIVGen(object):
- # this only exists in openssl so we will use pure python implementation instead
- # PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1',
- # [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
- def pbkdf2(self, passwd, salt, iter, keylen):
-
- def xorstr( a, b ):
- if len(a) != len(b):
- raise Exception("xorstr(): lengths differ")
- return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b)))
-
- def prf( h, data ):
- hm = h.copy()
- hm.update( data )
- return hm.digest()
-
- def pbkdf2_F( h, salt, itercount, blocknum ):
- U = prf( h, salt + pack('>i',blocknum ) )
- T = U
- for i in range(2, itercount+1):
- U = prf( h, U )
- T = xorstr( T, U )
- return T
-
- sha = hashlib.sha1
- digest_size = sha().digest_size
- # l - number of output blocks to produce
- l = keylen / digest_size
- if keylen % digest_size != 0:
- l += 1
- h = hmac.new( passwd, None, sha )
- T = ""
- for i in range(1, l+1):
- T += pbkdf2_F( h, salt, iter, i )
- return T[0: keylen]
-
- def UnprotectHeaderData(encryptedData):
- passwdData = 'header_key_data'
- salt = 'HEADER.2011'
- iter = 0x80
- keylen = 0x100
- key_iv = KeyIVGen().pbkdf2(passwdData, salt, iter, keylen)
- key = key_iv[0:32]
- iv = key_iv[32:48]
- aes=AES_CBC()
- aes.set_decrypt_key(key, iv)
- cleartext = aes.decrypt(encryptedData)
- return cleartext
-
- # Various character maps used to decrypt kindle info values.
- # Probably supposed to act as obfuscation
- charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
- charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
- # New maps in K4PC 1.9.0
- testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
- testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
- testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
-
- # interface with Windows OS Routines
- class DataBlob(Structure):
- _fields_ = [('cbData', c_uint),
- ('pbData', c_void_p)]
- DataBlob_p = POINTER(DataBlob)
-
-
- def GetSystemDirectory():
- GetSystemDirectoryW = kernel32.GetSystemDirectoryW
- GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
- GetSystemDirectoryW.restype = c_uint
- def GetSystemDirectory():
- buffer = create_unicode_buffer(MAX_PATH + 1)
- GetSystemDirectoryW(buffer, len(buffer))
- return buffer.value
- return GetSystemDirectory
- GetSystemDirectory = GetSystemDirectory()
-
- def GetVolumeSerialNumber():
- GetVolumeInformationW = kernel32.GetVolumeInformationW
- GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
- POINTER(c_uint), POINTER(c_uint),
- POINTER(c_uint), c_wchar_p, c_uint]
- GetVolumeInformationW.restype = c_uint
- def GetVolumeSerialNumber(path = GetSystemDirectory().split('\\')[0] + '\\'):
- vsn = c_uint(0)
- GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
- return str(vsn.value)
- return GetVolumeSerialNumber
- GetVolumeSerialNumber = GetVolumeSerialNumber()
-
- def GetIDString():
- vsn = GetVolumeSerialNumber()
- #print('Using Volume Serial Number for ID: '+vsn)
- return vsn
-
- def getLastError():
- GetLastError = kernel32.GetLastError
- GetLastError.argtypes = None
- GetLastError.restype = c_uint
- def getLastError():
- return GetLastError()
- return getLastError
- getLastError = getLastError()
-
- def GetUserName():
- GetUserNameW = advapi32.GetUserNameW
- GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
- GetUserNameW.restype = c_uint
- def GetUserName():
- buffer = create_unicode_buffer(2)
- size = c_uint(len(buffer))
- while not GetUserNameW(buffer, byref(size)):
- errcd = getLastError()
- if errcd == 234:
- # bad wine implementation up through wine 1.3.21
- return "AlternateUserName"
- # double the buffer size
- buffer = create_unicode_buffer(len(buffer) * 2)
- size.value = len(buffer)
-
- # replace any non-ASCII values with 0xfffd
- for i in xrange(0,len(buffer)):
- if buffer[i]>u"\u007f":
- #print u"swapping char "+str(i)+" ("+buffer[i]+")"
- buffer[i] = u"\ufffd"
- # return utf-8 encoding of modified username
- #print u"modified username:"+buffer.value
- return buffer.value.encode('utf-8')
- return GetUserName
- GetUserName = GetUserName()
-
- def CryptUnprotectData():
- _CryptUnprotectData = crypt32.CryptUnprotectData
- _CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
- c_void_p, c_void_p, c_uint, DataBlob_p]
- _CryptUnprotectData.restype = c_uint
- def CryptUnprotectData(indata, entropy, flags):
- indatab = create_string_buffer(indata)
- indata = DataBlob(len(indata), cast(indatab, c_void_p))
- entropyb = create_string_buffer(entropy)
- entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
- outdata = DataBlob()
- if not _CryptUnprotectData(byref(indata), None, byref(entropy),
- None, None, flags, byref(outdata)):
- # raise DrmException("Failed to Unprotect Data")
- return 'failed'
- return string_at(outdata.pbData, outdata.cbData)
- return CryptUnprotectData
- CryptUnprotectData = CryptUnprotectData()
-
- # Returns Environmental Variables that contain unicode
- def getEnvironmentVariable(name):
- import ctypes
- name = unicode(name) # make sure string argument is unicode
- n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
- if n == 0:
- return None
- buf = ctypes.create_unicode_buffer(u'\0'*n)
- ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
- return buf.value
-
- # Locate all of the kindle-info style files and return as list
- def getKindleInfoFiles():
- kInfoFiles = []
- # some 64 bit machines do not have the proper registry key for some reason
- # or the python interface to the 32 vs 64 bit registry is broken
- path = ""
- if 'LOCALAPPDATA' in os.environ.keys():
- # Python 2.x does not return unicode env. Use Python 3.x
- path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
- # this is just another alternative.
- # path = getEnvironmentVariable('LOCALAPPDATA')
- if not os.path.isdir(path):
- path = ""
- else:
- # User Shell Folders show take precedent over Shell Folders if present
- try:
- # this will still break
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
- if not os.path.isdir(path):
- path = ""
- try:
- regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
- path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
- if not os.path.isdir(path):
- path = ""
- except RegError:
- pass
- except RegError:
- pass
-
- found = False
- if path == "":
- print ('Could not find the folder in which to look for kinfoFiles.')
- else:
- # Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
- print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore'))
-
- # look for (K4PC 1.9.0 and later) .kinf2011 file
- kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
- if os.path.isfile(kinfopath):
- found = True
- print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath.encode('ascii','ignore'))
- kInfoFiles.append(kinfopath)
-
- # look for (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
- kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
- if os.path.isfile(kinfopath):
- found = True
- print('Found K4PC 1.6-1.8 kinf file: ' + kinfopath)
- kInfoFiles.append(kinfopath)
-
- # look for (K4PC 1.5.0 and later) rainier.2.1.1.kinf file
- kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
- if os.path.isfile(kinfopath):
- found = True
- print('Found K4PC 1.5 kinf file: ' + kinfopath)
- kInfoFiles.append(kinfopath)
-
- # look for original (earlier than K4PC 1.5.0) kindle-info files
- kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
- if os.path.isfile(kinfopath):
- found = True
- print('Found K4PC kindle.info file: ' + kinfopath)
- kInfoFiles.append(kinfopath)
-
- if not found:
- print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
- return kInfoFiles
-
-
- # determine type of kindle info provided and return a
- # database of keynames and values
- def getDBfromFile(kInfoFile):
- names = [\
- 'kindle.account.tokens',\
- 'kindle.cookie.item',\
- 'eulaVersionAccepted',\
- 'login_date',\
- 'kindle.token.item',\
- 'login',\
- 'kindle.key.item',\
- 'kindle.name.info',\
- 'kindle.device.info',\
- 'MazamaRandomNumber',\
- 'max_date',\
- 'SIGVERIF',\
- 'build_version',\
- 'SerialNumber',\
- 'UsernameHash',\
- 'kindle.directedid.info',\
- 'DSN',\
- 'kindle.accounttype.info',\
- 'krx.flashcardsplugin.data.encryption_key',\
- 'krx.notebookexportplugin.data.encryption_key',\
- 'proxy.http.password',\
- 'proxy.http.username'
- ]
- DB = {}
- with open(kInfoFile, 'rb') as infoReader:
- data = infoReader.read()
- # assume newest .kinf2011 style .kinf file
- # the .kinf file uses "/" to separate it into records
- # so remove the trailing "/" to make it easy to use split
- data = data[:-1]
- items = data.split('/')
-
- # starts with an encoded and encrypted header blob
- headerblob = items.pop(0)
- encryptedValue = decode(headerblob, testMap1)
- cleartext = UnprotectHeaderData(encryptedValue)
- #print "header cleartext:",cleartext
- # now extract the pieces that form the added entropy
- pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
- for m in re.finditer(pattern, cleartext):
- added_entropy = m.group(2) + m.group(4)
-
-
- # loop through the item records until all are processed
- while len(items) > 0:
-
- # get the first item record
- item = items.pop(0)
-
- # the first 32 chars of the first record of a group
- # is the MD5 hash of the key name encoded by charMap5
- keyhash = item[0:32]
-
- # the sha1 of raw keyhash string is used to create entropy along
- # with the added entropy provided above from the headerblob
- entropy = SHA1(keyhash) + added_entropy
-
- # the remainder of the first record when decoded with charMap5
- # has the ':' split char followed by the string representation
- # of the number of records that follow
- # and make up the contents
- srcnt = decode(item[34:],charMap5)
- rcnt = int(srcnt)
-
- # read and store in rcnt records of data
- # that make up the contents value
- edlst = []
- for i in xrange(rcnt):
- item = items.pop(0)
- edlst.append(item)
-
- # key names now use the new testMap8 encoding
- keyname = "unknown"
- for name in names:
- if encodeHash(name,testMap8) == keyhash:
- keyname = name
- #print "keyname found from hash:",keyname
- break
- if keyname == "unknown":
- keyname = keyhash
- #print "keyname not found, hash is:",keyname
-
- # the testMap8 encoded contents data has had a length
- # of chars (always odd) cut off of the front and moved
- # to the end to prevent decoding using testMap8 from
- # working properly, and thereby preventing the ensuing
- # CryptUnprotectData call from succeeding.
-
- # The offset into the testMap8 encoded contents seems to be:
- # len(contents)-largest prime number <= int(len(content)/3)
- # (in other words split "about" 2/3rds of the way through)
-
- # move first offsets chars to end to align for decode by testMap8
- # by moving noffset chars from the start of the
- # string to the end of the string
- encdata = "".join(edlst)
- #print "encrypted data:",encdata
- contlen = len(encdata)
- noffset = contlen - primes(int(contlen/3))[-1]
- pfx = encdata[0:noffset]
- encdata = encdata[noffset:]
- encdata = encdata + pfx
- #print "rearranged data:",encdata
-
-
- # decode using new testMap8 to get the original CryptProtect Data
- encryptedValue = decode(encdata,testMap8)
- #print "decoded data:",encryptedValue.encode('hex')
- cleartext = CryptUnprotectData(encryptedValue, entropy, 1)
- if len(cleartext)>0:
- #print "cleartext data:",cleartext,":end data"
- DB[keyname] = cleartext
- #print keyname, cleartext
-
- if len(DB)>6:
- # store values used in decryption
- DB['IDString'] = GetIDString()
- DB['UserName'] = GetUserName()
- print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex'))
- else:
- print u"Couldn't decrypt file."
- DB = {}
- return DB
-elif isosx:
- import copy
- import subprocess
-
- # interface to needed routines in openssl's libcrypto
- def _load_crypto_libcrypto():
- from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
- Structure, c_ulong, create_string_buffer, addressof, string_at, cast
- from ctypes.util import find_library
-
- libcrypto = find_library('crypto')
- if libcrypto is None:
- raise DrmException(u"libcrypto not found")
- libcrypto = CDLL(libcrypto)
-
- # From OpenSSL's crypto aes header
- #
- # AES_ENCRYPT 1
- # AES_DECRYPT 0
- # AES_MAXNR 14 (in bytes)
- # AES_BLOCK_SIZE 16 (in bytes)
- #
- # struct aes_key_st {
- # unsigned long rd_key[4 *(AES_MAXNR + 1)];
- # int rounds;
- # };
- # typedef struct aes_key_st AES_KEY;
- #
- # int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
- #
- # note: the ivec string, and output buffer are both mutable
- # void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
- # const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc);
-
- AES_MAXNR = 14
- c_char_pp = POINTER(c_char_p)
- c_int_p = POINTER(c_int)
-
- class AES_KEY(Structure):
- _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
- AES_KEY_p = POINTER(AES_KEY)
-
- def F(restype, name, argtypes):
- func = getattr(libcrypto, name)
- func.restype = restype
- func.argtypes = argtypes
- return func
-
- AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int])
-
- AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
-
- # From OpenSSL's Crypto evp/p5_crpt2.c
- #
- # int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
- # const unsigned char *salt, int saltlen, int iter,
- # int keylen, unsigned char *out);
-
- PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1',
- [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
-
- class LibCrypto(object):
- def __init__(self):
- self._blocksize = 0
- self._keyctx = None
- self._iv = 0
-
- def set_decrypt_key(self, userkey, iv):
- self._blocksize = len(userkey)
- if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
- raise DrmException(u"AES improper key used")
- return
- keyctx = self._keyctx = AES_KEY()
- self._iv = iv
- self._userkey = userkey
- rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
- if rv < 0:
- raise DrmException(u"Failed to initialize AES key")
-
- def decrypt(self, data):
- out = create_string_buffer(len(data))
- mutable_iv = create_string_buffer(self._iv, len(self._iv))
- keyctx = self._keyctx
- rv = AES_cbc_encrypt(data, out, len(data), keyctx, mutable_iv, 0)
- if rv == 0:
- raise DrmException(u"AES decryption failed")
- return out.raw
-
- def keyivgen(self, passwd, salt, iter, keylen):
- saltlen = len(salt)
- passlen = len(passwd)
- out = create_string_buffer(keylen)
- rv = PKCS5_PBKDF2_HMAC_SHA1(passwd, passlen, salt, saltlen, iter, keylen, out)
- return out.raw
- return LibCrypto
-
- def _load_crypto():
- LibCrypto = None
- try:
- LibCrypto = _load_crypto_libcrypto()
- except (ImportError, DrmException):
- pass
- return LibCrypto
-
- LibCrypto = _load_crypto()
-
- # Various character maps used to decrypt books. Probably supposed to act as obfuscation
- charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
- charMap2 = 'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM'
-
- # For kinf approach of K4Mac 1.6.X or later
- # On K4PC charMap5 = 'AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE'
- # For Mac they seem to re-use charMap2 here
- charMap5 = charMap2
-
- # new in K4M 1.9.X
- testMap8 = 'YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD'
-
- # uses a sub process to get the Hard Drive Serial Number using ioreg
- # returns serial numbers of all internal hard drive drives
- def GetVolumesSerialNumbers():
- sernums = []
- sernum = os.getenv('MYSERIALNUMBER')
- if sernum != None:
- sernums.append(sernum.strip())
- cmdline = '/usr/sbin/ioreg -w 0 -r -c AppleAHCIDiskDriver'
- cmdline = cmdline.encode(sys.getfilesystemencoding())
- p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
- out1, out2 = p.communicate()
- #print out1
- reslst = out1.split('\n')
- cnt = len(reslst)
- for j in xrange(cnt):
- resline = reslst[j]
- pp = resline.find('\"Serial Number\" = \"')
- if pp >= 0:
- sernum = resline[pp+19:-1]
- sernums.append(sernum.strip())
- return sernums
-
- def GetDiskPartitionNames():
- names = []
- cmdline = '/sbin/mount'
- cmdline = cmdline.encode(sys.getfilesystemencoding())
- p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
- out1, out2 = p.communicate()
- reslst = out1.split('\n')
- cnt = len(reslst)
- for j in xrange(cnt):
- resline = reslst[j]
- if resline.startswith('/dev'):
- (devpart, mpath) = resline.split(' on ')[:2]
- dpart = devpart[5:]
- names.append(dpart)
- return names
-
- # uses a sub process to get the UUID of all disk partitions
- def GetDiskPartitionUUIDs():
- uuids = []
- uuidnum = os.getenv('MYUUIDNUMBER')
- if uuidnum != None:
- uuids.append(strip(uuidnum))
- cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
- cmdline = cmdline.encode(sys.getfilesystemencoding())
- p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
- out1, out2 = p.communicate()
- #print out1
- reslst = out1.split('\n')
- cnt = len(reslst)
- for j in xrange(cnt):
- resline = reslst[j]
- pp = resline.find('\"UUID\" = \"')
- if pp >= 0:
- uuidnum = resline[pp+10:-1]
- uuidnum = uuidnum.strip()
- uuids.append(uuidnum)
- return uuids
-
- def GetMACAddressesMunged():
- macnums = []
- macnum = os.getenv('MYMACNUM')
- if macnum != None:
- macnums.append(macnum)
- cmdline = 'networksetup -listallhardwareports' # en0'
- cmdline = cmdline.encode(sys.getfilesystemencoding())
- p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
- out1, out2 = p.communicate()
- reslst = out1.split('\n')
- cnt = len(reslst)
- for j in xrange(cnt):
- resline = reslst[j]
- pp = resline.find('Ethernet Address: ')
- if pp >= 0:
- #print resline
- macnum = resline[pp+18:]
- macnum = macnum.strip()
- maclst = macnum.split(':')
- n = len(maclst)
- if n != 6:
- continue
- #print 'original mac', macnum
- # now munge it up the way Kindle app does
- # by xoring it with 0xa5 and swapping elements 3 and 4
- for i in range(6):
- maclst[i] = int('0x' + maclst[i], 0)
- mlst = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mlst[5] = maclst[5] ^ 0xa5
- mlst[4] = maclst[3] ^ 0xa5
- mlst[3] = maclst[4] ^ 0xa5
- mlst[2] = maclst[2] ^ 0xa5
- mlst[1] = maclst[1] ^ 0xa5
- mlst[0] = maclst[0] ^ 0xa5
- macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
- #print 'munged mac', macnum
- macnums.append(macnum)
- return macnums
-
-
- # uses unix env to get username instead of using sysctlbyname
- def GetUserName():
- username = os.getenv('USER')
- #print "Username:",username
- return username
-
- def GetIDStrings():
- # Return all possible ID Strings
- strings = []
- strings.extend(GetMACAddressesMunged())
- strings.extend(GetVolumesSerialNumbers())
- strings.extend(GetDiskPartitionNames())
- strings.extend(GetDiskPartitionUUIDs())
- strings.append('9999999999')
- #print "ID Strings:\n",strings
- return strings
-
-
- # unprotect the new header blob in .kinf2011
- # used in Kindle for Mac Version >= 1.9.0
- def UnprotectHeaderData(encryptedData):
- passwdData = 'header_key_data'
- salt = 'HEADER.2011'
- iter = 0x80
- keylen = 0x100
- crp = LibCrypto()
- key_iv = crp.keyivgen(passwdData, salt, iter, keylen)
- key = key_iv[0:32]
- iv = key_iv[32:48]
- crp.set_decrypt_key(key,iv)
- cleartext = crp.decrypt(encryptedData)
- return cleartext
-
-
- # implements an Pseudo Mac Version of Windows built-in Crypto routine
- class CryptUnprotectData(object):
- def __init__(self, entropy, IDString):
- sp = GetUserName() + '+@#$%+' + IDString
- passwdData = encode(SHA256(sp),charMap2)
- salt = entropy
- self.crp = LibCrypto()
- iter = 0x800
- keylen = 0x400
- key_iv = self.crp.keyivgen(passwdData, salt, iter, keylen)
- self.key = key_iv[0:32]
- self.iv = key_iv[32:48]
- self.crp.set_decrypt_key(self.key, self.iv)
-
- def decrypt(self, encryptedData):
- cleartext = self.crp.decrypt(encryptedData)
- cleartext = decode(cleartext, charMap2)
- return cleartext
-
-
- # Locate the .kindle-info files
- def getKindleInfoFiles():
- # file searches can take a long time on some systems, so just look in known specific places.
- kInfoFiles=[]
- found = False
- home = os.getenv('HOME')
- # check for .kinf2011 file in new location (App Store Kindle for Mac)
- testpath = home + '/Library/Containers/com.amazon.Kindle/Data/Library/Application Support/Kindle/storage/.kinf2011'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac kinf2011 file: ' + testpath)
- found = True
- # check for .kinf2011 files from 1.10
- testpath = home + '/Library/Application Support/Kindle/storage/.kinf2011'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac kinf2011 file: ' + testpath)
- found = True
- # check for .rainier-2.1.1-kinf files from 1.6
- testpath = home + '/Library/Application Support/Kindle/storage/.rainier-2.1.1-kinf'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac rainier file: ' + testpath)
- found = True
- # check for .kindle-info files from 1.4
- testpath = home + '/Library/Application Support/Kindle/storage/.kindle-info'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac kindle-info file: ' + testpath)
- found = True
- # check for .kindle-info file from 1.2.2
- testpath = home + '/Library/Application Support/Amazon/Kindle/storage/.kindle-info'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac kindle-info file: ' + testpath)
- found = True
- # check for .kindle-info file from 1.0 beta 1 (27214)
- testpath = home + '/Library/Application Support/Amazon/Kindle for Mac/storage/.kindle-info'
- if os.path.isfile(testpath):
- kInfoFiles.append(testpath)
- print('Found k4Mac kindle-info file: ' + testpath)
- found = True
- if not found:
- print('No k4Mac kindle-info/rainier/kinf2011 files have been found.')
- return kInfoFiles
-
- # determine type of kindle info provided and return a
- # database of keynames and values
- def getDBfromFile(kInfoFile):
- names = [\
- 'kindle.account.tokens',\
- 'kindle.cookie.item',\
- 'eulaVersionAccepted',\
- 'login_date',\
- 'kindle.token.item',\
- 'login',\
- 'kindle.key.item',\
- 'kindle.name.info',\
- 'kindle.device.info',\
- 'MazamaRandomNumber',\
- 'max_date',\
- 'SIGVERIF',\
- 'build_version',\
- 'SerialNumber',\
- 'UsernameHash',\
- 'kindle.directedid.info',\
- 'DSN'
- ]
- with open(kInfoFile, 'rb') as infoReader:
- filedata = infoReader.read()
-
- data = filedata[:-1]
- items = data.split('/')
- IDStrings = GetIDStrings()
- for IDString in IDStrings:
- #print "trying IDString:",IDString
- try:
- DB = {}
- items = data.split('/')
-
- # the headerblob is the encrypted information needed to build the entropy string
- headerblob = items.pop(0)
- encryptedValue = decode(headerblob, charMap1)
- cleartext = UnprotectHeaderData(encryptedValue)
-
- # now extract the pieces in the same way
- # this version is different from K4PC it scales the build number by multipying by 735
- pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
- for m in re.finditer(pattern, cleartext):
- entropy = str(int(m.group(2)) * 0x2df) + m.group(4)
-
- cud = CryptUnprotectData(entropy,IDString)
-
- # loop through the item records until all are processed
- while len(items) > 0:
-
- # get the first item record
- item = items.pop(0)
-
- # the first 32 chars of the first record of a group
- # is the MD5 hash of the key name encoded by charMap5
- keyhash = item[0:32]
- keyname = 'unknown'
-
- # unlike K4PC the keyhash is not used in generating entropy
- # entropy = SHA1(keyhash) + added_entropy
- # entropy = added_entropy
-
- # the remainder of the first record when decoded with charMap5
- # has the ':' split char followed by the string representation
- # of the number of records that follow
- # and make up the contents
- srcnt = decode(item[34:],charMap5)
- rcnt = int(srcnt)
-
- # read and store in rcnt records of data
- # that make up the contents value
- edlst = []
- for i in xrange(rcnt):
- item = items.pop(0)
- edlst.append(item)
-
- keyname = 'unknown'
- for name in names:
- if encodeHash(name,testMap8) == keyhash:
- keyname = name
- break
- if keyname == 'unknown':
- keyname = keyhash
-
- # the testMap8 encoded contents data has had a length
- # of chars (always odd) cut off of the front and moved
- # to the end to prevent decoding using testMap8 from
- # working properly, and thereby preventing the ensuing
- # CryptUnprotectData call from succeeding.
-
- # The offset into the testMap8 encoded contents seems to be:
- # len(contents) - largest prime number less than or equal to int(len(content)/3)
- # (in other words split 'about' 2/3rds of the way through)
-
- # move first offsets chars to end to align for decode by testMap8
- encdata = ''.join(edlst)
- contlen = len(encdata)
-
- # now properly split and recombine
- # by moving noffset chars from the start of the
- # string to the end of the string
- noffset = contlen - primes(int(contlen/3))[-1]
- pfx = encdata[0:noffset]
- encdata = encdata[noffset:]
- encdata = encdata + pfx
-
- # decode using testMap8 to get the CryptProtect Data
- encryptedValue = decode(encdata,testMap8)
- cleartext = cud.decrypt(encryptedValue)
- # print keyname
- # print cleartext
- if len(cleartext) > 0:
- DB[keyname] = cleartext
-
- if len(DB)>6:
- break
- except:
- pass
- if len(DB)>6:
- # store values used in decryption
- print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName())
- DB['IDString'] = IDString
- DB['UserName'] = GetUserName()
- else:
- print u"Couldn't decrypt file."
- DB = {}
- return DB
-else:
- def getDBfromFile(kInfoFile):
- raise DrmException(u"This script only runs under Windows or Mac OS X.")
- return {}
-
-def kindlekeys(files = []):
- keys = []
- if files == []:
- files = getKindleInfoFiles()
- for file in files:
- key = getDBfromFile(file)
- if key:
- # convert all values to hex, just in case.
- for keyname in key:
- key[keyname]=key[keyname].encode('hex')
- keys.append(key)
- return keys
-
-# interface for Python DeDRM
-# returns single key or multiple keys, depending on path or file passed in
-def getkey(outpath, files=[]):
- keys = kindlekeys(files)
- if len(keys) > 0:
- if not os.path.isdir(outpath):
- outfile = outpath
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(json.dumps(keys[0]))
- print u"Saved a key to {0}".format(outfile)
- else:
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(outpath,u"kindlekey{0:d}.k4i".format(keycount))
- if not os.path.exists(outfile):
- break
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(json.dumps(key))
- print u"Saved a key to {0}".format(outfile)
- return True
- return False
-
-def usage(progname):
- print u"Finds, decrypts and saves the default Kindle For Mac/PC encryption keys."
- print u"Keys are saved to the current directory, or a specified output directory."
- print u"If a file name is passed instead of a directory, only the first key is saved, in that file."
- print u"Usage:"
- print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__)
-
- try:
- opts, args = getopt.getopt(argv[1:], "hk:")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
- usage(progname)
- sys.exit(2)
-
- files = []
- for o, a in opts:
- if o == "-h":
- usage(progname)
- sys.exit(0)
- if o == "-k":
- files = [a]
-
- if len(args) > 1:
- usage(progname)
- sys.exit(2)
-
- if len(args) == 1:
- # save to the specified file or directory
- outpath = args[0]
- if not os.path.isabs(outpath):
- outpath = os.path.abspath(outpath)
- else:
- # save to the same directory as the script
- outpath = os.path.dirname(argv[0])
-
- # make sure the outpath is canonical
- outpath = os.path.realpath(os.path.normpath(outpath))
-
- if not getkey(outpath, files):
- print u"Could not retrieve Kindle for Mac/PC key."
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- import traceback
- except:
- return cli_main()
-
- class ExceptionDialog(Tkinter.Frame):
- def __init__(self, root, text):
- Tkinter.Frame.__init__(self, root, border=5)
- label = Tkinter.Label(self, text=u"Unexpected error:",
- anchor=Tkconstants.W, justify=Tkconstants.LEFT)
- label.pack(fill=Tkconstants.X, expand=0)
- self.text = Tkinter.Text(self)
- self.text.pack(fill=Tkconstants.BOTH, expand=1)
-
- self.text.insert(Tkconstants.END, text)
-
-
- argv=unicode_argv()
- root = Tkinter.Tk()
- root.withdraw()
- progpath, progname = os.path.split(argv[0])
- success = False
- try:
- keys = kindlekeys()
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(progpath,u"kindlekey{0:d}.k4i".format(keycount))
- if not os.path.exists(outfile):
- break
-
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(json.dumps(key))
- success = True
- tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
- except DrmException, e:
- tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
- except Exception:
- root.wm_state('normal')
- root.title(progname)
- text = traceback.format_exc()
- ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
- root.mainloop()
- if not success:
- return 1
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw
deleted file mode 100644
index 0c0b11b..0000000
--- a/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-# kindleforios4key.py
-# Copyright © 2013 by Apprentice Alf
-# Portions Copyright © 2007, 2009 Igor Skochinsky <[email protected]>
-
-# Revision history:
-# 1.0 - Generates fixed PID for Kindle for iOS 3.1.1 running on iOS 4.x
-
-
-"""
-Generate fixed PID for Kindle for iOS 3.1.1
-"""
-
-__license__ = 'GPL v3'
-__version__ = '1.0'
-
-import sys, os
-import getopt
-import binascii
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-iswindows = sys.platform.startswith('win')
-isosx = sys.platform.startswith('darwin')
-
-def unicode_argv():
- if iswindows:
- # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
- # strings.
-
- # Versions 2.x of Python don't support Unicode in sys.argv on
- # Windows, with the underlying Windows API instead replacing multi-byte
- # characters with '?'.
-
-
- from ctypes import POINTER, byref, cdll, c_int, windll
- from ctypes.wintypes import LPCWSTR, LPWSTR
-
- GetCommandLineW = cdll.kernel32.GetCommandLineW
- GetCommandLineW.argtypes = []
- GetCommandLineW.restype = LPCWSTR
-
- CommandLineToArgvW = windll.shell32.CommandLineToArgvW
- CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
- CommandLineToArgvW.restype = POINTER(LPWSTR)
-
- cmd = GetCommandLineW()
- argc = c_int(0)
- argv = CommandLineToArgvW(cmd, byref(argc))
- if argc.value > 0:
- # Remove Python executable and commands if present
- start = argc.value - len(sys.argv)
- return [argv[i] for i in
- xrange(start, argc.value)]
- # if we don't have any arguments at all, just pass back script name
- # this should never happen
- return [u"mobidedrm.py"]
- else:
- argvencoding = sys.stdin.encoding
- if argvencoding == None:
- argvencoding = "utf-8"
- return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
-
-import hashlib
-
-def SHA256(message):
- ctx = hashlib.sha256()
- ctx.update(message)
- return ctx.digest()
-
-def crc32(s):
- return (~binascii.crc32(s,-1))&0xFFFFFFFF
-
-letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
-
-def checksumPid(s):
- crc = crc32(s)
- crc = crc ^ (crc >> 16)
- res = s
- l = len(letters)
- for i in (0,1):
- b = crc & 0xff
- pos = (b // l) ^ (b % l)
- res += letters[pos%l]
- crc >>= 8
-
- return res
-
-def pidFromSerial(s, l):
- crc = crc32(s)
-
- arr1 = [0]*l
- for i in xrange(len(s)):
- arr1[i%l] ^= ord(s[i])
-
- crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
- for i in xrange(l):
- arr1[i] ^= crc_bytes[i&3]
-
- pid = ''
- for i in xrange(l):
- b = arr1[i] & 0xff
- pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
-
- return pid
-
-def generatekeys(email, mac):
- keys = []
- email = email.encode('utf-8').lower()
- mac = mac.encode('utf-8').lower()
- cleanmac = "".join(c if (c in "0123456789abcdef") else "" for c in mac)
- lowermac = cleanmac.lower()
- #print lowermac
- keyseed = lowermac + email.encode('utf-8')
- #print keyseed
- keysha256 = SHA256(keyseed)
- keybase64 = keysha256.encode('base64')
- #print keybase64
- cleankeybase64 = "".join(c if (c in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") else "0" for c in keybase64)
- #print cleankeybase64
- pseudoudid = cleankeybase64[:40]
- #print pseudoudid
- keys.append(pidFromSerial(pseudoudid.encode("utf-8"),8))
- return keys
-
-# interface for Python DeDRM
-# returns single key or multiple keys, depending on path or file passed in
-def getkey(email, mac, outpath):
- keys = generatekeys(email,mac)
- if len(keys) > 0:
- if not os.path.isdir(outpath):
- outfile = outpath
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(keys[0])
- print u"Saved a key to {0}".format(outfile)
- else:
- keycount = 0
- for key in keys:
- while True:
- keycount += 1
- outfile = os.path.join(outpath,u"kindleios{0:d}.pid".format(keycount))
- if not os.path.exists(outfile):
- break
- with file(outfile, 'w') as keyfileout:
- keyfileout.write(key)
- print u"Saved a key to {0}".format(outfile)
- return True
- return False
-
-def usage(progname):
- print u"Generates the key for Kindle for iOS 3.1.1"
- print u"Requires email address of Amazon acccount"
- print u"And MAC address for iOS device’s wifi"
- print u"Outputs to a file or to stdout"
- print u"Usage:"
- print u" {0:s} [-h] <email address> <MAC address> [<outfile>]".format(progname)
-
-
-def cli_main():
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- argv=unicode_argv()
- progname = os.path.basename(argv[0])
- print u"{0} v{1}\nCopyright © 2013 Apprentice Alf".format(progname,__version__)
-
- try:
- opts, args = getopt.getopt(argv[1:], "h")
- except getopt.GetoptError, err:
- print u"Error in options or arguments: {0}".format(err.args[0])
- usage(progname)
- sys.exit(2)
-
- for o, a in opts:
- if o == "-h":
- usage(progname)
- sys.exit(0)
-
-
- if len(args) < 2 or len(args) > 3:
- usage(progname)
- sys.exit(2)
-
- if len(args) == 3:
- # save to the specified file or folder
- getkey(args[0],args[1],args[2])
- else:
- keys = generatekeys(args[0],args[1])
- for key in keys:
- print key
-
- return 0
-
-
-def gui_main():
- try:
- import Tkinter
- import Tkconstants
- import tkMessageBox
- except:
- print "Tkinter not installed"
- return cli_main()
-
- class DecryptionDialog(Tkinter.Frame):
- def __init__(self, root):
- Tkinter.Frame.__init__(self, root, border=5)
- self.status = Tkinter.Label(self, text=u"Enter parameters")
- self.status.pack(fill=Tkconstants.X, expand=1)
- body = Tkinter.Frame(self)
- body.pack(fill=Tkconstants.X, expand=1)
- sticky = Tkconstants.E + Tkconstants.W
- body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text=u"Amazon email address").grid(row=0)
- self.email = Tkinter.Entry(body, width=40)
- self.email.grid(row=0, column=1, sticky=sticky)
- Tkinter.Label(body, text=u"iOS MAC address").grid(row=1)
- self.mac = Tkinter.Entry(body, width=40)
- self.mac.grid(row=1, column=1, sticky=sticky)
- buttons = Tkinter.Frame(self)
- buttons.pack()
- button = Tkinter.Button(
- buttons, text=u"Generate", width=10, command=self.generate)
- button.pack(side=Tkconstants.LEFT)
- Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
- button = Tkinter.Button(
- buttons, text=u"Quit", width=10, command=self.quit)
- button.pack(side=Tkconstants.RIGHT)
-
- def generate(self):
- email = self.email.get()
- mac = self.mac.get()
- if not email:
- self.status['text'] = u"Email not specified"
- return
- if not mac:
- self.status['text'] = u"MAC not specified"
- return
- self.status['text'] = u"Generating..."
- try:
- keys = generatekeys(email, mac)
- except Exception, e:
- self.status['text'] = u"Error: (0}".format(e.args[0])
- return
- self.status['text'] = ", ".join(key for key in keys)
-
- root = Tkinter.Tk()
- root.title(u"Kindle for iOS PID Generator v.{0}".format(__version__))
- root.resizable(True, False)
- root.minsize(300, 0)
- DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
- root.mainloop()
- return 0
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/Kindle_for_Android_Patches/A_Patching_Experience.txt b/Other_Tools/Kindle_for_Android_Patches/A_Patching_Experience.txt
deleted file mode 100644
index 0c24f0d..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/A_Patching_Experience.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-Of Historical Interest Only
-===========================
-
-It is now much simpler and easier to get a backup.ab file from your Android device and import that into the tools.
-
-
-
-
-
-
-
-Comment at Apprentice Alf's Blog by cestmoicestmoi, 21st December, 2012.
-========================================================================
-
-just to share my experience in patching the kindle.apk
-
-first thanks to all of you guys for this great site & tools, keep going
-
-following instructions in latest readme.txt
-successfully patched Android Kindle 3.7.0.108, Android 2.3.4 Xperia Arc unrooted;
-key painful snags encountered in my case (+ had never coded java/built any apk! .apk = app on Android) (just sums up here the tip of the iceberg):
-on phone :
-to get the app kindle.apk (invisible in unrooted phones) :
-use MyAppShare (free), but this required old Kindle.apk version (for patching) won’t probably be in your phone anymore (latest today 3.8.1.11, with new updates almost everyday !)
-anyway in any-case turnoff the Play-Store auto update
-otherwise/then look for a 3.7.0.108 on the web (or try patch a newer one ?)
-reminder: .apk are just containers = zip files… to check just add an extra .zip and unzip kindle.apk, but don’t play with that… patching wouldn’t work; however all names of kindle.apk can be completely freely changed : kindle2patchedJojo.apk, com.amazon.kindle.apk, etc.
-on your PC (XP):
-rather long & tricky expert process for any non-android-app maker
-(basics is to use the cmd.exe very old DOS style command line with the tricky path and directories changes, forward and back slash etc etc !)
-pb to obtain all the necessary .exe tools (then you call them as a ‘command’ in cdm.exe)
-to make things easier, put copy of everything in same folder anywhere:
-kindle.apk, cmd.exe (from XP), apktool.jar+apktool.bat+aapt.exe (from http://code.google.com/p/android-apktool/), patch.exe (from http://gnuwin32.sourceforge.net/packages/patch.htm), keytool.exe+jarsign.exe(from http://www.oracle.com/technetwork/java/javase/downloads/ download JDK+JRE same recent version, in my case v6038; start with keytool then jarsign; keep these .exe in their java/…/bin files, make shortcuts, copy them in your “work” folder, then do the path thing in cmd.exe in just copying the paths in the properties of the shortcuts)
-then, key points:
-download/use apktool.jar v143 not v150 (version seen in calling from cdm.exe)
-do not “apktool if…” (frameworks)
-besides all cmd.exe commands given in the readme.txt (read both .txt the old and the newer, even if same name) are perfectly ok (except in keytool: a typo: -valkeidity is -validity; and -genkey preferably -genkeypair)
-(for the tricky keytool then jarsign stuff better read for ex. https://www.owasp.org/index.php/Signing_jar_files_with_jarsigner)
-(almost1) finally move your patched & signed new kindle.apk anywhere to phone sd card, and then from within android just like possibly for any .apk move to it with any file manager and click it, it installs by itself (mine didn’t want to install over the non-patched kindle app, so I had to desinstall the latter 1st from within the settings, but it’s probably because my phone ram was full !)
-(almost2) finally you get your famous PIDs at the bottom of the info in the kindle.apk: don’t worry, in my test case (just 1 book yet), there were 11 PIDs, but 5 were redundant, and the last is a weird large neg number -obviously not a 10 max char. PID, so forget it-).
-then thanks to Android 2.3.4 now you can screenshot (press power button then back button), then from my PC i got this screen pict with all these PIDs, which I even OCRised (stupidly without checking… so I mixed up some “O” and “0″ etc !),
-(almost3) finally then I implemented these PIDs in deDRM v541, …and it didn’t work, long error message, but, finally, at last, worked in deDRM v531 !
-
-conclusion:
-the idea and work behind this patch is brilliant… but I have the bad feeling that most of this/my rather crazy work above to do myself the whole patching thing was more for glory, like climbing some kind of Everest without any training, than anything else…
-must precise that I have only an old mac-ppc + this Android phone; ppc has no kindle.app available*; ppc has no java 1.6 so no possible apktool etc. (nor eclipse Android plugin etc.) (possible with very old versions ?), luckily I had an old version of VPC(virtualpc)/XP (extremely slow!), so I could try all this java stuff at least in a normal XP window/virtual machine, on my mac-ppc; i didn’t try any XP-Android link through USB or else, had only my usual Mac-Android wifi ftp (great direct drag’n drop with cyberduck).
-But when i see all these/my efforts described above for a non-specialist (like 1 week work vs. 1-2 hours for you apk developers !), I wonder if this patched apk shouldn’t/couldn’t be made directly available for most other average joes, no ? just like deDRM ? (or maybe i don’t see the problems here ?);
-besides, i didn’t check, but i have the strong feeling that in using kindle directly on XP to buy my books, this great app deDRM would have found directly these PIDs or other keys automatically (as it does for me on my mac-ppc for some ebook purchased and downloaded directly from other vendors; i.e. “stupid me”… on XP, the deDRM app would have worked in 1 sec… vs. 1 week work above for patching Android, + the crazy procedure just to get these pids, screenshot + ocr + etc. ! well, it should be much faster now, of course !).
-
-*reminder: there is a solution for a mac-ppc only guy who want to buy & read Kindle books for very cheap, and accept to read them online in a browser : little advertised “Kindle cloud reader” which used to work even with rather old Safari versions (didn’t check recently if it still works)… (looks a lot like the ‘Google play’ book reader) texts are very little protected/not encrypted there apparently (journalists even said at launch 2-3 years ago that Amazon was abandoning DRMs, just like Apple did with music a while ago) : texts can more or less easily be copied piece by piece/ by page with a few astute clicks (same for Google; but formatting is gone; and who want to recopy a 300 p. book page by page !); tried to reverse engineer a bit for fun this browser reader to see where and how the text was stored, etc. but (as mentioned I think by another in another comment) the text is downloaded only by pieces, not all at once (don’t remember what if browser turned “offline” ?), and stored here and there, in caches… maybe there are tools for capturing auto these books sent to browsers ? didn’t check ? (not for my mac-ppc at the time in any case; but possibly Windows).
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/ReadMe_K4Android.txt b/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/ReadMe_K4Android.txt
deleted file mode 100644
index c4bda17..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/ReadMe_K4Android.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-Kindle for Android
-------------------
-
-Kindle for Android uses a different scheme to generate its books specific PIDs than Kindle for PC, Kindle for iPhone/iPad, and standalone Kindles.
-
-Unfortunately, K4Android uses an "account secrets" file that would only be available if the device were jail-broken and even then someone would have to figure out how to decode this secret information in order to reverse the process.
-
-Instead of trying to calculate the correct PIDs for each book from this primary data, "Me" (a commenter who posted to the ApprenticeAlf site) came up with a wonderful idea to simply modify the Kindle 3 for Android application to store the PIDs it uses to decode each book in its "about activity" window. This list of PIDS can then be provided to MobiDeDRM.py, which in its latest incarnations allows a comma separated list of pids to be passed in, to successfully remove the DRM from that book. Effectively "Me" has created an "Unswindle" for the Kindle for Android 3 application!
-
-Obviously, to use "Me"'s approach, requires an Android Developer's Certificate (to sign the modified application) and access to and knowledge of the developer tools, but does not require anything to be jail-broken.
-
-This is a copy the detailed instructions supplied by "Me" to the ApprenticeAlf blog in the comments. The kindle3.patch described below is included in this folder in the tools:
-
-From the ApprenticeAlf Comments:
-
-"Me" writes:
-
-A better solution seems to create a patched version of the Kindle apk which either logs or displays it’s PID. I created a patch to both log the pid list and show it in the Kindle application in the about activity screen. The pid list isn’t available until the DRMed book has been opened (and the list seem to differ for different books).
-
-To create the patched kindle apk a certificate must be created (http://developer.android.com/guide/publishing/app-signing.html#cert) and the apktool must be build from source (all subprojects) as long as version 1.4.2 isn’t released (http://code.google.com/p/android-apktool/wiki/BuildApktool).
-
-These are the steps to pull the original apk from the Android device, uninstall it, create a patched apk and install that (tested on a rooted device, but I think all steps should also work on non-rooted devices):
-
-adb pull /data/app/com.amazon.kindle-1.apk kindle3.apk
-adb uninstall com.amazon.kindle
-apktool d kindle3.apk kindle3
-cd kindle3
-patch -p1 < ../kindle3.patch
-cd ..
-apktool b kindle3 kindle3_patched.apk
-jarsigner -verbose -keystore kindle.keystore kindle3_patched.apk kindle
-zipalign -v 4 kindle3_patched.apk kindle3_signed.apk
-adb install kindle3_signed.apk
-
-kindle3.patch (based on kindle version 3.0.1.70) is available on pastebin:
-http://pastebin.com/LNpgkcpP
-
-Have fun!
-
-Comment by me — June 9, 2011 @ 9:01 pm | Reply
-
-Hi me,
-Wow! Great work!!!!
-
-With your patch, you have created the equivalent of Unswindle for the Kindle for Android app and it does not even require jailbreaking!
-
-Very nice work indeed!
-
-Comment by some_updates — June 10, 2011 @ 4:28 am | Reply
-
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/kindle3.0.1.70.patch b/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/kindle3.0.1.70.patch
deleted file mode 100644
index 09d2e27..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/kindle3.0.1.70.patch
+++ /dev/null
@@ -1,100 +0,0 @@
-diff -ru kindle3_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle3/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
---- kindle3_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-+++ kindle3/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-@@ -11,6 +11,8 @@
-
- .field private security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
-+.field private pidList:Ljava/lang/String;
-+
-
- # direct methods
- .method public constructor <init>(Lcom/mobipocket/android/library/reader/AndroidSecurity;Lcom/amazon/kcp/application/AndroidDeviceType;)V
-@@ -28,6 +30,10 @@
- .line 26
- iput-object p2, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->deviceType:Lcom/amazon/kcp/application/AndroidDeviceType;
-
-+ const-string v0, "Open DRMed book to show PID list."
-+
-+ iput-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
- .line 27
- new-instance v0, Ljava/lang/StringBuilder;
-
-@@ -175,4 +181,26 @@
- move-result-object v0
-
- return-object v0
-+.end method
-+
-+.method public getPidList()Ljava/lang/String;
-+ .locals 1
-+
-+ .prologue
-+ .line 15
-+ iget-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ return-object v0
-+.end method
-+
-+.method public setPidList(Ljava/lang/String;)V
-+ .locals 0
-+ .parameter "value"
-+
-+ .prologue
-+ .line 11
-+ iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ .line 12
-+ return-void
- .end method
-diff -ru kindle3_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali kindle3/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
---- kindle3_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-+++ kindle3/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-@@ -27,3 +27,9 @@
-
- .method public abstract getPid()Ljava/lang/String;
- .end method
-+
-+.method public abstract getPidList()Ljava/lang/String;
-+.end method
-+
-+.method public abstract setPidList(Ljava/lang/String;)V
-+.end method
-\ No newline at end of file
-diff -ru kindle3_orig/smali/com/amazon/kcp/info/AboutActivity.smali kindle3/smali/com/amazon/kcp/info/AboutActivity.smali
---- kindle3_orig/smali/com/amazon/kcp/info/AboutActivity.smali
-+++ kindle3/smali/com/amazon/kcp/info/AboutActivity.smali
-@@ -32,9 +32,11 @@
- invoke-direct {v6, v1}, Ljava/util/ArrayList;-><init>(I)V
-
- .line 36
-- const v1, 0x7f0b0005
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-
-- invoke-virtual {p0, v1}, Lcom/amazon/kcp/info/AboutActivity;->getString(I)Ljava/lang/String;
-+ move-result-object v0
-+
-+ invoke-interface {v0}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->getPidList()Ljava/lang/String;
-
- move-result-object v1
-
-diff -ru kindle3_orig/smali/com/amazon/system/security/Security.smali kindle3/smali/com/amazon/system/security/Security.smali
---- kindle3_orig/smali/com/amazon/system/security/Security.smali
-+++ kindle3/smali/com/amazon/system/security/Security.smali
-@@ -884,6 +884,15 @@
-
- .line 332
- :cond_1
-+
-+ const-string v1, "PID list"
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+ move-result-object v0
-+ invoke-static {v7}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
-+ move-result-object v2
-+ invoke-interface {v0, v2}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->setPidList(Ljava/lang/String;)V
-+ invoke-static {v1, v2}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
-+
- return-object v7
-
- :cond_2
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/ReadMe_K4Android.txt b/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/ReadMe_K4Android.txt
deleted file mode 100644
index 808bf51..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/ReadMe_K4Android.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-Kindle for Android
-------------------
-
-Kindle for Android uses a different scheme to generate its books specific PIDs than Kindle for PC, Kindle for iPhone/iPad, and standalone Kindles.
-
-Unfortunately, K4Android uses an "account secrets" file that would only be available if the device were jail-broken and even then someone would have to figure out how to decode this secret information in order to reverse the process.
-
-Instead of trying to calculate the correct PIDs for each book from this primary data, "Me" (a commenter who posted to the ApprenticeAlf site) came up with a wonderful idea to simply modify the Kindle 3 for Android application to store the PIDs it uses to decode each book in its "about activity" window. This list of PIDS can then be provided to MobiDeDRM.py, which in its latest incarnations allows a comma separated list of pids to be passed in, to successfully remove the DRM from that book. Effectively "Me" has created an "Unswindle" for the Kindle for Android 3 application!
-
-"Me"'s original patch was for Kindle for Android version 3.0.1.70. Now "Me II" has created a patch for Kindle for Android version 3.7.0.108 and new instructions, since some of the previous steps are no longer necessary.
-
-From the ApprenticeAlf Comments:
-
-
-"Me II" writes:
-
-Since “Me”‘s old method for getting PIDs from Kindle for Android is outdated and no longer works with newer versions of the app, I decided I’d take a stab at bringing it up to date. It took a little fiddling to get everything working, considering how much has changed since the last patch, but I managed to do it. The process is pretty much identical to “Me”‘s original instructions, with a few minor changes.
-
-1) You don’t need to build apktool from source. You can just grab the binaries from here for whatever OS you’re running: http://code.google.com/p/android-apktool/
-2) When you sign the rebuilt APK, use the following command instead of the one in the instructions:
-jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore kindle.keystore kindle3_patched.apk kindle
-3) It no longer logs the PIDs, only displays them within the app.
-
-You can get the new patch, for version 3.7.0.108, here: http://pastebin.com/6FN2cTSN
-
-And here’s a screenshot of the updated menu: http://imgur.com/BbFVF (sorry for the Japanese, I was too lazy to change my phone’s language).
-
-Subsequently, "s" wrote:
-
-For others it would be useful to add the keystore generation command into the help file:
-keytool -genkey -v -keystore kindle.keystore -alias kindle -keyalg RSA -keysize 2048 -validity 10000
-As well as location of prc’s on android being (with sdcard):
-/mnt/sdcard/Android/data/com.amazon.kindle/files/
-
-"s" also reported success with using the patch on version 3.7.1.8, although I recommend using the 3.7.0.108 version just in case.
-
-
-"Me"'s original instructions, from the ApprenticeAlf Comments:
-
-"Me" writes:
-
-A better solution seems to create a patched version of the Kindle apk which either logs or displays it’s PID. I created a patch to both log the pid list and show it in the Kindle application in the about activity screen. The pid list isn’t available until the DRMed book has been opened (and the list seem to differ for different books).
-
-To create the patched kindle apk a certificate must be created (http://developer.android.com/guide/publishing/app-signing.html#cert) and the apktool must be build from source (all subprojects) as long as version 1.4.2 isn’t released (http://code.google.com/p/android-apktool/wiki/BuildApktool).
-
-These are the steps to pull the original apk from the Android device, uninstall it, create a patched apk and install that (tested on a rooted device, but I think all steps should also work on non-rooted devices):
-
-adb pull /data/app/com.amazon.kindle-1.apk kindle3.apk
-adb uninstall com.amazon.kindle
-apktool d kindle3.apk kindle3
-cd kindle3
-patch -p1 < ../kindle3.patch
-cd ..
-apktool b kindle3 kindle3_patched.apk
-jarsigner -verbose -keystore kindle.keystore kindle3_patched.apk kindle
-zipalign -v 4 kindle3_patched.apk kindle3_signed.apk
-adb install kindle3_signed.apk
-
-kindle3.patch (based on kindle version 3.0.1.70) is available on pastebin:
-http://pastebin.com/LNpgkcpP
-
-Have fun!
-
-Comment by me — June 9, 2011 @ 9:01 pm | Reply
-
-Hi me,
-Wow! Great work!!!!
-
-With your patch, you have created the equivalent of Unswindle for the Kindle for Android app and it does not even require jailbreaking!
-
-Very nice work indeed!
-
-Comment by some_updates — June 10, 2011 @ 4:28 am | Reply
-
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/kindle3.7.0.108.patch b/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/kindle3.7.0.108.patch
deleted file mode 100644
index 5c7b512..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/kindle3.7.0.108.patch
+++ /dev/null
@@ -1,155 +0,0 @@
-diff -ru kindle3.7.0.108_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle3.7.0.108/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
---- kindle3.7.0.108_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-+++ kindle3.7.0.108/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-@@ -43,6 +43,8 @@
-
- .field private security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
-+.field private pidList:Ljava/lang/String;
-+
- .field private totalMemory:J
-
-
-@@ -78,6 +80,10 @@
-
- .line 132
- iput-object p2, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->deviceType:Lcom/amazon/kcp/application/AmazonDeviceType;
-+
-+ const-string v0, "Open DRMed book to show PID list."
-+
-+ iput-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-
- .line 133
- sget-object v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->TAG:Ljava/lang/String;
-@@ -1242,3 +1248,25 @@
-
- return-wide v0
- .end method
-+
-+.method public getPidList()Ljava/lang/String;
-+ .locals 1
-+
-+ .prologue
-+ .line 15
-+ iget-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ return-object v0
-+.end method
-+
-+.method public setPidList(Ljava/lang/String;)V
-+ .locals 0
-+ .parameter "value"
-+
-+ .prologue
-+ .line 11
-+ iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ .line 12
-+ return-void
-+.end method
-\ No newline at end of file
-diff -ru kindle3.7.0.108_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali kindle3.7.0.108/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
---- kindle3.7.0.108_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-+++ kindle3.7.0.108/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-@@ -30,3 +30,9 @@
-
- .method public abstract getPid()Ljava/lang/String;
- .end method
-+
-+.method public abstract getPidList()Ljava/lang/String;
-+.end method
-+
-+.method public abstract setPidList(Ljava/lang/String;)V
-+.end method
-\ No newline at end of file
-diff -ru kindle3.7.0.108_orig/smali/com/amazon/kcp/info/AboutActivity.smali kindle3.7.0.108/smali/com/amazon/kcp/info/AboutActivity.smali
---- kindle3.7.0.108_orig/smali/com/amazon/kcp/info/AboutActivity.smali
-+++ kindle3.7.0.108/smali/com/amazon/kcp/info/AboutActivity.smali
-@@ -493,6 +493,57 @@
- return-void
- .end method
-
-+.method private populatePIDList()V
-+ .locals 7
-+
-+ .prologue
-+ .line 313
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v0
-+
-+ invoke-interface {v0}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->getPidList()Ljava/lang/String;
-+
-+ move-result-object v1
-+
-+ .line 314
-+ .local v1, PidList:Ljava/lang/String;
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
-+
-+ new-instance v4, Lcom/amazon/kcp/info/AboutActivity$GroupItem;
-+
-+ const-string v5, "PID List"
-+
-+ const v6, 0x1
-+
-+ invoke-direct {v4, p0, v5, v6}, Lcom/amazon/kcp/info/AboutActivity$GroupItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Z)V
-+
-+ invoke-interface {v3, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 315
-+ new-instance v2, Ljava/util/ArrayList;
-+
-+ invoke-direct {v2}, Ljava/util/ArrayList;-><init>()V
-+
-+ .line 316
-+ .local v2, children:Ljava/util/List;,"Ljava/util/List<Lcom/amazon/kcp/info/AboutActivity$DetailItem;>;"
-+ new-instance v3, Lcom/amazon/kcp/info/AboutActivity$DetailItem;
-+
-+ const-string v4, "PIDs"
-+
-+ invoke-direct {v3, p0, v4, v1}, Lcom/amazon/kcp/info/AboutActivity$DetailItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Ljava/lang/String;)V
-+
-+ invoke-interface {v2, v3}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 317
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
-+
-+ invoke-interface {v3, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 318
-+ return-void
-+.end method
-+
- .method private populateDisplayItems()V
- .locals 1
-
-@@ -539,6 +590,9 @@
- invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateDisplayInformation()V
-
- .line 190
-+ invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populatePIDList()V
-+
-+ .line 191
- return-void
-
- .line 172
-diff -ru kindle3.7.0.108_orig/smali/com/amazon/system/security/Security.smali kindle3.7.0.108/smali/com/amazon/system/security/Security.smali
---- kindle3.7.0.108_orig/smali/com/amazon/system/security/Security.smali
-+++ kindle3.7.0.108/smali/com/amazon/system/security/Security.smali
-@@ -926,6 +926,16 @@
- sget-object v0, Lcom/amazon/system/security/Security;->CUSTOM_PID_FOR_BUNDLED_DICTIONARY_DRM:Ljava/lang/String;
-
- aput-object v0, v6, v8
-+
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v5
-+
-+ invoke-static {v6}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
-+
-+ move-result-object v2
-+
-+ invoke-interface {v5, v2}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->setPidList(Ljava/lang/String;)V
-
- .line 353
- return-object v6
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.0.2.1/kindle4.0.2.1.patch b/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.0.2.1/kindle4.0.2.1.patch
deleted file mode 100644
index 010f68f..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.0.2.1/kindle4.0.2.1.patch
+++ /dev/null
@@ -1,238 +0,0 @@
-Only in kindle4.0.2.1: build
-diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle4.0.2.1/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
---- kindle4.0.2.1_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali 2013-05-22 18:39:03.000000000 -0500
-+++ kindle4.0.2.1/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali 2013-05-23 16:54:53.000000000 -0500
-@@ -36,20 +36,22 @@
- .field private maxCpuSpeed:J
-
- .field private maxMemory:J
-
- .field private minCpuSpeed:J
-
- .field private resources:Landroid/content/res/Resources;
-
- .field private security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
-+.field private pidList:Ljava/lang/String;
-+
- .field private totalMemory:J
-
-
- # direct methods
- .method static constructor <clinit>()V
- .locals 1
-
- .prologue
- .line 30
- const-class v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;
-@@ -72,20 +74,24 @@
- .prologue
- .line 130
- invoke-direct {p0}, Ljava/lang/Object;-><init>()V
-
- .line 131
- iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
- .line 132
- iput-object p2, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->deviceType:Lcom/amazon/kcp/application/AmazonDeviceType;
-
-+ const-string v0, "Open DRMed book to show PID list."
-+
-+ iput-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
- .line 133
- sget-object v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->TAG:Ljava/lang/String;
-
- new-instance v0, Ljava/lang/StringBuilder;
-
- invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
-
- const-string v1, "Device Type is set to \""
-
- invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
-@@ -1235,10 +1241,33 @@
- move-result-wide v0
-
- iput-wide v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->totalMemory:J
-
- .line 308
- :cond_0
- iget-wide v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->totalMemory:J
-
- return-wide v0
- .end method
-+
-+.method public getPidList()Ljava/lang/String;
-+ .locals 1
-+
-+ .prologue
-+ .line 15
-+ iget-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ return-object v0
-+.end method
-+
-+.method public setPidList(Ljava/lang/String;)V
-+ .locals 0
-+ .parameter "value"
-+
-+ .prologue
-+ .line 11
-+ iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ .line 12
-+ return-void
-+.end method
-+
-diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali kindle4.0.2.1/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
---- kindle4.0.2.1_orig/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali 2013-05-22 18:39:03.000000000 -0500
-+++ kindle4.0.2.1/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali 2013-05-23 16:55:58.000000000 -0500
-@@ -23,10 +23,16 @@
- .end method
-
- .method public abstract getDeviceTypeId()Ljava/lang/String;
- .end method
-
- .method public abstract getOsVersion()Ljava/lang/String;
- .end method
-
- .method public abstract getPid()Ljava/lang/String;
- .end method
-+
-+.method public abstract getPidList()Ljava/lang/String;
-+.end method
-+
-+.method public abstract setPidList(Ljava/lang/String;)V
-+.end method
-diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/info/AboutActivity.smali kindle4.0.2.1/smali/com/amazon/kcp/info/AboutActivity.smali
---- kindle4.0.2.1_orig/smali/com/amazon/kcp/info/AboutActivity.smali 2013-05-22 18:39:03.000000000 -0500
-+++ kindle4.0.2.1/smali/com/amazon/kcp/info/AboutActivity.smali 2013-05-23 17:18:14.000000000 -0500
-@@ -486,20 +486,71 @@
- .end local v2 #screenDpi:Ljava/lang/String;
- :cond_0
- iget-object v5, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
-
- invoke-interface {v5, v0}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-
- .line 317
- return-void
- .end method
-
-+.method private populatePIDList()V
-+ .locals 7
-+
-+ .prologue
-+ .line 313
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v0
-+
-+ invoke-interface {v0}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->getPidList()Ljava/lang/String;
-+
-+ move-result-object v1
-+
-+ .line 314
-+ .local v1, PidList:Ljava/lang/String;
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
-+
-+ new-instance v4, Lcom/amazon/kcp/info/AboutActivity$GroupItem;
-+
-+ const-string v5, "PID List"
-+
-+ const v6, 0x1
-+
-+ invoke-direct {v4, p0, v5, v6}, Lcom/amazon/kcp/info/AboutActivity$GroupItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Z)V
-+
-+ invoke-interface {v3, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 315
-+ new-instance v2, Ljava/util/ArrayList;
-+
-+ invoke-direct {v2}, Ljava/util/ArrayList;-><init>()V
-+
-+ .line 316
-+ .local v2, children:Ljava/util/List;,"Ljava/util/List<Lcom/amazon/kcp/info/AboutActivity$DetailItem;>;"
-+ new-instance v3, Lcom/amazon/kcp/info/AboutActivity$DetailItem;
-+
-+ const-string v4, "PIDs"
-+
-+ invoke-direct {v3, p0, v4, v1}, Lcom/amazon/kcp/info/AboutActivity$DetailItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Ljava/lang/String;)V
-+
-+ invoke-interface {v2, v3}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 317
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
-+
-+ invoke-interface {v3, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 318
-+ return-void
-+.end method
-+
- .method private populateDisplayItems()V
- .locals 1
-
- .prologue
- .line 171
- iget-object v0, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
-
- if-nez v0, :cond_0
-
- .line 173
-@@ -531,20 +582,22 @@
-
- .line 192
- invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateRamInformation()V
-
- .line 193
- invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateStorageInformation()V
-
- .line 194
- invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateDisplayInformation()V
-
-+ invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populatePIDList()V
-+
- .line 195
- return-void
-
- .line 177
- :cond_0
- iget-object v0, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
-
- invoke-interface {v0}, Ljava/util/List;->clear()V
-
- goto :goto_0
-diff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/system/security/Security.smali kindle4.0.2.1/smali/com/amazon/system/security/Security.smali
---- kindle4.0.2.1_orig/smali/com/amazon/system/security/Security.smali 2013-05-22 18:39:04.000000000 -0500
-+++ kindle4.0.2.1/smali/com/amazon/system/security/Security.smali 2013-05-23 17:19:05.000000000 -0500
-@@ -920,20 +920,30 @@
-
- .line 350
- :cond_2
- add-int/lit8 v8, v8, 0x1
-
- .line 351
- sget-object v0, Lcom/amazon/system/security/Security;->CUSTOM_PID_FOR_BUNDLED_DICTIONARY_DRM:Ljava/lang/String;
-
- aput-object v0, v6, v8
-
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v5
-+
-+ invoke-static {v6}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
-+
-+ move-result-object v2
-+
-+ invoke-interface {v5, v2}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->setPidList(Ljava/lang/String;)V
-+
- .line 353
- return-object v6
- .end method
-
-
- # virtual methods
- .method public customDrmOnly()I
- .locals 1
-
- .prologue
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/Notes on the Patch.txt b/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/Notes on the Patch.txt
deleted file mode 100644
index 07e579a..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/Notes on the Patch.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Notes from UE01 about this patch:
-
-1. Revised the “Other_Tools/Kindle_for_Android_Patches/” for Kindle 4.8.1.10
-2. Built Kindle 4.8.1.10 with the PID List added to the About activity
-3. Uninstalled the Amazon/Play-store version and installed the patched version
-4. Signed in to Amazon
-5. Opened the book
-6. Did Info > About > PID List and copied the PIDs to Calibre’s Plugins>File type > DeDRM > Mobipocket dialog
-7. **Crucial** copied the PRC file to the PC (because the file’s checksum has changed since it was last copied)
-8. In Calibre, Add Books (from a single directory)
-
diff --git a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/kindle4.8.1.10.patch b/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/kindle4.8.1.10.patch
deleted file mode 100644
index daebdcb..0000000
--- a/Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/kindle4.8.1.10.patch
+++ /dev/null
@@ -1,157 +0,0 @@
-diff --git a/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali b/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-index 8ea400e..3aefad2 100644
---- a/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-+++ b/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali
-@@ -41,6 +41,8 @@
-
- .field private security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
-+.field private pidList:Ljava/lang/String;
-+
- .field private totalMemory:J
-
-
-@@ -74,6 +76,10 @@
- .line 133
- iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->security:Lcom/mobipocket/android/library/reader/AndroidSecurity;
-
-+ const-string v0, "Open DRMed book to show PID list."
-+
-+ iput-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
- .line 134
- sget-object v0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->TAG:Ljava/lang/String;
-
-@@ -1339,3 +1345,26 @@
-
- return-wide v0
- .end method
-+
-+.method public getPidList()Ljava/lang/String;
-+ .locals 1
-+
-+ .prologue
-+ .line 15
-+ iget-object v0, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ return-object v0
-+.end method
-+
-+.method public setPidList(Ljava/lang/String;)V
-+ .locals 0
-+ .param p1, "value"
-+
-+ .prologue
-+ .line 11
-+ iput-object p1, p0, Lcom/amazon/kcp/application/AndroidDeviceInformationProvider;->pidList:Ljava/lang/String;
-+
-+ .line 12
-+ return-void
-+.end method
-+
-diff --git a/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali b/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-index e4a3523..2269fab 100644
---- a/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-+++ b/smali/com/amazon/kcp/application/IDeviceInformationProvider.smali
-@@ -30,3 +30,9 @@
-
- .method public abstract getPid()Ljava/lang/String;
- .end method
-+
-+.method public abstract getPidList()Ljava/lang/String;
-+.end method
-+
-+.method public abstract setPidList(Ljava/lang/String;)V
-+.end method
-diff --git a/smali/com/amazon/kcp/info/AboutActivity.smali b/smali/com/amazon/kcp/info/AboutActivity.smali
-index 5640e9e..e298341 100644
---- a/smali/com/amazon/kcp/info/AboutActivity.smali
-+++ b/smali/com/amazon/kcp/info/AboutActivity.smali
-@@ -493,6 +493,57 @@
- return-void
- .end method
-
-+.method private populatePIDList()V
-+ .locals 7
-+
-+ .prologue
-+ .line 313
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v0
-+
-+ invoke-interface {v0}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->getPidList()Ljava/lang/String;
-+
-+ move-result-object v1
-+
-+ .line 314
-+ .local v1, "PidList":Ljava/lang/String;
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->groupItemList:Ljava/util/List;
-+
-+ new-instance v4, Lcom/amazon/kcp/info/AboutActivity$GroupItem;
-+
-+ const-string v5, "PID List"
-+
-+ const v6, 0x1
-+
-+ invoke-direct {v4, p0, v5, v6}, Lcom/amazon/kcp/info/AboutActivity$GroupItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Z)V
-+
-+ invoke-interface {v3, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 315
-+ new-instance v2, Ljava/util/ArrayList;
-+
-+ invoke-direct {v2}, Ljava/util/ArrayList;-><init>()V
-+
-+ .line 316
-+ .local v2, "children":Ljava/util/List;,"Ljava/util/List<Lcom/amazon/kcp/info/AboutActivity$DetailItem;>;"
-+ new-instance v3, Lcom/amazon/kcp/info/AboutActivity$DetailItem;
-+
-+ const-string v4, "PIDs"
-+
-+ invoke-direct {v3, p0, v4, v1}, Lcom/amazon/kcp/info/AboutActivity$DetailItem;-><init>(Lcom/amazon/kcp/info/AboutActivity;Ljava/lang/String;Ljava/lang/String;)V
-+
-+ invoke-interface {v2, v3}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 317
-+ iget-object v3, p0, Lcom/amazon/kcp/info/AboutActivity;->detailItemList:Ljava/util/List;
-+
-+ invoke-interface {v3, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z
-+
-+ .line 318
-+ return-void
-+.end method
-+
- .method private populateDisplayItems()V
- .locals 1
-
-@@ -538,6 +589,8 @@
- .line 173
- invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populateDisplayInformation()V
-
-+ invoke-direct {p0}, Lcom/amazon/kcp/info/AboutActivity;->populatePIDList()V
-+
- .line 174
- return-void
-
-diff --git a/smali/com/amazon/system/security/Security.smali b/smali/com/amazon/system/security/Security.smali
-index 04ea997..e88fe08 100644
---- a/smali/com/amazon/system/security/Security.smali
-+++ b/smali/com/amazon/system/security/Security.smali
-@@ -940,6 +940,16 @@
-
- aput-object v0, v6, v8
-
-+ invoke-static {}, Lcom/amazon/kcp/application/DeviceInformationProviderFactory;->getProvider()Lcom/amazon/kcp/application/IDeviceInformationProvider;
-+
-+ move-result-object v5
-+
-+ invoke-static {v6}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
-+
-+ move-result-object v2
-+
-+ invoke-interface {v5, v2}, Lcom/amazon/kcp/application/IDeviceInformationProvider;->setPidList(Ljava/lang/String;)V
-+
- .line 347
- return-object v6
- .end method \ No newline at end of file
diff --git a/Other_Tools/Kobo/obok.py b/Other_Tools/Kobo/obok.py
deleted file mode 100755
index 2a35795..0000000
--- a/Other_Tools/Kobo/obok.py
+++ /dev/null
@@ -1,751 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Version 3.2.5 December 2016
-# Improve detection of good text decryption.
-#
-# Version 3.2.4 December 2016
-# Remove incorrect support for Kobo Desktop under Wine
-#
-# Version 3.2.3 October 2016
-# Fix for windows network user and more xml fixes
-#
-# Version 3.2.2 October 2016
-# Change to the way the new database version is handled.
-#
-# Version 3.2.1 September 2016
-# Update for v4.0 of Windows Desktop app.
-#
-# Version 3.2.0 January 2016
-# Update for latest version of Windows Desktop app.
-# Support Kobo devices in the command line version.
-#
-# Version 3.1.9 November 2015
-# Handle Kobo Desktop under wine on Linux
-#
-# Version 3.1.8 November 2015
-# Handle the case of Kobo Arc or Vox device (i.e. don't crash).
-#
-# Version 3.1.7 October 2015
-# Handle the case of no device or database more gracefully.
-#
-# Version 3.1.6 September 2015
-# Enable support for Kobo devices
-# More character encoding fixes (unicode strings)
-#
-# Version 3.1.5 September 2015
-# Removed requirement that a purchase has been made.
-# Also add in character encoding fixes
-#
-# Version 3.1.4 September 2015
-# Updated for version 3.17 of the Windows Desktop app.
-#
-# Version 3.1.3 August 2015
-# Add translations for Portuguese and Arabic
-#
-# Version 3.1.2 January 2015
-# Add coding, version number and version announcement
-#
-# Version 3.05 October 2014
-# Identifies DRM-free books in the dialog
-#
-# Version 3.04 September 2014
-# Handles DRM-free books as well (sometimes Kobo Library doesn't
-# show download link for DRM-free books)
-#
-# Version 3.03 August 2014
-# If PyCrypto is unavailable try to use libcrypto for AES_ECB.
-#
-# Version 3.02 August 2014
-# Relax checking of application/xhtml+xml and image/jpeg content.
-#
-# Version 3.01 June 2014
-# Check image/jpeg as well as application/xhtml+xml content. Fix typo
-# in Windows ipconfig parsing.
-#
-# Version 3.0 June 2014
-# Made portable for Mac and Windows, and the only module dependency
-# not part of python core is PyCrypto. Major code cleanup/rewrite.
-# No longer tries the first MAC address; tries them all if it detects
-# the decryption failed.
-#
-# Updated September 2013 by Anon
-# Version 2.02
-# Incorporated minor fixes posted at Apprentice Alf's.
-#
-# Updates July 2012 by Michael Newton
-# PWSD ID is no longer a MAC address, but should always
-# be stored in the registry. Script now works with OS X
-# and checks plist for values instead of registry. Must
-# have biplist installed for OS X support.
-#
-# Original comments left below; note the "AUTOPSY" is inaccurate. See
-# KoboLibrary.userkeys and KoboFile.decrypt()
-#
-##########################################################
-# KOBO DRM CRACK BY #
-# PHYSISTICATED #
-##########################################################
-# This app was made for Python 2.7 on Windows 32-bit
-#
-# This app needs pycrypto - get from here:
-# http://www.voidspace.org.uk/python/modules.shtml
-#
-# Usage: obok.py
-# Choose the book you want to decrypt
-#
-# Shouts to my krew - you know who you are - and one in
-# particular who gave me a lot of help with this - thank
-# you so much!
-#
-# Kopimi /K\
-# Keep sharing, keep copying, but remember that nothing is
-# for free - make sure you compensate your favorite
-# authors - and cut out the middle man whenever possible
-# ;) ;) ;)
-#
-# DRM AUTOPSY
-# The Kobo DRM was incredibly easy to crack, but it took
-# me months to get around to making this. Here's the
-# basics of how it works:
-# 1: Get MAC address of first NIC in ipconfig (sometimes
-# stored in registry as pwsdid)
-# 2: Get user ID (stored in tons of places, this gets it
-# from HKEY_CURRENT_USER\Software\Kobo\Kobo Desktop
-# Edition\Browser\cookies)
-# 3: Concatenate and SHA256, take the second half - this
-# is your master key
-# 4: Open %LOCALAPPDATA%\Kobo Desktop Editions\Kobo.sqlite
-# and dump content_keys
-# 5: Unbase64 the keys, then decode these with the master
-# key - these are your page keys
-# 6: Unzip EPUB of your choice, decrypt each page with its
-# page key, then zip back up again
-#
-# WHY USE THIS WHEN INEPT WORKS FINE? (adobe DRM stripper)
-# Inept works very well, but authors on Kobo can choose
-# what DRM they want to use - and some have chosen not to
-# let people download them with Adobe Digital Editions -
-# they would rather lock you into a single platform.
-#
-# With Obok, you can sync Kobo Desktop, decrypt all your
-# ebooks, and then use them on whatever device you want
-# - you bought them, you own them, you can do what you
-# like with them.
-#
-# Obok is Kobo backwards, but it is also means "next to"
-# in Polish.
-# When you buy a real book, it is right next to you. You
-# can read it at home, at work, on a train, you can lend
-# it to a friend, you can scribble on it, and add your own
-# explanations/translations.
-#
-# Obok gives you this power over your ebooks - no longer
-# are you restricted to one device. This allows you to
-# embed foreign fonts into your books, as older Kobo's
-# can't display them properly. You can read your books
-# on your phones, in different PC readers, and different
-# ereader devices. You can share them with your friends
-# too, if you like - you can do that with a real book
-# after all.
-#
-"""Manage all Kobo books, either encrypted or DRM-free."""
-
-__version__ = '3.2.4'
-__about__ = u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__)
-
-import sys
-import os
-import subprocess
-import sqlite3
-import base64
-import binascii
-import re
-import zipfile
-import hashlib
-import xml.etree.ElementTree as ET
-import string
-import shutil
-import argparse
-import tempfile
-
-can_parse_xml = True
-try:
- from xml.etree import ElementTree as ET
- # print u"using xml.etree for xml parsing"
-except ImportError:
- can_parse_xml = False
- # print u"Cannot find xml.etree, disabling extraction of serial numbers"
-
-# List of all known hash keys
-KOBO_HASH_KEYS = ['88b3a2e13', 'XzUhGYdFp', 'NoCanLook','QJhwzAtXL']
-
-class ENCRYPTIONError(Exception):
- pass
-
-def _load_crypto_libcrypto():
- from ctypes import CDLL, POINTER, c_void_p, c_char_p, c_int, c_long, \
- Structure, c_ulong, create_string_buffer, cast
- from ctypes.util import find_library
-
- if sys.platform.startswith('win'):
- libcrypto = find_library('libeay32')
- else:
- libcrypto = find_library('crypto')
-
- if libcrypto is None:
- raise ENCRYPTIONError('libcrypto not found')
- libcrypto = CDLL(libcrypto)
-
- AES_MAXNR = 14
-
- c_char_pp = POINTER(c_char_p)
- c_int_p = POINTER(c_int)
-
- class AES_KEY(Structure):
- _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
- ('rounds', c_int)]
- AES_KEY_p = POINTER(AES_KEY)
-
- def F(restype, name, argtypes):
- func = getattr(libcrypto, name)
- func.restype = restype
- func.argtypes = argtypes
- return func
-
- AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
- [c_char_p, c_int, AES_KEY_p])
- AES_ecb_encrypt = F(None, 'AES_ecb_encrypt',
- [c_char_p, c_char_p, AES_KEY_p, c_int])
-
- class AES(object):
- def __init__(self, userkey):
- self._blocksize = len(userkey)
- if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
- raise ENCRYPTIONError(_('AES improper key used'))
- return
- key = self._key = AES_KEY()
- rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
- if rv < 0:
- raise ENCRYPTIONError(_('Failed to initialize AES key'))
-
- def decrypt(self, data):
- clear = ''
- for i in range(0, len(data), 16):
- out = create_string_buffer(16)
- rv = AES_ecb_encrypt(data[i:i+16], out, self._key, 0)
- if rv == 0:
- raise ENCRYPTIONError(_('AES decryption failed'))
- clear += out.raw
- return clear
-
- return AES
-
-def _load_crypto_pycrypto():
- from Crypto.Cipher import AES as _AES
- class AES(object):
- def __init__(self, key):
- self._aes = _AES.new(key, _AES.MODE_ECB)
-
- def decrypt(self, data):
- return self._aes.decrypt(data)
-
- return AES
-
-def _load_crypto():
- AES = None
- cryptolist = (_load_crypto_pycrypto, _load_crypto_libcrypto)
- for loader in cryptolist:
- try:
- AES = loader()
- break
- except (ImportError, ENCRYPTIONError):
- pass
- return AES
-
-AES = _load_crypto()
-
-# Wrap a stream so that output gets flushed immediately
-# and also make sure that any unicode strings get
-# encoded using "replace" before writing them.
-class SafeUnbuffered:
- def __init__(self, stream):
- self.stream = stream
- self.encoding = stream.encoding
- if self.encoding == None:
- self.encoding = "utf-8"
- def write(self, data):
- if isinstance(data,unicode):
- data = data.encode(self.encoding,"replace")
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-
-class KoboLibrary(object):
- """The Kobo library.
-
- This class represents all the information available from the data
- written by the Kobo Desktop Edition application, including the list
- of books, their titles, and the user's encryption key(s)."""
-
- def __init__ (self, serials = [], device_path = None):
- print __about__
- self.kobodir = u""
- kobodb = u""
-
- # Order of checks
- # 1. first check if a device_path has been passed in, and whether
- # we can find the sqlite db in the respective place
- # 2. if 1., and we got some serials passed in (from saved
- # settings in calibre), just use it
- # 3. if 1. worked, but we didn't get serials, try to parse them
- # from the device, if this didn't work, unset everything
- # 4. if by now we don't have kobodir set, give up on device and
- # try to use the Desktop app.
-
- # step 1. check whether this looks like a real device
- if (device_path):
- # we got a device path
- self.kobodir = os.path.join(device_path, u".kobo")
- # devices use KoboReader.sqlite
- kobodb = os.path.join(self.kobodir, u"KoboReader.sqlite")
- if (not(os.path.isfile(kobodb))):
- # device path seems to be wrong, unset it
- device_path = u""
- self.kobodir = u""
- kobodb = u""
-
- if (self.kobodir):
- # step 3. we found a device but didn't get serials, try to get them
- if (len(serials) == 0):
- # we got a device path but no saved serial
- # try to get the serial from the device
- # print u"get_device_settings - device_path = {0}".format(device_path)
- # get serial from device_path/.adobe-digital-editions/device.xml
- if can_parse_xml:
- devicexml = os.path.join(device_path, '.adobe-digital-editions', 'device.xml')
- # print u"trying to load {0}".format(devicexml)
- if (os.path.exists(devicexml)):
- # print u"trying to parse {0}".format(devicexml)
- xmltree = ET.parse(devicexml)
- for node in xmltree.iter():
- if "deviceSerial" in node.tag:
- serial = node.text
- # print u"found serial {0}".format(serial)
- serials.append(serial)
- break
- else:
- # print u"cannot get serials from device."
- device_path = u""
- self.kobodir = u""
- kobodb = u""
-
- if (self.kobodir == u""):
- # step 4. we haven't found a device with serials, so try desktop apps
- if sys.platform.startswith('win'):
- import _winreg as winreg
- if sys.getwindowsversion().major > 5:
- if 'LOCALAPPDATA' in os.environ.keys():
- # Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
- if (self.kobodir == u""):
- if 'USERPROFILE' in os.environ.keys():
- # Python 2.x does not return unicode env. Use Python 3.x
- self.kobodir = os.path.join(winreg.ExpandEnvironmentStrings(u"%USERPROFILE%"), u"Local Settings", u"Application Data")
- self.kobodir = os.path.join(self.kobodir, u"Kobo", u"Kobo Desktop Edition")
- elif sys.platform.startswith('darwin'):
- self.kobodir = os.path.join(os.environ['HOME'], u"Library", u"Application Support", u"Kobo", u"Kobo Desktop Edition")
- #elif linux_path != None:
- # Probably Linux, let's get the wine prefix and path to Kobo.
- # self.kobodir = os.path.join(linux_path, u"Local Settings", u"Application Data", u"Kobo", u"Kobo Desktop Edition")
- # desktop versions use Kobo.sqlite
- kobodb = os.path.join(self.kobodir, u"Kobo.sqlite")
- # check for existence of file
- if (not(os.path.isfile(kobodb))):
- # give up here, we haven't found anything useful
- self.kobodir = u""
- kobodb = u""
-
-
- if (self.kobodir != u""):
- self.bookdir = os.path.join(self.kobodir, u"kepub")
- # make a copy of the database in a temporary file
- # so we can ensure it's not using WAL logging which sqlite3 can't do.
- self.newdb = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- print self.newdb.name
- olddb = open(kobodb, 'rb')
- self.newdb.write(olddb.read(18))
- self.newdb.write('\x01\x01')
- olddb.read(2)
- self.newdb.write(olddb.read())
- olddb.close()
- self.newdb.close()
- self.__sqlite = sqlite3.connect(self.newdb.name)
- self.__cursor = self.__sqlite.cursor()
- self._userkeys = []
- self._books = []
- self._volumeID = []
- self._serials = serials
-
- def close (self):
- """Closes the database used by the library."""
- self.__cursor.close()
- self.__sqlite.close()
- # delete the temporary copy of the database
- os.remove(self.newdb.name)
-
- @property
- def userkeys (self):
- """The list of potential userkeys being used by this library.
- Only one of these will be valid.
- """
- if len(self._userkeys) != 0:
- return self._userkeys
- for macaddr in self.__getmacaddrs():
- self._userkeys.extend(self.__getuserkeys(macaddr))
- return self._userkeys
-
- @property
- def books (self):
- """The list of KoboBook objects in the library."""
- if len(self._books) != 0:
- return self._books
- """Drm-ed kepub"""
- for row in self.__cursor.execute('SELECT DISTINCT volumeid, Title, Attribution, Series FROM content_keys, content WHERE contentid = volumeid'):
- self._books.append(KoboBook(row[0], row[1], self.__bookfile(row[0]), 'kepub', self.__cursor, author=row[2], series=row[3]))
- self._volumeID.append(row[0])
- """Drm-free"""
- for f in os.listdir(self.bookdir):
- if(f not in self._volumeID):
- row = self.__cursor.execute("SELECT Title, Attribution, Series FROM content WHERE ContentID = '" + f + "'").fetchone()
- if row is not None:
- fTitle = row[0]
- self._books.append(KoboBook(f, fTitle, self.__bookfile(f), 'drm-free', self.__cursor, author=row[1], series=row[2]))
- self._volumeID.append(f)
- """Sort"""
- self._books.sort(key=lambda x: x.title)
- return self._books
-
- def __bookfile (self, volumeid):
- """The filename needed to open a given book."""
- return os.path.join(self.kobodir, u"kepub", volumeid)
-
- def __getmacaddrs (self):
- """The list of all MAC addresses on this machine."""
- macaddrs = []
- if sys.platform.startswith('win'):
- c = re.compile('\s(' + '[0-9a-f]{2}-' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
- (p_in, p_out, p_err) = os.popen3('ipconfig /all')
- for line in p_out:
- m = c.search(line)
- if m:
- macaddrs.append(re.sub("-", ":", m.group(1)).upper())
- elif sys.platform.startswith('darwin'):
- c = re.compile('\s(' + '[0-9a-f]{2}:' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
- output = subprocess.check_output('/sbin/ifconfig -a', shell=True)
- matches = c.findall(output)
- for m in matches:
- # print u"m:{0}".format(m[0])
- macaddrs.append(m[0].upper())
- elif sys.platform.startswith('linux'):
- p_out = subprocess.check_output("ip -br link show | awk '{print $3}'", shell=True)
- for line in p_out:
- macaddrs.append(line.upper())
- else:
- # probably linux, let's try ipconfig under wine
- c = re.compile('\s(' + '[0-9a-f]{2}-' * 5 + '[0-9a-f]{2})(\s|$)', re.IGNORECASE)
- for line in os.popen('ipconfig /all'):
- m = c.search(line)
- if m:
- macaddrs.append(re.sub("-", ":", m.group(1)).upper())
-
- # extend the list of macaddrs in any case with the serials
- # cannot hurt ;-)
- macaddrs.extend(self._serials)
-
- return macaddrs
-
- def __getuserids (self):
- userids = []
- cursor = self.__cursor.execute('SELECT UserID FROM user')
- row = cursor.fetchone()
- while row is not None:
- try:
- userid = row[0]
- userids.append(userid)
- except:
- pass
- row = cursor.fetchone()
- return userids
-
- def __getuserkeys (self, macaddr):
- userids = self.__getuserids()
- userkeys = []
- for hash in KOBO_HASH_KEYS:
- deviceid = hashlib.sha256(hash + macaddr).hexdigest()
- for userid in userids:
- userkey = hashlib.sha256(deviceid + userid).hexdigest()
- userkeys.append(binascii.a2b_hex(userkey[32:]))
- return userkeys
-
-class KoboBook(object):
- """A Kobo book.
-
- A Kobo book contains a number of unencrypted and encrypted files.
- This class provides a list of the encrypted files.
-
- Each book has the following instance variables:
- volumeid - a UUID which uniquely refers to the book in this library.
- title - the human-readable book title.
- filename - the complete path and filename of the book.
- type - either kepub or drm-free"""
- def __init__ (self, volumeid, title, filename, type, cursor, author=None, series=None):
- self.volumeid = volumeid
- self.title = title
- self.author = author
- self.series = series
- self.series_index = None
- self.filename = filename
- self.type = type
- self.__cursor = cursor
- self._encryptedfiles = {}
-
- @property
- def encryptedfiles (self):
- """A dictionary of KoboFiles inside the book.
-
- The dictionary keys are the relative pathnames, which are
- the same as the pathnames inside the book 'zip' file."""
- if (self.type == 'drm-free'):
- return self._encryptedfiles
- if len(self._encryptedfiles) != 0:
- return self._encryptedfiles
- # Read the list of encrypted files from the DB
- for row in self.__cursor.execute('SELECT elementid,elementkey FROM content_keys,content WHERE volumeid = ? AND volumeid = contentid',(self.volumeid,)):
- self._encryptedfiles[row[0]] = KoboFile(row[0], None, base64.b64decode(row[1]))
-
- # Read the list of files from the kepub OPF manifest so that
- # we can get their proper MIME type.
- # NOTE: this requires that the OPF file is unencrypted!
- zin = zipfile.ZipFile(self.filename, "r")
- xmlns = {
- 'ocf': 'urn:oasis:names:tc:opendocument:xmlns:container',
- 'opf': 'http://www.idpf.org/2007/opf'
- }
- ocf = ET.fromstring(zin.read('META-INF/container.xml'))
- opffile = ocf.find('.//ocf:rootfile', xmlns).attrib['full-path']
- basedir = re.sub('[^/]+$', '', opffile)
- opf = ET.fromstring(zin.read(opffile))
- zin.close()
-
- c = re.compile('/')
- for item in opf.findall('.//opf:item', xmlns):
- mimetype = item.attrib['media-type']
-
- # Convert relative URIs
- href = item.attrib['href']
- if not c.match(href):
- href = string.join((basedir, href), '')
-
- # Update books we've found from the DB.
- if href in self._encryptedfiles:
- self._encryptedfiles[href].mimetype = mimetype
- return self._encryptedfiles
-
- @property
- def has_drm (self):
- return not self.type == 'drm-free'
-
-
-class KoboFile(object):
- """An encrypted file in a KoboBook.
-
- Each file has the following instance variables:
- filename - the relative pathname inside the book zip file.
- mimetype - the file's MIME type, e.g. 'image/jpeg'
- key - the encrypted page key."""
-
- def __init__ (self, filename, mimetype, key):
- self.filename = filename
- self.mimetype = mimetype
- self.key = key
- def decrypt (self, userkey, contents):
- """
- Decrypt the contents using the provided user key and the
- file page key. The caller must determine if the decrypted
- data is correct."""
- # The userkey decrypts the page key (self.key)
- keyenc = AES(userkey)
- decryptedkey = keyenc.decrypt(self.key)
- # The decrypted page key decrypts the content
- pageenc = AES(decryptedkey)
- return self.__removeaespadding(pageenc.decrypt(contents))
-
- def check (self, contents):
- """
- If the contents uses some known MIME types, check if it
- conforms to the type. Throw a ValueError exception if not.
- If the contents uses an uncheckable MIME type, don't check
- it and don't throw an exception.
- Returns True if the content was checked, False if it was not
- checked."""
- if self.mimetype == 'application/xhtml+xml':
- # assume utf-8 with no BOM
- textoffset = 0
- stride = 1
- print u"Checking text:{0}:".format(contents[:10])
- # check for byte order mark
- if contents[:3]=="\xef\xbb\xbf":
- # seems to be utf-8 with BOM
- print u"Could be utf-8 with BOM"
- textoffset = 3
- elif contents[:2]=="\xfe\xff":
- # seems to be utf-16BE
- print u"Could be utf-16BE"
- textoffset = 3
- stride = 2
- elif contents[:2]=="\xff\xfe":
- # seems to be utf-16LE
- print u"Could be utf-16LE"
- textoffset = 2
- stride = 2
- else:
- print u"Perhaps utf-8 without BOM"
-
- # now check that the first few characters are in the ASCII range
- for i in xrange(textoffset,textoffset+5*stride,stride):
- if ord(contents[i])<32 or ord(contents[i])>127:
- # Non-ascii, so decryption probably failed
- print u"Bad character at {0}, value {1}".format(i,ord(contents[i]))
- raise ValueError
- print u"Seems to be good text"
- return True
- if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml":
- # utf-8
- return True
- elif contents[:14]=="\xfe\xff\x00<\x00?\x00x\x00m\x00l":
- # utf-16BE
- return True
- elif contents[:14]=="\xff\xfe<\x00?\x00x\x00m\x00l\x00":
- # utf-16LE
- return True
- elif contents[:9]=="<!DOCTYPE" or contents[:12]=="\xef\xbb\xbf<!DOCTYPE":
- # utf-8 of weird <!DOCTYPE start
- return True
- elif contents[:22]=="\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E":
- # utf-16BE of weird <!DOCTYPE start
- return True
- elif contents[:22]=="\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00":
- # utf-16LE of weird <!DOCTYPE start
- return True
- else:
- print u"Bad XML: {0}".format(contents[:8])
- raise ValueError
- elif self.mimetype == 'image/jpeg':
- if contents[:3] == '\xff\xd8\xff':
- return True
- else:
- print u"Bad JPEG: {0}".format(contents[:3].encode('hex'))
- raise ValueError()
- return False
-
- def __removeaespadding (self, contents):
- """
- Remove the trailing padding, using what appears to be the CMS
- algorithm from RFC 5652 6.3"""
- lastchar = binascii.b2a_hex(contents[-1:])
- strlen = int(lastchar, 16)
- padding = strlen
- if strlen == 1:
- return contents[:-1]
- if strlen < 16:
- for i in range(strlen):
- testchar = binascii.b2a_hex(contents[-strlen:-(strlen-1)])
- if testchar != lastchar:
- padding = 0
- if padding > 0:
- contents = contents[:-padding]
- return contents
-
-def decrypt_book(book, lib):
- print u"Converting {0}".format(book.title)
- zin = zipfile.ZipFile(book.filename, "r")
- # make filename out of Unicode alphanumeric and whitespace equivalents from title
- outname = u"{0}.epub".format(re.sub('[^\s\w]', '_', book.title, 0, re.UNICODE))
- if (book.type == 'drm-free'):
- print u"DRM-free book, conversion is not needed"
- shutil.copyfile(book.filename, outname)
- print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
- return 0
- result = 1
- for userkey in lib.userkeys:
- print u"Trying key: {0}".format(userkey.encode('hex_codec'))
- try:
- zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
- for filename in zin.namelist():
- contents = zin.read(filename)
- if filename in book.encryptedfiles:
- file = book.encryptedfiles[filename]
- contents = file.decrypt(userkey, contents)
- # Parse failures mean the key is probably wrong.
- file.check(contents)
- zout.writestr(filename, contents)
- zout.close()
- print u"Decryption succeeded."
- print u"Book saved as {0}".format(os.path.join(os.getcwd(), outname))
- result = 0
- break
- except ValueError:
- print u"Decryption failed."
- zout.close()
- os.remove(outname)
- zin.close()
- return result
-
-
-def cli_main():
- description = __about__
- epilog = u"Parsing of arguments failed."
- parser = argparse.ArgumentParser(prog=sys.argv[0], description=description, epilog=epilog)
- parser.add_argument('--devicedir', default='/media/KOBOeReader', help="directory of connected Kobo device")
- parser.add_argument('--all', action='store_true', help="flag for converting all books on device")
- args = vars(parser.parse_args())
- serials = []
- devicedir = u""
- if args['devicedir']:
- devicedir = args['devicedir']
-
- lib = KoboLibrary(serials, devicedir)
-
- if args['all']:
- books = lib.books
- else:
- for i, book in enumerate(lib.books):
- print u"{0}: {1}".format(i + 1, book.title)
- print u"Or 'all'"
-
- choice = raw_input(u"Convert book number... ")
- if choice == u'all':
- books = list(lib.books)
- else:
- try:
- num = int(choice)
- books = [lib.books[num - 1]]
- except (ValueError, IndexError):
- print u"Invalid choice. Exiting..."
- exit()
-
- results = [decrypt_book(book, lib) for book in books]
- lib.close()
- overall_result = all(result != 0 for result in results)
- if overall_result != 0:
- print u"Could not decrypt book with any of the keys found."
- return overall_result
-
-
-if __name__ == '__main__':
- sys.stdout=SafeUnbuffered(sys.stdout)
- sys.stderr=SafeUnbuffered(sys.stderr)
- sys.exit(cli_main())
diff --git a/Other_Tools/Rocket_ebooks/rebhack.zip b/Other_Tools/Rocket_ebooks/rebhack.zip
deleted file mode 100644
index 252628e..0000000
--- a/Other_Tools/Rocket_ebooks/rebhack.zip
+++ /dev/null
Binary files differ
diff --git a/Other_Tools/Rocket_ebooks/rebhack_ReadMe.txt b/Other_Tools/Rocket_ebooks/rebhack_ReadMe.txt
deleted file mode 100644
index b8b8430..0000000
--- a/Other_Tools/Rocket_ebooks/rebhack_ReadMe.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Rocket eBooks
-=============
-
-Rocket ebooks (.rb) are no longer sold.
-
-This is the only archive of tools for decrypting Rocket ebooks that I have been able to find. It is included here without further comment or support.
-
-— Alf.
diff --git a/Other_Tools/Scuolabook_DRM/Scuolabook_ReadMe.txt b/Other_Tools/Scuolabook_DRM/Scuolabook_ReadMe.txt
deleted file mode 100644
index cd15dbf..0000000
--- a/Other_Tools/Scuolabook_DRM/Scuolabook_ReadMe.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-The latest Scuolabook tool can be found at Hex's own blog:
-https://thisishex.wordpress.com/scuolabook-drm-remover/
-
-Harper. \ No newline at end of file
diff --git a/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51.pyw b/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51.pyw
deleted file mode 100644
index 6277c50..0000000
--- a/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51.pyw
+++ /dev/null
@@ -1,3160 +0,0 @@
-#! /usr/bin/python
-
-# ineptpdf8.4.51.pyw
-# ineptpdf, version 8.4.51
-
-# To run this program install Python 2.7 from http://www.python.org/download/
-#
-# PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
-#
-# and PyWin Extension (Win32API module) from
-# http://sourceforge.net/projects/pywin32/files/
-#
-# Make sure to install the dedicated versions for Python 2.7.
-#
-# It's recommended to use the 32-Bit Python Windows versions (even with a 64-bit
-# Windows system).
-#
-# Save this script file as
-# ineptpdf8.4.51.pyw and double-click on it to run it.
-
-# Revision history:
-# 1 - Initial release
-# 2 - Improved determination of key-generation algorithm
-# 3 - Correctly handle PDF >=1.5 cross-reference streams
-# 4 - Removal of ciando's personal ID (anon)
-# 5 - removing small bug with V3 ebooks (anon)
-# 6 - changed to adeptkey4.der format for 1.7.2 support (anon)
-# 6.1 - backward compatibility for 1.7.1 and old adeptkey.der (anon)
-# 7 - Get cross reference streams and object streams working for input.
-# Not yet supported on output but this only effects file size,
-# not functionality. (anon2)
-# 7.1 - Correct a problem when an old trailer is not followed by startxref (anon2)
-# 7.2 - Correct malformed Mac OS resource forks for Stanza
-# - Support for cross ref streams on output (decreases file size) (anon2)
-# 7.3 - Correct bug in trailer with cross ref stream that caused the error (anon2)
-# "The root object is missing or invalid" in Adobe Reader.
-# 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
-# instead of first) (anon2)
-# 8 - fileopen user machine identifier support (Tetrachroma)
-# 8.1 - fileopen user cookies support (Tetrachroma)
-# 8.2 - fileopen user name/password support (Tetrachroma)
-# 8.3 - fileopen session cookie support (Tetrachroma)
-# 8.3.1 - fix for the "specified key file does not exist" error (Tetrachroma)
-# 8.3.2 - improved server result parsing (Tetrachroma)
-# 8.4 - Ident4D and encrypted Uuid support (Tetrachroma)
-# 8.4.1 - improved MAC address processing (Tetrachroma)
-# 8.4.2 - FowP3Uuid fallback file processing (Tetrachroma)
-# 8.4.3 - improved user/password pdf file detection (Tetrachroma)
-# 8.4.4 - small bugfix (Tetrachroma)
-# 8.4.5 - improved cookie host searching (Tetrachroma)
-# 8.4.6 - STRICT parsing disabled (non-standard pdf processing) (Tetrachroma)
-# 8.4.7 - UTF-8 input file conversion (Tetrachroma)
-# 8.4.8 - fix for more rare utf8 problems (Tetrachroma)
-# 8.4.9 - solution for utf8 in comination with
-# ident4id method (Tetrachroma)
-# 8.4.10 - line feed processing, non c system drive patch, nrbook support (Tetrachroma)
-# 8.4.11 - alternative ident4id calculation (Tetrachroma)
-# 8.4.12 - fix for capital username characters and
-# other unusual user login names (Tetrachroma & ZeroPoint)
-# 8.4.13 - small bug fixes (Tetrachroma)
-# 8.4.14 - fix for non-standard-conform fileopen pdfs (Tetrachroma)
-# 8.4.15 - 'bad file descriptor'-fix (Tetrachroma)
-# 8.4.16 - improves user/pass detection (Tetrachroma)
-# 8.4.17 - fix for several '=' chars in a DPRM entity (Tetrachroma)
-# 8.4.18 - follow up bug fix for the DPRM problem,
-# more readable error messages (Tetrachroma)
-# 8.4.19 - 2nd fix for 'bad file descriptor' problem (Tetrachroma)
-# 8.4.20 - follow up patch (Tetrachroma)
-# 8.4.21 - 3rd patch for 'bad file descriptor' (Tetrachroma)
-# 8.4.22 - disable prints for exception prevention (Tetrachroma)
-# 8.4.23 - check for additional security attributes (Tetrachroma)
-# 8.4.24 - improved cookie session support (Tetrachroma)
-# 8.4.25 - more compatibility with unicode files (Tetrachroma)
-# 8.4.26 - automated session/user cookie request function (works
-# only with Firefox 3.x+) (Tetrachroma)
-# 8.4.27 - user/password fallback
-# 8.4.28 - AES decryption, improved misconfigured pdf handling,
-# limited experimental APS support (Tetrachroma & Neisklar)
-# 8.4.29 - backport for bad formatted rc4 encrypted pdfs (Tetrachroma)
-# 8.4.30 - extended authorization attributes support (Tetrachroma)
-# 8.4.31 - improved session cookie and better server response error
-# handling (Tetrachroma)
-# 8.4.33 - small cookie optimizations (Tetrachroma)
-# 8.4.33 - debug output option (Tetrachroma)
-# 8.4.34 - better user/password management
-# handles the 'AskUnp' response) (Tetrachroma)
-# 8.4.35 - special handling for non-standard systems (Tetrachroma)
-# 8.4.36 - previous machine/disk handling [PrevMach/PrevDisk] (Tetrachroma)
-# 8.4.36 - FOPN_flock support (Tetrachroma)
-# 8.4.37 - patch for unicode paths/filenames (Tetrachroma)
-# 8.4.38 - small fix for user/password dialog (Tetrachroma)
-# 8.4.39 - sophisticated request mode differentiation, forced
-# uuid calculation (Tetrachroma)
-# 8.4.40 - fix for non standard server responses (Tetrachroma)
-# 8.4.41 - improved user/password request windows,
-# better server response tolerance (Tetrachroma)
-# 8.4.42 - improved nl/cr server response parsing (Tetrachroma)
-# 8.4.43 - fix for user names longer than 13 characters and special
-# uuid encryption (Tetrachroma)
-# 8.4.44 - another fix for ident4d problem (Tetrachroma)
-# 8.4.45 - 2nd fix for ident4d problem (Tetrachroma)
-# 8.4.46 - script cleanup and optimizations (Tetrachroma)
-# 8.4.47 - script identification change to Adobe Reader (Tetrachroma)
-# 8.4.48 - improved tolerance for false file/registry entries (Tetrachroma)
-# 8.4.49 - improved username encryption (Tetrachroma)
-# 8.4.50 - improved (experimental) APS support (Tetrachroma & Neisklar)
-# 8.4.51 - automatic APS offline key retrieval (works only for
-# Onleihe right now) (80ka80 & Tetrachroma)
-
-"""
-Decrypts Adobe ADEPT-encrypted and Fileopen PDF files.
-"""
-
-from __future__ import with_statement
-
-__license__ = 'GPL v3'
-
-import sys
-import os
-import re
-import zlib
-import struct
-import hashlib
-from itertools import chain, islice
-import xml.etree.ElementTree as etree
-import Tkinter
-import Tkconstants
-import tkFileDialog
-import tkMessageBox
-# added for fileopen support
-import urllib
-import urlparse
-import time
-import socket
-import string
-import uuid
-import subprocess
-import time
-import getpass
-from ctypes import *
-import traceback
-import inspect
-import tempfile
-import sqlite3
-import httplib
-try:
- from Crypto.Cipher import ARC4
- # needed for newer pdfs
- from Crypto.Cipher import AES
- from Crypto.Hash import SHA256
- from Crypto.PublicKey import RSA
-
-except ImportError:
- ARC4 = None
- RSA = None
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-class ADEPTError(Exception):
- pass
-
-# global variable (needed for fileopen and password decryption)
-INPUTFILEPATH = ''
-KEYFILEPATH = ''
-PASSWORD = ''
-DEBUG_MODE = False
-IVERSION = '8.4.51'
-
-# Do we generate cross reference streams on output?
-# 0 = never
-# 1 = only if present in input
-# 2 = always
-
-GEN_XREF_STM = 1
-
-# This is the value for the current document
-gen_xref_stm = False # will be set in PDFSerializer
-
-###
-### ASN.1 parsing code from tlslite
-
-def bytesToNumber(bytes):
- total = 0L
- for byte in bytes:
- total = (total << 8) + byte
- return total
-
-class ASN1Error(Exception):
- pass
-
-class ASN1Parser(object):
- class Parser(object):
- def __init__(self, bytes):
- self.bytes = bytes
- self.index = 0
-
- def get(self, length):
- if self.index + length > len(self.bytes):
- raise ASN1Error("Error decoding ASN.1")
- x = 0
- for count in range(length):
- x <<= 8
- x |= self.bytes[self.index]
- self.index += 1
- return x
-
- def getFixBytes(self, lengthBytes):
- bytes = self.bytes[self.index : self.index+lengthBytes]
- self.index += lengthBytes
- return bytes
-
- def getVarBytes(self, lengthLength):
- lengthBytes = self.get(lengthLength)
- return self.getFixBytes(lengthBytes)
-
- def getFixList(self, length, lengthList):
- l = [0] * lengthList
- for x in range(lengthList):
- l[x] = self.get(length)
- return l
-
- def getVarList(self, length, lengthLength):
- lengthList = self.get(lengthLength)
- if lengthList % length != 0:
- raise ASN1Error("Error decoding ASN.1")
- lengthList = int(lengthList/length)
- l = [0] * lengthList
- for x in range(lengthList):
- l[x] = self.get(length)
- return l
-
- def startLengthCheck(self, lengthLength):
- self.lengthCheck = self.get(lengthLength)
- self.indexCheck = self.index
-
- def setLengthCheck(self, length):
- self.lengthCheck = length
- self.indexCheck = self.index
-
- def stopLengthCheck(self):
- if (self.index - self.indexCheck) != self.lengthCheck:
- raise ASN1Error("Error decoding ASN.1")
-
- def atLengthCheck(self):
- if (self.index - self.indexCheck) < self.lengthCheck:
- return False
- elif (self.index - self.indexCheck) == self.lengthCheck:
- return True
- else:
- raise ASN1Error("Error decoding ASN.1")
-
- def __init__(self, bytes):
- p = self.Parser(bytes)
- p.get(1)
- self.length = self._getASN1Length(p)
- self.value = p.getFixBytes(self.length)
-
- def getChild(self, which):
- p = self.Parser(self.value)
- for x in range(which+1):
- markIndex = p.index
- p.get(1)
- length = self._getASN1Length(p)
- p.getFixBytes(length)
- return ASN1Parser(p.bytes[markIndex:p.index])
-
- def _getASN1Length(self, p):
- firstLength = p.get(1)
- if firstLength<=127:
- return firstLength
- else:
- lengthLength = firstLength & 0x7F
- return p.get(lengthLength)
-
-###
-### PDF parsing routines from pdfminer, with changes for EBX_HANDLER
-
-## Utilities
-##
-def choplist(n, seq):
- '''Groups every n elements of the list.'''
- r = []
- for x in seq:
- r.append(x)
- if len(r) == n:
- yield tuple(r)
- r = []
- return
-
-def nunpack(s, default=0):
- '''Unpacks up to 4 bytes big endian.'''
- l = len(s)
- if not l:
- return default
- elif l == 1:
- return ord(s)
- elif l == 2:
- return struct.unpack('>H', s)[0]
- elif l == 3:
- return struct.unpack('>L', '\x00'+s)[0]
- elif l == 4:
- return struct.unpack('>L', s)[0]
- else:
- return TypeError('invalid length: %d' % l)
-
-
-STRICT = 0
-
-
-## PS Exceptions
-##
-class PSException(Exception): pass
-class PSEOF(PSException): pass
-class PSSyntaxError(PSException): pass
-class PSTypeError(PSException): pass
-class PSValueError(PSException): pass
-
-
-## Basic PostScript Types
-##
-
-# PSLiteral
-class PSObject(object): pass
-
-class PSLiteral(PSObject):
- '''
- PS literals (e.g. "/Name").
- Caution: Never create these objects directly.
- Use PSLiteralTable.intern() instead.
- '''
- def __init__(self, name):
- self.name = name
- return
-
- def __repr__(self):
- name = []
- for char in self.name:
- if not char.isalnum():
- char = '#%02x' % ord(char)
- name.append(char)
- return '/%s' % ''.join(name)
-
-# PSKeyword
-class PSKeyword(PSObject):
- '''
- PS keywords (e.g. "showpage").
- Caution: Never create these objects directly.
- Use PSKeywordTable.intern() instead.
- '''
- def __init__(self, name):
- self.name = name
- return
-
- def __repr__(self):
- return self.name
-
-# PSSymbolTable
-class PSSymbolTable(object):
-
- '''
- Symbol table that stores PSLiteral or PSKeyword.
- '''
-
- def __init__(self, classe):
- self.dic = {}
- self.classe = classe
- return
-
- def intern(self, name):
- if name in self.dic:
- lit = self.dic[name]
- else:
- lit = self.classe(name)
- self.dic[name] = lit
- return lit
-
-PSLiteralTable = PSSymbolTable(PSLiteral)
-PSKeywordTable = PSSymbolTable(PSKeyword)
-LIT = PSLiteralTable.intern
-KWD = PSKeywordTable.intern
-KEYWORD_BRACE_BEGIN = KWD('{')
-KEYWORD_BRACE_END = KWD('}')
-KEYWORD_ARRAY_BEGIN = KWD('[')
-KEYWORD_ARRAY_END = KWD(']')
-KEYWORD_DICT_BEGIN = KWD('<<')
-KEYWORD_DICT_END = KWD('>>')
-
-
-def literal_name(x):
- if not isinstance(x, PSLiteral):
- if STRICT:
- raise PSTypeError('Literal required: %r' % x)
- else:
- return str(x)
- return x.name
-
-def keyword_name(x):
- if not isinstance(x, PSKeyword):
- if STRICT:
- raise PSTypeError('Keyword required: %r' % x)
- else:
- return str(x)
- return x.name
-
-
-## PSBaseParser
-##
-EOL = re.compile(r'[\r\n]')
-SPC = re.compile(r'\s')
-NONSPC = re.compile(r'\S')
-HEX = re.compile(r'[0-9a-fA-F]')
-END_LITERAL = re.compile(r'[#/%\[\]()<>{}\s]')
-END_HEX_STRING = re.compile(r'[^\s0-9a-fA-F]')
-HEX_PAIR = re.compile(r'[0-9a-fA-F]{2}|.')
-END_NUMBER = re.compile(r'[^0-9]')
-END_KEYWORD = re.compile(r'[#/%\[\]()<>{}\s]')
-END_STRING = re.compile(r'[()\134]')
-OCT_STRING = re.compile(r'[0-7]')
-ESC_STRING = { 'b':8, 't':9, 'n':10, 'f':12, 'r':13, '(':40, ')':41, '\\':92 }
-
-class PSBaseParser(object):
-
- '''
- Most basic PostScript parser that performs only basic tokenization.
- '''
- BUFSIZ = 4096
-
- def __init__(self, fp):
- self.fp = fp
- self.seek(0)
- return
-
- def __repr__(self):
- return '<PSBaseParser: %r, bufpos=%d>' % (self.fp, self.bufpos)
-
- def flush(self):
- return
-
- def close(self):
- self.flush()
- return
-
- def tell(self):
- return self.bufpos+self.charpos
-
- def poll(self, pos=None, n=80):
- pos0 = self.fp.tell()
- if not pos:
- pos = self.bufpos+self.charpos
- self.fp.seek(pos)
- ##print >>sys.stderr, 'poll(%d): %r' % (pos, self.fp.read(n))
- self.fp.seek(pos0)
- return
-
- def seek(self, pos):
- '''
- Seeks the parser to the given position.
- '''
- self.fp.seek(pos)
- # reset the status for nextline()
- self.bufpos = pos
- self.buf = ''
- self.charpos = 0
- # reset the status for nexttoken()
- self.parse1 = self.parse_main
- self.tokens = []
- return
-
- def fillbuf(self):
- if self.charpos < len(self.buf): return
- # fetch next chunk.
- self.bufpos = self.fp.tell()
- self.buf = self.fp.read(self.BUFSIZ)
- if not self.buf:
- raise PSEOF('Unexpected EOF')
- self.charpos = 0
- return
-
- def parse_main(self, s, i):
- m = NONSPC.search(s, i)
- if not m:
- return (self.parse_main, len(s))
- j = m.start(0)
- c = s[j]
- self.tokenstart = self.bufpos+j
- if c == '%':
- self.token = '%'
- return (self.parse_comment, j+1)
- if c == '/':
- self.token = ''
- return (self.parse_literal, j+1)
- if c in '-+' or c.isdigit():
- self.token = c
- return (self.parse_number, j+1)
- if c == '.':
- self.token = c
- return (self.parse_float, j+1)
- if c.isalpha():
- self.token = c
- return (self.parse_keyword, j+1)
- if c == '(':
- self.token = ''
- self.paren = 1
- return (self.parse_string, j+1)
- if c == '<':
- self.token = ''
- return (self.parse_wopen, j+1)
- if c == '>':
- self.token = ''
- return (self.parse_wclose, j+1)
- self.add_token(KWD(c))
- return (self.parse_main, j+1)
-
- def add_token(self, obj):
- self.tokens.append((self.tokenstart, obj))
- return
-
- def parse_comment(self, s, i):
- m = EOL.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_comment, len(s))
- j = m.start(0)
- self.token += s[i:j]
- # We ignore comments.
- #self.tokens.append(self.token)
- return (self.parse_main, j)
-
- def parse_literal(self, s, i):
- m = END_LITERAL.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_literal, len(s))
- j = m.start(0)
- self.token += s[i:j]
- c = s[j]
- if c == '#':
- self.hex = ''
- return (self.parse_literal_hex, j+1)
- self.add_token(LIT(self.token))
- return (self.parse_main, j)
-
- def parse_literal_hex(self, s, i):
- c = s[i]
- if HEX.match(c) and len(self.hex) < 2:
- self.hex += c
- return (self.parse_literal_hex, i+1)
- if self.hex:
- self.token += chr(int(self.hex, 16))
- return (self.parse_literal, i)
-
- def parse_number(self, s, i):
- m = END_NUMBER.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_number, len(s))
- j = m.start(0)
- self.token += s[i:j]
- c = s[j]
- if c == '.':
- self.token += c
- return (self.parse_float, j+1)
- try:
- self.add_token(int(self.token))
- except ValueError:
- pass
- return (self.parse_main, j)
- def parse_float(self, s, i):
- m = END_NUMBER.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_float, len(s))
- j = m.start(0)
- self.token += s[i:j]
- self.add_token(float(self.token))
- return (self.parse_main, j)
-
- def parse_keyword(self, s, i):
- m = END_KEYWORD.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_keyword, len(s))
- j = m.start(0)
- self.token += s[i:j]
- if self.token == 'true':
- token = True
- elif self.token == 'false':
- token = False
- else:
- token = KWD(self.token)
- self.add_token(token)
- return (self.parse_main, j)
-
- def parse_string(self, s, i):
- m = END_STRING.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_string, len(s))
- j = m.start(0)
- self.token += s[i:j]
- c = s[j]
- if c == '\\':
- self.oct = ''
- return (self.parse_string_1, j+1)
- if c == '(':
- self.paren += 1
- self.token += c
- return (self.parse_string, j+1)
- if c == ')':
- self.paren -= 1
- if self.paren:
- self.token += c
- return (self.parse_string, j+1)
- self.add_token(self.token)
- return (self.parse_main, j+1)
- def parse_string_1(self, s, i):
- c = s[i]
- if OCT_STRING.match(c) and len(self.oct) < 3:
- self.oct += c
- return (self.parse_string_1, i+1)
- if self.oct:
- self.token += chr(int(self.oct, 8))
- return (self.parse_string, i)
- if c in ESC_STRING:
- self.token += chr(ESC_STRING[c])
- return (self.parse_string, i+1)
-
- def parse_wopen(self, s, i):
- c = s[i]
- if c.isspace() or HEX.match(c):
- return (self.parse_hexstring, i)
- if c == '<':
- self.add_token(KEYWORD_DICT_BEGIN)
- i += 1
- return (self.parse_main, i)
-
- def parse_wclose(self, s, i):
- c = s[i]
- if c == '>':
- self.add_token(KEYWORD_DICT_END)
- i += 1
- return (self.parse_main, i)
-
- def parse_hexstring(self, s, i):
- m = END_HEX_STRING.search(s, i)
- if not m:
- self.token += s[i:]
- return (self.parse_hexstring, len(s))
- j = m.start(0)
- self.token += s[i:j]
- token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
- SPC.sub('', self.token))
- self.add_token(token)
- return (self.parse_main, j)
-
- def nexttoken(self):
- while not self.tokens:
- self.fillbuf()
- (self.parse1, self.charpos) = self.parse1(self.buf, self.charpos)
- token = self.tokens.pop(0)
- return token
-
- def nextline(self):
- '''
- Fetches a next line that ends either with \\r or \\n.
- '''
- linebuf = ''
- linepos = self.bufpos + self.charpos
- eol = False
- while 1:
- self.fillbuf()
- if eol:
- c = self.buf[self.charpos]
- # handle '\r\n'
- if c == '\n':
- linebuf += c
- self.charpos += 1
- break
- m = EOL.search(self.buf, self.charpos)
- if m:
- linebuf += self.buf[self.charpos:m.end(0)]
- self.charpos = m.end(0)
- if linebuf[-1] == '\r':
- eol = True
- else:
- break
- else:
- linebuf += self.buf[self.charpos:]
- self.charpos = len(self.buf)
- return (linepos, linebuf)
-
- def revreadlines(self):
- '''
- Fetches a next line backword. This is used to locate
- the trailers at the end of a file.
- '''
- self.fp.seek(0, 2)
- pos = self.fp.tell()
- buf = ''
- while 0 < pos:
- prevpos = pos
- pos = max(0, pos-self.BUFSIZ)
- self.fp.seek(pos)
- s = self.fp.read(prevpos-pos)
- if not s: break
- while 1:
- n = max(s.rfind('\r'), s.rfind('\n'))
- if n == -1:
- buf = s + buf
- break
- yield s[n:]+buf
- s = s[:n]
- buf = ''
- return
-
-
-## PSStackParser
-##
-class PSStackParser(PSBaseParser):
-
- def __init__(self, fp):
- PSBaseParser.__init__(self, fp)
- self.reset()
- return
-
- def reset(self):
- self.context = []
- self.curtype = None
- self.curstack = []
- self.results = []
- return
-
- def seek(self, pos):
- PSBaseParser.seek(self, pos)
- self.reset()
- return
-
- def push(self, *objs):
- self.curstack.extend(objs)
- return
- def pop(self, n):
- objs = self.curstack[-n:]
- self.curstack[-n:] = []
- return objs
- def popall(self):
- objs = self.curstack
- self.curstack = []
- return objs
- def add_results(self, *objs):
- self.results.extend(objs)
- return
-
- def start_type(self, pos, type):
- self.context.append((pos, self.curtype, self.curstack))
- (self.curtype, self.curstack) = (type, [])
- return
- def end_type(self, type):
- if self.curtype != type:
- raise PSTypeError('Type mismatch: %r != %r' % (self.curtype, type))
- objs = [ obj for (_,obj) in self.curstack ]
- (pos, self.curtype, self.curstack) = self.context.pop()
- return (pos, objs)
-
- def do_keyword(self, pos, token):
- return
-
- def nextobject(self, direct=False):
- '''
- Yields a list of objects: keywords, literals, strings,
- numbers, arrays and dictionaries. Arrays and dictionaries
- are represented as Python sequence and dictionaries.
- '''
- while not self.results:
- (pos, token) = self.nexttoken()
- ##print (pos,token), (self.curtype, self.curstack)
- if (isinstance(token, int) or
- isinstance(token, float) or
- isinstance(token, bool) or
- isinstance(token, str) or
- isinstance(token, PSLiteral)):
- # normal token
- self.push((pos, token))
- elif token == KEYWORD_ARRAY_BEGIN:
- # begin array
- self.start_type(pos, 'a')
- elif token == KEYWORD_ARRAY_END:
- # end array
- try:
- self.push(self.end_type('a'))
- except PSTypeError:
- if STRICT: raise
- elif token == KEYWORD_DICT_BEGIN:
- # begin dictionary
- self.start_type(pos, 'd')
- elif token == KEYWORD_DICT_END:
- # end dictionary
- try:
- (pos, objs) = self.end_type('d')
- if len(objs) % 2 != 0:
- raise PSSyntaxError(
- 'Invalid dictionary construct: %r' % objs)
- d = dict((literal_name(k), v) \
- for (k,v) in choplist(2, objs))
- self.push((pos, d))
- except PSTypeError:
- if STRICT: raise
- else:
- self.do_keyword(pos, token)
- if self.context:
- continue
- else:
- if direct:
- return self.pop(1)[0]
- self.flush()
- obj = self.results.pop(0)
- return obj
-
-
-LITERAL_CRYPT = PSLiteralTable.intern('Crypt')
-LITERALS_FLATE_DECODE = (PSLiteralTable.intern('FlateDecode'), PSLiteralTable.intern('Fl'))
-LITERALS_LZW_DECODE = (PSLiteralTable.intern('LZWDecode'), PSLiteralTable.intern('LZW'))
-LITERALS_ASCII85_DECODE = (PSLiteralTable.intern('ASCII85Decode'), PSLiteralTable.intern('A85'))
-
-
-## PDF Objects
-##
-class PDFObject(PSObject): pass
-
-class PDFException(PSException): pass
-class PDFTypeError(PDFException): pass
-class PDFValueError(PDFException): pass
-class PDFNotImplementedError(PSException): pass
-
-
-## PDFObjRef
-##
-class PDFObjRef(PDFObject):
-
- def __init__(self, doc, objid, genno):
- if objid == 0:
- if STRICT:
- raise PDFValueError('PDF object id cannot be 0.')
- self.doc = doc
- self.objid = objid
- self.genno = genno
- return
-
- def __repr__(self):
- return '<PDFObjRef:%d %d>' % (self.objid, self.genno)
-
- def resolve(self):
- return self.doc.getobj(self.objid)
-
-
-# resolve
-def resolve1(x):
- '''
- Resolve an object. If this is an array or dictionary,
- it may still contains some indirect objects inside.
- '''
- while isinstance(x, PDFObjRef):
- x = x.resolve()
- return x
-
-def resolve_all(x):
- '''
- Recursively resolve X and all the internals.
- Make sure there is no indirect reference within the nested object.
- This procedure might be slow.
- '''
- while isinstance(x, PDFObjRef):
- x = x.resolve()
- if isinstance(x, list):
- x = [ resolve_all(v) for v in x ]
- elif isinstance(x, dict):
- for (k,v) in x.iteritems():
- x[k] = resolve_all(v)
- return x
-
-def decipher_all(decipher, objid, genno, x):
- '''
- Recursively decipher X.
- '''
- if isinstance(x, str):
- return decipher(objid, genno, x)
- decf = lambda v: decipher_all(decipher, objid, genno, v)
- if isinstance(x, list):
- x = [decf(v) for v in x]
- elif isinstance(x, dict):
- x = dict((k, decf(v)) for (k, v) in x.iteritems())
- return x
-
-
-# Type cheking
-def int_value(x):
- x = resolve1(x)
- if not isinstance(x, int):
- if STRICT:
- raise PDFTypeError('Integer required: %r' % x)
- return 0
- return x
-
-def float_value(x):
- x = resolve1(x)
- if not isinstance(x, float):
- if STRICT:
- raise PDFTypeError('Float required: %r' % x)
- return 0.0
- return x
-
-def num_value(x):
- x = resolve1(x)
- if not (isinstance(x, int) or isinstance(x, float)):
- if STRICT:
- raise PDFTypeError('Int or Float required: %r' % x)
- return 0
- return x
-
-def str_value(x):
- x = resolve1(x)
- if not isinstance(x, str):
- if STRICT:
- raise PDFTypeError('String required: %r' % x)
- return ''
- return x
-
-def list_value(x):
- x = resolve1(x)
- if not (isinstance(x, list) or isinstance(x, tuple)):
- if STRICT:
- raise PDFTypeError('List required: %r' % x)
- return []
- return x
-
-def dict_value(x):
- x = resolve1(x)
- if not isinstance(x, dict):
- if STRICT:
- raise PDFTypeError('Dict required: %r' % x)
- return {}
- return x
-
-def stream_value(x):
- x = resolve1(x)
- if not isinstance(x, PDFStream):
- if STRICT:
- raise PDFTypeError('PDFStream required: %r' % x)
- return PDFStream({}, '')
- return x
-
-# ascii85decode(data)
-def ascii85decode(data):
- n = b = 0
- out = ''
- for c in data:
- if '!' <= c and c <= 'u':
- n += 1
- b = b*85+(ord(c)-33)
- if n == 5:
- out += struct.pack('>L',b)
- n = b = 0
- elif c == 'z':
- assert n == 0
- out += '\0\0\0\0'
- elif c == '~':
- if n:
- for _ in range(5-n):
- b = b*85+84
- out += struct.pack('>L',b)[:n-1]
- break
- return out
-
-
-## PDFStream type
-class PDFStream(PDFObject):
- def __init__(self, dic, rawdata, decipher=None):
- length = int_value(dic.get('Length', 0))
- eol = rawdata[length:]
- # quick and dirty fix for false length attribute,
- # might not work if the pdf stream parser has a problem
- if decipher != None and decipher.__name__ == 'decrypt_aes':
- if (len(rawdata) % 16) != 0:
- cutdiv = len(rawdata) // 16
- rawdata = rawdata[:16*cutdiv]
- else:
- if eol in ('\r', '\n', '\r\n'):
- rawdata = rawdata[:length]
-
- self.dic = dic
- self.rawdata = rawdata
- self.decipher = decipher
- self.data = None
- self.decdata = None
- self.objid = None
- self.genno = None
- return
-
- def set_objid(self, objid, genno):
- self.objid = objid
- self.genno = genno
- return
-
- def __repr__(self):
- if self.rawdata:
- return '<PDFStream(%r): raw=%d, %r>' % \
- (self.objid, len(self.rawdata), self.dic)
- else:
- return '<PDFStream(%r): data=%d, %r>' % \
- (self.objid, len(self.data), self.dic)
-
- def decode(self):
- assert self.data is None and self.rawdata is not None
- data = self.rawdata
- if self.decipher:
- # Handle encryption
- data = self.decipher(self.objid, self.genno, data)
- if gen_xref_stm:
- self.decdata = data # keep decrypted data
- if 'Filter' not in self.dic:
- self.data = data
- self.rawdata = None
- ##print self.dict
- return
- filters = self.dic['Filter']
- if not isinstance(filters, list):
- filters = [ filters ]
- for f in filters:
- if f in LITERALS_FLATE_DECODE:
- # will get errors if the document is encrypted.
- data = zlib.decompress(data)
- elif f in LITERALS_LZW_DECODE:
- data = ''.join(LZWDecoder(StringIO(data)).run())
- elif f in LITERALS_ASCII85_DECODE:
- data = ascii85decode(data)
- elif f == LITERAL_CRYPT:
- raise PDFNotImplementedError('/Crypt filter is unsupported')
- else:
- raise PDFNotImplementedError('Unsupported filter: %r' % f)
- # apply predictors
- if 'DP' in self.dic:
- params = self.dic['DP']
- else:
- params = self.dic.get('DecodeParms', {})
- if 'Predictor' in params:
- pred = int_value(params['Predictor'])
- if pred:
- if pred != 12:
- raise PDFNotImplementedError(
- 'Unsupported predictor: %r' % pred)
- if 'Columns' not in params:
- raise PDFValueError(
- 'Columns undefined for predictor=12')
- columns = int_value(params['Columns'])
- buf = ''
- ent0 = '\x00' * columns
- for i in xrange(0, len(data), columns+1):
- pred = data[i]
- ent1 = data[i+1:i+1+columns]
- if pred == '\x02':
- ent1 = ''.join(chr((ord(a)+ord(b)) & 255) \
- for (a,b) in zip(ent0,ent1))
- buf += ent1
- ent0 = ent1
- data = buf
- self.data = data
- self.rawdata = None
- return
-
- def get_data(self):
- if self.data is None:
- self.decode()
- return self.data
-
- def get_rawdata(self):
- return self.rawdata
-
- def get_decdata(self):
- if self.decdata is not None:
- return self.decdata
- data = self.rawdata
- if self.decipher and data:
- # Handle encryption
- data = self.decipher(self.objid, self.genno, data)
- return data
-
-
-## PDF Exceptions
-##
-class PDFSyntaxError(PDFException): pass
-class PDFNoValidXRef(PDFSyntaxError): pass
-class PDFEncryptionError(PDFException): pass
-class PDFPasswordIncorrect(PDFEncryptionError): pass
-
-# some predefined literals and keywords.
-LITERAL_OBJSTM = PSLiteralTable.intern('ObjStm')
-LITERAL_XREF = PSLiteralTable.intern('XRef')
-LITERAL_PAGE = PSLiteralTable.intern('Page')
-LITERAL_PAGES = PSLiteralTable.intern('Pages')
-LITERAL_CATALOG = PSLiteralTable.intern('Catalog')
-
-
-## XRefs
-##
-
-## PDFXRef
-##
-class PDFXRef(object):
-
- def __init__(self):
- self.offsets = None
- return
-
- def __repr__(self):
- return '<PDFXRef: objs=%d>' % len(self.offsets)
-
- def objids(self):
- return self.offsets.iterkeys()
-
- def load(self, parser):
- self.offsets = {}
- while 1:
- try:
- (pos, line) = parser.nextline()
- except PSEOF:
- raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
- if not line:
- raise PDFNoValidXRef('Premature eof: %r' % parser)
- if line.startswith('trailer'):
- parser.seek(pos)
- break
- f = line.strip().split(' ')
- if len(f) != 2:
- raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
- try:
- (start, nobjs) = map(int, f)
- except ValueError:
- raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
- for objid in xrange(start, start+nobjs):
- try:
- (_, line) = parser.nextline()
- except PSEOF:
- raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
- f = line.strip().split(' ')
- if len(f) != 3:
- raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
- (pos, genno, use) = f
- if use != 'n': continue
- self.offsets[objid] = (int(genno), int(pos))
- self.load_trailer(parser)
- return
-
- KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
- def load_trailer(self, parser):
- try:
- (_,kwd) = parser.nexttoken()
- assert kwd is self.KEYWORD_TRAILER
- (_,dic) = parser.nextobject(direct=True)
- except PSEOF:
- x = parser.pop(1)
- if not x:
- raise PDFNoValidXRef('Unexpected EOF - file corrupted')
- (_,dic) = x[0]
- self.trailer = dict_value(dic)
- return
-
- def getpos(self, objid):
- try:
- (genno, pos) = self.offsets[objid]
- except KeyError:
- raise
- return (None, pos)
-
-
-## PDFXRefStream
-##
-class PDFXRefStream(object):
-
- def __init__(self):
- self.index = None
- self.data = None
- self.entlen = None
- self.fl1 = self.fl2 = self.fl3 = None
- return
-
- def __repr__(self):
- return '<PDFXRef: objids=%s>' % self.index
-
- def objids(self):
- for first, size in self.index:
- for objid in xrange(first, first + size):
- yield objid
-
- def load(self, parser, debug=0):
- (_,objid) = parser.nexttoken() # ignored
- (_,genno) = parser.nexttoken() # ignored
- (_,kwd) = parser.nexttoken()
- (_,stream) = parser.nextobject()
- if not isinstance(stream, PDFStream) or \
- stream.dic['Type'] is not LITERAL_XREF:
- raise PDFNoValidXRef('Invalid PDF stream spec.')
- size = stream.dic['Size']
- index = stream.dic.get('Index', (0,size))
- self.index = zip(islice(index, 0, None, 2),
- islice(index, 1, None, 2))
- (self.fl1, self.fl2, self.fl3) = stream.dic['W']
- self.data = stream.get_data()
- self.entlen = self.fl1+self.fl2+self.fl3
- self.trailer = stream.dic
- return
-
- def getpos(self, objid):
- offset = 0
- for first, size in self.index:
- if first <= objid and objid < (first + size):
- break
- offset += size
- else:
- raise KeyError(objid)
- i = self.entlen * ((objid - first) + offset)
- ent = self.data[i:i+self.entlen]
- f1 = nunpack(ent[:self.fl1], 1)
- if f1 == 1:
- pos = nunpack(ent[self.fl1:self.fl1+self.fl2])
- genno = nunpack(ent[self.fl1+self.fl2:])
- return (None, pos)
- elif f1 == 2:
- objid = nunpack(ent[self.fl1:self.fl1+self.fl2])
- index = nunpack(ent[self.fl1+self.fl2:])
- return (objid, index)
- # this is a free object
- raise KeyError(objid)
-
-
-## PDFDocument
-##
-## A PDFDocument object represents a PDF document.
-## Since a PDF file is usually pretty big, normally it is not loaded
-## at once. Rather it is parsed dynamically as processing goes.
-## A PDF parser is associated with the document.
-##
-class PDFDocument(object):
-
- def __init__(self):
- self.xrefs = []
- self.objs = {}
- self.parsed_objs = {}
- self.root = None
- self.catalog = None
- self.parser = None
- self.encryption = None
- self.decipher = None
- # dictionaries for fileopen
- self.fileopen = {}
- self.urlresult = {}
- self.ready = False
- return
-
- # set_parser(parser)
- # Associates the document with an (already initialized) parser object.
- def set_parser(self, parser):
- if self.parser: return
- self.parser = parser
- # The document is set to be temporarily ready during collecting
- # all the basic information about the document, e.g.
- # the header, the encryption information, and the access rights
- # for the document.
- self.ready = True
- # Retrieve the information of each header that was appended
- # (maybe multiple times) at the end of the document.
- self.xrefs = parser.read_xref()
- for xref in self.xrefs:
- trailer = xref.trailer
- if not trailer: continue
-
- # If there's an encryption info, remember it.
- if 'Encrypt' in trailer:
- #assert not self.encryption
- try:
- self.encryption = (list_value(trailer['ID']),
- dict_value(trailer['Encrypt']))
- # fix for bad files
- except:
- self.encryption = ('ffffffffffffffffffffffffffffffffffff',
- dict_value(trailer['Encrypt']))
- if 'Root' in trailer:
- self.set_root(dict_value(trailer['Root']))
- break
- else:
- raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
- # The document is set to be non-ready again, until all the
- # proper initialization (asking the password key and
- # verifying the access permission, so on) is finished.
- self.ready = False
- return
-
- # set_root(root)
- # Set the Root dictionary of the document.
- # Each PDF file must have exactly one /Root dictionary.
- def set_root(self, root):
- self.root = root
- self.catalog = dict_value(self.root)
- if self.catalog.get('Type') is not LITERAL_CATALOG:
- if STRICT:
- raise PDFSyntaxError('Catalog not found!')
- return
- # initialize(password='')
- # Perform the initialization with a given password.
- # This step is mandatory even if there's no password associated
- # with the document.
- def initialize(self, password=''):
- if not self.encryption:
- self.is_printable = self.is_modifiable = self.is_extractable = True
- self.ready = True
- return
- (docid, param) = self.encryption
- type = literal_name(param['Filter'])
- if type == 'Adobe.APS':
- return self.initialize_adobe_ps(password, docid, param)
- if type == 'Standard':
- return self.initialize_standard(password, docid, param)
- if type == 'EBX_HANDLER':
- return self.initialize_ebx(password, docid, param)
- if type == 'FOPN_fLock':
- # remove of unnecessairy password attribute
- return self.initialize_fopn_flock(docid, param)
- if type == 'FOPN_foweb':
- # remove of unnecessairy password attribute
- return self.initialize_fopn(docid, param)
- raise PDFEncryptionError('Unknown filter: param=%r' % param)
-
- def initialize_adobe_ps(self, password, docid, param):
- global KEYFILEPATH
- self.decrypt_key = self.genkey_adobe_ps(param)
- self.genkey = self.genkey_v4
- self.decipher = self.decrypt_aes
- self.ready = True
- return
-
- def getPrincipalKey(self, k=None, url=None, referer=None):
- if url == None:
- url="ssl://edc.bibliothek-digital.de/edcws/services/urn:EDCLicenseService"
- data1='<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SO'+\
- 'AP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http'+\
- '://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/'+\
- 'XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns1="'+\
- 'http://edc.adobe.com/edcwebservice" xmlns:impl="http://localhost:8080/axis/s'+\
- 'ervices/urn:EDCLicenseService" xmlns:ns2="http://common.edc.adobe.com" xmlns:ns1="'+\
- 'http://ns.adobe.com/PolicyServer/ws"><SOAP-ENV:Header><EDCSecurity>&lt;wsse:Security '+\
- 'xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-'+\
- '1.0.xsd"&gt;&lt;wsse:UsernameToken&gt;&lt;wsse:Username&gt;edc_anonymous&lt;/wsse:Username&'+\
- 'gt;&lt;wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-'+\
- 'token-profile-1.0#PasswordText"&gt;edc_anonymous&lt;/wsse:Password&gt;&lt;/wsse:UsernameToken&'+\
- 'gt;&lt;/wsse:Security&gt;</EDCSecurity><Version>7</Version><Locale>de-de</Locale></SOAP-ENV:Header>'+\
- '<SOAP-ENV:Body><impl:synchronize><SynchronizationRequest><firstTime>1</firstTime><licenseSeqNum>0</'+\
- 'licenseSeqNum><policySeqNum>1</policySeqNum><revocationSeqNum>0</revocationSeqNum><'+\
- 'watermarkTemplateSeqNum>0</watermarkTemplateSeqNum></SynchronizationRequest></'+\
- 'impl:synchronize></SOAP-ENV:Body></SOAP-ENV:Envelope>'
- if k not in url[:40]:
- return None
- #~ extract host and path:
- host=re.compile(r'[a-zA-Z]://([^/]+)/.+', re.I).search(url).group(1)
- urlpath=re.compile(r'[a-zA-Z]://[^/]+(/.+)', re.I).search(url).group(1)
-
- # open a socket connection on port 80
-
- conn = httplib.HTTPSConnection(host, 443)
-
- #~ Headers for request
- headers={"Accept": "*/*", "Host": host, "User-Agent": "Mozilla/3.0 (compatible; Acrobat EDC SOAP 1.0)",
- "Content-Type": "text/xml; charset=utf-8", "Cache-Control": "no-cache", "SOAPAction": ""}
-
- # send data1 and headers
- try:
- conn.request("POST", urlpath, data1, headers)
- except:
- raise ADEPTError("Could not post request to '"+host+"'.")
-
- # read respose
- try:
- response = conn.getresponse()
- responsedata=response.read()
- except:
- raise ADEPTError("Could not read response from '"+host+"'.")
-
- # close connection
- conn.close()
-
- try:
- key=re.compile(r'PricipalKey"((?!<key>).)*<key[^>]*>(((?!</key>).)*)</key>', re.I).search(responsedata).group(2)
-
- except :
- key=None
- return key
-
- def genkey_adobe_ps(self, param):
- # nice little offline principal keys dictionary
- principalkeys = { 'bibliothek-digital.de': 'Dzqx8McQUNd2CDzBVmtnweUxVWlqJTMqyYtiDIc4dZI='.decode('base64')}
- for k, v in principalkeys.iteritems():
- result = self.getPrincipalKey(k)
- #print result
- if result != None:
- principalkeys[k] = result.decode('base64')
- else:
- raise ADEPTError("No (Online) PrincipalKey found.")
-
- self.is_printable = self.is_modifiable = self.is_extractable = True
-## print 'keyvalue'
-## print len(keyvalue)
-## print keyvalue.encode('hex')
- length = int_value(param.get('Length', 0)) / 8
- edcdata = str_value(param.get('EDCData')).decode('base64')
- pdrllic = str_value(param.get('PDRLLic')).decode('base64')
- pdrlpol = str_value(param.get('PDRLPol')).decode('base64')
- #print 'ecd rights'
- edclist = []
- for pair in edcdata.split('\n'):
- edclist.append(pair)
-## print edclist
-## print 'edcdata decrypted'
-## print edclist[0].decode('base64').encode('hex')
-## print edclist[1].decode('base64').encode('hex')
-## print edclist[2].decode('base64').encode('hex')
-## print edclist[3].decode('base64').encode('hex')
-## print 'offlinekey'
-## print len(edclist[9].decode('base64'))
-## print pdrllic
- # principal key request
- for key in principalkeys:
- if key in pdrllic:
- principalkey = principalkeys[key]
- else:
- raise ADEPTError('Cannot find principal key for this pdf')
-## print 'minorversion'
-## print int(edclist[8])
- # fix for minor version
-## minorversion = int(edclist[8]) - 100
-## if minorversion < 1:
-## minorversion = 1
-## print int(minorversion)
- shakey = SHA256.new()
- shakey.update(principalkey)
-## for i in range(0,minorversion):
-## shakey.update(principalkey)
- shakey = shakey.digest()
-## shakey = SHA256.new(principalkey).digest()
- ivector = 16 * chr(0)
- #print shakey
- plaintext = AES.new(shakey,AES.MODE_CBC,ivector).decrypt(edclist[9].decode('base64'))
- if plaintext[-16:] != 16 * chr(16):
- raise ADEPTError('Offlinekey cannot be decrypted, aborting (hint: redownload pdf) ...')
- pdrlpol = AES.new(plaintext[16:32],AES.MODE_CBC,edclist[2].decode('base64')).decrypt(pdrlpol)
- if ord(pdrlpol[-1]) < 1 or ord(pdrlpol[-1]) > 16:
- raise ADEPTError('Could not decrypt PDRLPol, aborting ...')
- else:
- cutter = -1 * ord(pdrlpol[-1])
- #print cutter
- pdrlpol = pdrlpol[:cutter]
- #print plaintext.encode('hex')
- #print 'pdrlpol'
- #print pdrlpol
- return plaintext[:16]
-
- PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
- '\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
- # experimental aes pw support
- def initialize_standard(self, password, docid, param):
- # copy from a global variable
- V = int_value(param.get('V', 0))
- if (V <=0 or V > 4):
- raise PDFEncryptionError('Unknown algorithm: param=%r' % param)
- length = int_value(param.get('Length', 40)) # Key length (bits)
- O = str_value(param['O'])
- R = int_value(param['R']) # Revision
- if 5 <= R:
- raise PDFEncryptionError('Unknown revision: %r' % R)
- U = str_value(param['U'])
- P = int_value(param['P'])
- try:
- EncMetadata = str_value(param['EncryptMetadata'])
- except:
- EncMetadata = 'True'
- self.is_printable = bool(P & 4)
- self.is_modifiable = bool(P & 8)
- self.is_extractable = bool(P & 16)
- self.is_annotationable = bool(P & 32)
- self.is_formsenabled = bool(P & 256)
- self.is_textextractable = bool(P & 512)
- self.is_assemblable = bool(P & 1024)
- self.is_formprintable = bool(P & 2048)
- # Algorithm 3.2
- password = (password+self.PASSWORD_PADDING)[:32] # 1
- hash = hashlib.md5(password) # 2
- hash.update(O) # 3
- hash.update(struct.pack('<l', P)) # 4
- hash.update(docid[0]) # 5
- # aes special handling if metadata isn't encrypted
- if EncMetadata == ('False' or 'false'):
- hash.update('ffffffff'.decode('hex'))
- # 6
-## raise PDFNotImplementedError(
-## 'Revision 4 encryption is currently unsupported')
- if 5 <= R:
- # 8
- for _ in xrange(50):
- hash = hashlib.md5(hash.digest()[:length/8])
- key = hash.digest()[:length/8]
- if R == 2:
- # Algorithm 3.4
- u1 = ARC4.new(key).decrypt(password)
- elif R >= 3:
- # Algorithm 3.5
- hash = hashlib.md5(self.PASSWORD_PADDING) # 2
- hash.update(docid[0]) # 3
- x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
- for i in xrange(1,19+1):
- k = ''.join( chr(ord(c) ^ i) for c in key )
- x = ARC4.new(k).decrypt(x)
- u1 = x+x # 32bytes total
- if R == 2:
- is_authenticated = (u1 == U)
- else:
- is_authenticated = (u1[:16] == U[:16])
- if not is_authenticated:
- raise ADEPTError('Password is not correct.')
-## raise PDFPasswordIncorrect
- self.decrypt_key = key
- # genkey method
- if V == 1 or V == 2:
- self.genkey = self.genkey_v2
- elif V == 3:
- self.genkey = self.genkey_v3
- elif V == 4:
- self.genkey = self.genkey_v2
- #self.genkey = self.genkey_v3 if V == 3 else self.genkey_v2
- # rc4
- if V != 4:
- self.decipher = self.decipher_rc4 # XXX may be AES
- # aes
- elif V == 4 and Length == 128:
- elf.decipher = self.decipher_aes
- elif V == 4 and Length == 256:
- raise PDFNotImplementedError('AES256 encryption is currently unsupported')
- self.ready = True
- return
-
- def initialize_ebx(self, password, docid, param):
- global KEYFILEPATH
- self.is_printable = self.is_modifiable = self.is_extractable = True
- # keyfile path is wrong
- if KEYFILEPATH == False:
- errortext = 'Cannot find adeptkey.der keyfile. Use ineptkey to generate it.'
- raise ADEPTError(errortext)
- with open(password, 'rb') as f:
- keyder = f.read()
- # KEYFILEPATH = ''
- key = ASN1Parser([ord(x) for x in keyder])
- key = [bytesToNumber(key.getChild(x).value) for x in xrange(1, 4)]
- rsa = RSA.construct(key)
- length = int_value(param.get('Length', 0)) / 8
- rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
- rights = zlib.decompress(rights, -15)
- rights = etree.fromstring(rights)
- expr = './/{http://ns.adobe.com/adept}encryptedKey'
- bookkey = ''.join(rights.findtext(expr)).decode('base64')
- bookkey = rsa.decrypt(bookkey)
- if bookkey[0] != '\x02':
- raise ADEPTError('error decrypting book session key')
- index = bookkey.index('\0') + 1
- bookkey = bookkey[index:]
- ebx_V = int_value(param.get('V', 4))
- ebx_type = int_value(param.get('EBX_ENCRYPTIONTYPE', 6))
- # added because of the booktype / decryption book session key error
- if ebx_V == 3:
- V = 3
- elif ebx_V < 4 or ebx_type < 6:
- V = ord(bookkey[0])
- bookkey = bookkey[1:]
- else:
- V = 2
- if length and len(bookkey) != length:
- raise ADEPTError('error decrypting book session key')
- self.decrypt_key = bookkey
- self.genkey = self.genkey_v3 if V == 3 else self.genkey_v2
- self.decipher = self.decrypt_rc4
- self.ready = True
- return
-
- # fileopen support
- def initialize_fopn_flock(self, docid, param):
- raise ADEPTError('FOPN_fLock not supported, yet ...')
- # debug mode processing
- global DEBUG_MODE
- global IVERSION
- if DEBUG_MODE == True:
- if os.access('.',os.W_OK) == True:
- debugfile = open('ineptpdf-'+IVERSION+'-debug.txt','w')
- else:
- raise ADEPTError('Cannot write debug file, current directory is not writable')
- self.is_printable = self.is_modifiable = self.is_extractable = True
- # get parameters and add it to the fo dictionary
- self.fileopen['V'] = int_value(param.get('V',2))
- # crypt base
- (docid, param) = self.encryption
- #rights = dict_value(param['Info'])
- rights = param['Info']
- #print rights
- if DEBUG_MODE == True: debugfile.write(rights + '\n\n')
-## for pair in rights.split(';'):
-## try:
-## key, value = pair.split('=',1)
-## 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
- # differentiate OS types
-## sysplatform = sys.platform
-## # if ostype is Windows
-## if sysplatform=='win32':
-## self.osuseragent = 'Windows NT 6.0'
-## self.get_macaddress = self.get_win_macaddress
-## self.fo_sethwids = self.fo_win_sethwids
-## self.BrowserCookie = WinBrowserCookie
-## elif sysplatform=='linux2':
-## adeptout = 'Linux is not supported, yet.\n'
-## raise ADEPTError(adeptout)
-## self.osuseragent = 'Linux i686'
-## self.get_macaddress = self.get_linux_macaddress
-## self.fo_sethwids = self.fo_linux_sethwids
-## else:
-## adeptout = ''
-## adeptout = adeptout + 'Due to various privacy violations from Apple\n'
-## adeptout = adeptout + 'Mac OS X support is disabled by default.'
-## raise ADEPTError(adeptout)
-## # add static arguments for http/https request
-## self.fo_setattributes()
-## # add hardware specific arguments for http/https request
-## self.fo_sethwids()
-##
-## if 'Code' in self.urlresult:
-## if self.fileopen['Length'] == len(self.urlresult['Code']):
-## self.decrypt_key = self.urlresult['Code']
-## else:
-## self.decrypt_key = self.urlresult['Code'].decode('hex')
-## else:
-## raise ADEPTError('Cannot find decryption key.')
- self.decrypt_key = 'stuff'
- self.genkey = self.genkey_v2
- self.decipher = self.decrypt_rc4
- self.ready = True
- return
-
- def initialize_fopn(self, docid, param):
- # debug mode processing
- global DEBUG_MODE
- global IVERSION
- if DEBUG_MODE == True:
- if os.access('.',os.W_OK) == True:
- debugfile = open('ineptpdf-'+IVERSION+'-debug.txt','w')
- else:
- raise ADEPTError('Cannot write debug file, current directory is not writable')
- 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)
- if DEBUG_MODE == True: debugfile.write(rights + '\n\n')
- for pair in rights.split(';'):
- try:
- key, value = pair.split('=',1)
- 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:
- # fishing some misconfigured slashs out of it
- try:
- self.fileopen[kattr[keys]] = urllib.quote(self.fileopen[keys],safe='')
- del self.fileopen[keys]
- except:
- continue
- # differentiate OS types
- sysplatform = sys.platform
- # if ostype is Windows
- if sysplatform=='win32':
- self.osuseragent = 'Windows NT 6.0'
- self.get_macaddress = self.get_win_macaddress
- self.fo_sethwids = self.fo_win_sethwids
- self.BrowserCookie = WinBrowserCookie
- elif sysplatform=='linux2':
- adeptout = 'Linux is not supported, yet.\n'
- raise ADEPTError(adeptout)
- self.osuseragent = 'Linux i686'
- self.get_macaddress = self.get_linux_macaddress
- self.fo_sethwids = self.fo_linux_sethwids
- else:
- adeptout = ''
- adeptout = adeptout + 'Mac OS X is not supported, yet.'
- adeptout = adeptout + 'Read the blogs FAQs for more information'
- raise ADEPTError(adeptout)
- # add static arguments for http/https request
- self.fo_setattributes()
- # add hardware specific arguments for http/https request
- self.fo_sethwids()
- #if DEBUG_MODE == True: debugfile.write(self.fileopen)
- if 'UURL' in self.fileopen:
- buildurl = self.fileopen['UURL']
- else:
- buildurl = self.fileopen['PURL']
- # fix for bad DPRM structure
- if self.fileopen['DPRM'][0] != r'/':
- self.fileopen['DPRM'] = r'/' + self.fileopen['DPRM']
- # genius fix for bad server urls (IMHO)
- if '?' in self.fileopen['DPRM']:
- buildurl = buildurl + self.fileopen['DPRM'] + '&'
- else:
- buildurl = buildurl + self.fileopen['DPRM'] + '?'
-
- # debug customization
- #self.fileopen['Machine'] = ''
- #self.fileopen['Disk'] = ''
-
-
- surl = ( 'Stamp', 'Mode', 'USR', 'ServiceID', 'DocumentID',\
- 'Ident3ID', 'Ident4ID','DocStrFmt', 'OSType', 'OSName', 'OSData', 'Language',\
- 'LngLCID', 'LngRFC1766', 'LngISO4Char', 'Build', 'ProdVer', 'EncrVer',\
- 'Machine', 'Disk', 'Uuid', 'PrevMach', 'PrevDisk',\
- 'FormHFT',\
- 'SelServer', 'AcroVersion', 'AcroProduct', 'AcroReader',\
- 'AcroCanEdit', 'AcroPrefIDib', 'InBrowser', 'CliAppName',\
- 'DocIsLocal', 'DocPathUrl', 'VolName', 'VolType', 'VolSN',\
- 'FSName', 'FowpKbd', 'OSBuild',\
- 'RequestSchema')
-
- #settings request and special modes
- if 'EVER' in self.fileopen and float(self.fileopen['EVER']) < 3.8:
- self.fileopen['Mode'] = 'ICx'
-
- origurl = buildurl
- buildurl = buildurl + 'Request=Setting'
- for keys in surl:
- try:
- buildurl = buildurl + '&' + keys + '=' + self.fileopen[keys]
- except:
- continue
- if DEBUG_MODE == True: debugfile.write( 'settings url:\n')
- if DEBUG_MODE == True: debugfile.write( buildurl+'\n\n')
- # custom user agent identification?
- if 'AGEN' in self.fileopen:
- useragent = self.fileopen['AGEN']
- urllib.URLopener.version = useragent
- # attribute doesn't exist - take the default user agent
- else:
- urllib.URLopener.version = self.osuseragent
- # 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!')
-## finally:
-## u.close()
- # getting rid of the line feed
- if DEBUG_MODE == True: debugfile.write('Settings'+'\n')
- if DEBUG_MODE == True: debugfile.write(result+'\n\n')
- #get rid of unnecessary characters
- result = result.rstrip('\n')
- result = result.rstrip(chr(13))
- result = result.lstrip('\n')
- result = result.lstrip(chr(13))
- self.surlresult = {}
- for pair in result.split('&'):
- try:
- key, value = pair.split('=',1)
- # fix for bad server response
- if key not in self.surlresult:
- self.surlresult[key] = value
- except:
- pass
- if 'RequestSchema' in self.surlresult:
- self.fileopen['RequestSchema'] = self.surlresult['RequestSchema']
- if 'ServerSessionData' in self.surlresult:
- self.fileopen['ServerSessionData'] = self.surlresult['ServerSessionData']
- if 'SetScope' in self.surlresult:
- self.fileopen['RequestSchema'] = self.surlresult['SetScope']
- #print self.surlresult
- if 'RetVal' in self.surlresult and 'SEMO' not in self.fileopen and(('Reason' in self.surlresult and \
- self.surlresult['Reason'] == 'AskUnp') or ('SetTarget' in self.surlresult and\
- self.surlresult['SetTarget'] == 'UnpDlg')):
- # get user and password dialog
- try:
- self.gen_pw_dialog(self.surlresult['UnpUiName'], self.surlresult['UnpUiPass'],\
- self.surlresult['UnpUiTitle'], self.surlresult['UnpUiOk'],\
- self.surlresult['UnpUiSunk'], self.surlresult['UnpUiComm'])
- except:
- self.gen_pw_dialog()
-
- # the fileopen check might not be always right because of strange server responses
- if 'SEMO' in self.fileopen and (self.fileopen['SEMO'] == '1'\
- or self.fileopen['SEMO'] == '2') and ('CSES' in self.fileopen and\
- self.fileopen['CSES'] != 'fileopen'):
- # get the url name for the cookie(s)
- if 'CURL' in self.fileopen:
- self.surl = self.fileopen['CURL']
- if 'CSES' in self.fileopen:
- self.cses = self.fileopen['CSES']
- elif 'PHOS' in self.fileopen:
- self.surl = self.fileopen['PHOS']
- elif 'LHOS' in self.fileopen:
- self.surl = self.fileopen['LHOS']
- else:
- raise ADEPTError('unknown Cookie name.\n Check ineptpdf forum for further assistance')
- self.pwfieldreq = 1
- # session cookie processing
- if self.fileopen['SEMO'] == '1':
- cookies = self.BrowserCookie()
- #print self.cses
- #print self.surl
- csession = cookies.getcookie(self.cses,self.surl)
- if csession != None:
- self.fileopen['Session'] = csession
- self.gui = False
- # fallback
- else:
- self.pwtk = Tkinter.Tk()
- self.pwtk.title('Ineptpdf8')
- self.pwtk.minsize(150, 0)
- infotxt1 = 'Get the session cookie key manually (Firefox step-by-step:\n'+\
- 'Start Firefox -> Tools -> Options -> Privacy -> Show Cookies\n'+\
- '-> Search for a cookie from ' + self.surl +' with the\n'+\
- 'name ' + self.cses +' and copy paste the content field in the\n'+\
- 'Session Content field. Remove possible spaces or new lines at the '+\
- 'end\n (cursor must be blinking right behind the last character)'
- self.label0 = Tkinter.Label(self.pwtk, text=infotxt1)
- self.label0.pack()
- self.label1 = Tkinter.Label(self.pwtk, text="Session Content")
- self.pwfieldreq = 0
- self.gui = True
- # user cookie processing
- elif self.fileopen['SEMO'] == '2':
- cookies = self.BrowserCookie()
- #print self.cses
- #print self.surl
- name = cookies.getcookie('name',self.surl)
- passw = cookies.getcookie('pass',self.surl)
- if name != None or passw != None:
- self.fileopen['UserName'] = urllib.quote(name)
- self.fileopen['UserPass'] = urllib.quote(passw)
- self.gui = False
- # fallback
- else:
- self.pwtk = Tkinter.Tk()
- self.pwtk.title('Ineptpdf8')
- self.pwtk.minsize(150, 0)
- self.label1 = Tkinter.Label(self.pwtk, text="Username")
- infotxt1 = 'Get the user cookie keys manually (Firefox step-by-step:\n'+\
- 'Start Firefox -> Tools -> Options -> Privacy -> Show Cookies\n'+\
- '-> Search for cookies from ' + self.surl +' with the\n'+\
- 'name name in the user field and copy paste the content field in the\n'+\
- 'username field. Do the same with the name pass in the password field).'
- self.label0 = Tkinter.Label(self.pwtk, text=infotxt1)
- self.label0.pack()
- self.pwfieldreq = 1
- self.gui = True
-## else:
-## self.pwtk = Tkinter.Tk()
-## self.pwtk.title('Ineptpdf8')
-## self.pwtk.minsize(150, 0)
-## self.pwfieldreq = 0
-## self.label1 = Tkinter.Label(self.pwtk, text="Username")
-## self.pwfieldreq = 1
-## self.gui = True
- if self.gui == True:
- 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()
- # create a password label and field
- if self.pwfieldreq == 1:
- self.label2.pack()
- self.pw_entry.pack()
- self.button.pack()
- self.pwtk.update()
- # start the event loop
- self.pwtk.mainloop()
-
- # original request
- # drive through tupple for building the permission url
- burl = ( 'Stamp', 'Mode', 'USR', 'ServiceID', 'DocumentID',\
- 'Ident3ID', 'Ident4ID','DocStrFmt', 'OSType', 'Language',\
- 'LngLCID', 'LngRFC1766', 'LngISO4Char', 'Build', 'ProdVer', 'EncrVer',\
- 'Machine', 'Disk', 'Uuid', 'PrevMach', 'PrevDisk', 'User', 'SaUser', 'SaSID',\
- # special security measures
- 'HostIsDomain', 'PhysHostname', 'LogiHostname', 'SaRefDomain',\
- 'FormHFT', 'UserName', 'UserPass', 'Session', \
- 'SelServer', 'AcroVersion', 'AcroProduct', 'AcroReader',\
- 'AcroCanEdit', 'AcroPrefIDib', 'InBrowser', 'CliAppName',\
- 'DocIsLocal', 'DocPathUrl', 'VolName', 'VolType', 'VolSN',\
- 'FSName', 'ServerSessionData', 'FowpKbd', 'OSBuild', \
- 'DocumentSessionData', 'RequestSchema')
-
- buildurl = origurl
- buildurl = buildurl + 'Request=DocPerm'
- for keys in burl:
- try:
- buildurl = buildurl + '&' + keys + '=' + self.fileopen[keys]
- except:
- continue
- if DEBUG_MODE == True: debugfile.write('1st url:'+'\n')
- if DEBUG_MODE == True: debugfile.write(buildurl+'\n\n')
- # custom user agent identification?
- if 'AGEN' in self.fileopen:
- useragent = self.fileopen['AGEN']
- urllib.URLopener.version = useragent
- # attribute doesn't exist - take the default user agent
- else:
- urllib.URLopener.version = self.osuseragent
- # 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!')
-## finally:
-## u.close()
- # getting rid of the line feed
- if DEBUG_MODE == True: debugfile.write('1st preresult'+'\n')
- if DEBUG_MODE == True: debugfile.write(result+'\n\n')
- #get rid of unnecessary characters
- result = result.rstrip('\n')
- result = result.rstrip(chr(13))
- result = result.lstrip('\n')
- result = result.lstrip(chr(13))
- self.urlresult = {}
- for pair in result.split('&'):
- try:
- key, value = pair.split('=',1)
- self.urlresult[key] = value
- except:
- pass
-## if 'RequestSchema' in self.surlresult:
-## self.fileopen['RequestSchema'] = self.urlresult['RequestSchema']
- #self.urlresult
- #result[0:8] == 'RetVal=1') or (result[0:8] == 'RetVal=2'):
- if ('RetVal' in self.urlresult and (self.urlresult['RetVal'] != '1' and \
- self.urlresult['RetVal'] != '2' and \
- self.urlresult['RetVal'] != 'Update' and \
- self.urlresult['RetVal'] != 'Answer')):
-
- if ('Reason' in self.urlresult and (self.urlresult['Reason'] == 'BadUserPwd'\
- or self.urlresult['Reason'] == 'AskUnp')) or ('SwitchTo' in self.urlresult\
- and (self.urlresult['SwitchTo'] == 'Dialog')):
- if 'ServerSessionData' in self.urlresult:
- self.fileopen['ServerSessionData'] = self.urlresult['ServerSessionData']
- if 'DocumentSessionData' in self.urlresult:
- self.fileopen['DocumentSessionData'] = self.urlresult['DocumentSessionData']
- buildurl = origurl
- buildurl = buildurl + 'Request=DocPerm'
- self.gen_pw_dialog()
- # password not found - fallback
- for keys in burl:
- try:
- buildurl = buildurl + '&' + keys + '=' + self.fileopen[keys]
- except:
- continue
- if DEBUG_MODE == True: debugfile.write( '2ndurl:')
- if DEBUG_MODE == True: debugfile.write( buildurl+'\n\n')
- # 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!')
- # getting rid of the line feed
- if DEBUG_MODE == True: debugfile.write( '2nd preresult')
- if DEBUG_MODE == True: debugfile.write( result+'\n\n')
- #get rid of unnecessary characters
- result = result.rstrip('\n')
- result = result.rstrip(chr(13))
- result = result.lstrip('\n')
- result = result.lstrip(chr(13))
- self.urlresult = {}
- for pair in result.split('&'):
- try:
- key, value = pair.split('=',1)
- self.urlresult[key] = value
- except:
- pass
- # did it work?
- if ('RetVal' in self.urlresult and (self.urlresult['RetVal'] != '1' and \
- self.urlresult['RetVal'] != '2' and
- self.urlresult['RetVal'] != 'Update' and \
- self.urlresult['RetVal'] != 'Answer')):
- raise ADEPTError('Decryption was not successfull.\nReason: ' + self.urlresult['Error'])
- # fix for non-standard-conform fileopen pdfs
-## if self.fileopen['Length'] != 5 and self.fileopen['Length'] != 16:
-## if self.fileopen['V'] == 1:
-## self.fileopen['Length'] = 5
-## else:
-## self.fileopen['Length'] = 16
- # patch for malformed pdfs
- #print len(self.urlresult['Code'])
- #print self.urlresult['Code'].encode('hex')
- if 'code' in self.urlresult:
- self.urlresult['Code'] = self.urlresult['code']
- if 'Code' in self.urlresult:
- if len(self.urlresult['Code']) == 5 or len(self.urlresult['Code']) == 16:
- self.decrypt_key = self.urlresult['Code']
- else:
- self.decrypt_key = self.urlresult['Code'].decode('hex')
- else:
- raise ADEPTError('Cannot find decryption key.')
- self.genkey = self.genkey_v2
- self.decipher = self.decrypt_rc4
- self.ready = True
- return
-
- def gen_pw_dialog(self, Username='Username', Password='Password', Title='User/Password Authentication',\
- OK='Proceed', Text1='Authorization', Text2='Enter Required Data'):
- self.pwtk = Tkinter.Tk()
- self.pwtk.title(Title)
- self.pwtk.minsize(150, 0)
- self.label1 = Tkinter.Label(self.pwtk, text=Text1)
- self.label2 = Tkinter.Label(self.pwtk, text=Text2)
- self.label3 = Tkinter.Label(self.pwtk, text=Username)
- self.pwfieldreq = 1
- self.gui = True
- self.un_entry = Tkinter.Entry(self.pwtk)
- # cursor here
- self.un_entry.focus()
- self.label4 = Tkinter.Label(self.pwtk, text=Password)
- self.pw_entry = Tkinter.Entry(self.pwtk, show="*")
- self.button = Tkinter.Button(self.pwtk, text=OK, command=self.fo_save_values)
- # widget layout, stack vertical
- self.label1.pack()
- self.label2.pack()
- self.label3.pack()
- self.un_entry.pack()
- # create a password label and field
- if self.pwfieldreq == 1:
- self.label4.pack()
- self.pw_entry.pack()
- self.button.pack()
- self.pwtk.update()
- # start the event loop
- self.pwtk.mainloop()
-
- # genkey functions
- def genkey_v2(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 genkey_v3(self, objid, genno):
- objid = struct.pack('<L', objid ^ 0x3569ac)
- genno = struct.pack('<L', genno ^ 0xca96)
- key = self.decrypt_key
- key += objid[0] + genno[0] + objid[1] + genno[1] + objid[2] + 'sAlT'
- hash = hashlib.md5(key)
- key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
- return key
-
- # aes v2 and v4 algorithm
- def genkey_v4(self, objid, genno):
- objid = struct.pack('<L', objid)[:3]
- genno = struct.pack('<L', genno)[:2]
- key = self.decrypt_key + objid + genno + 'sAlT'
- hash = hashlib.md5(key)
- key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
- return key
-
- def decrypt_aes(self, objid, genno, data):
- key = self.genkey(objid, genno)
- ivector = data[:16]
- data = data[16:]
- plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
- # remove pkcs#5 aes padding
- cutter = -1 * ord(plaintext[-1])
- #print cutter
- plaintext = plaintext[:cutter]
- return plaintext
-
- def decrypt_aes256(self, objid, genno, data):
- key = self.genkey(objid, genno)
- ivector = data[:16]
- data = data[16:]
- plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
- # remove pkcs#5 aes padding
- cutter = -1 * ord(plaintext[-1])
- #print cutter
- plaintext = plaintext[:cutter]
- return plaintext
-
- def decrypt_rc4(self, objid, genno, data):
- key = self.genkey(objid, genno)
- return ARC4.new(key).decrypt(data)
-
- # fileopen user/password dialog
- def fo_save_values(self):
- getout = 0
- username = 0
- password = 0
- username = self.un_entry.get()
- if self.pwfieldreq == 1:
- password = self.pw_entry.get()
- un_length = len(username)
- if self.pwfieldreq == 1:
- pw_length = len(password)
- if (un_length != 0):
- if self.pwfieldreq == 1:
- if (pw_length != 0):
- getout = 1
- else:
- getout = 1
- if getout == 1:
- if 'SEMO' in self.fileopen and self.fileopen['SEMO'] == '1':
- self.fileopen['Session'] = urllib.quote(username)
- else:
- self.fileopen['UserName'] = urllib.quote(username)
- if self.pwfieldreq == 1:
- self.fileopen['UserPass'] = urllib.quote(password)
- else:
- pass
- #self.fileopen['UserPass'] = self.fileopen['UserName']
- # doesn't always close the password window, who
- # knows why (Tkinter secrets ;=))
- self.pwtk.quit()
-
-
- def fo_setattributes(self):
- self.fileopen['Request']='DocPerm'
- self.fileopen['Mode']='CNR'
- self.fileopen['DocStrFmt']='ASCII'
- self.fileopen['Language']='ENU'
- self.fileopen['LngLCID']='ENU'
- self.fileopen['LngRFC1766']='en'
- self.fileopen['LngISO4Char']='en-us'
- self.fileopen['ProdVer']='1.8.7.9'
- self.fileopen['FormHFT']='Yes'
- self.fileopen['SelServer']='Yes'
- self.fileopen['AcroCanEdit']='Yes'
- self.fileopen['AcroPrefIDib']='Yes'
- self.fileopen['InBrowser']='Unk'
- self.fileopen['CliAppName']=''
- self.fileopen['DocIsLocal']='Yes'
- self.fileopen['FowpKbd']='Yes'
- self.fileopen['RequestSchema']='Default'
-
- # get nic mac address
- def get_linux_macaddress(self):
- try:
- for line in os.popen("/sbin/ifconfig"):
- if line.find('Ether') > -1:
- mac = line.split()[4]
- break
- return mac.replace(':','')
- except:
- raise ADEPTError('Cannot find MAC address. Get forum help.')
-
- def get_win_macaddress(self):
- try:
- gasize = c_ulong(5000)
- p = create_string_buffer(5000)
- GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
- GetAdaptersInfo(byref(p),byref(gasize))
- return p[0x194:0x19a].encode('hex')
- except:
- raise ADEPTError('Cannot find MAC address. Get forum help.')
-
- # 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
-
- # Linux processing
- def fo_linux_sethwids(self):
- # linux specific attributes
- self.fileopen['OSType']='Linux'
- self.fileopen['AcroProduct']='AcroReader'
- self.fileopen['AcroReader']='Yes'
- self.fileopen['AcroVersion']='9.101'
- self.fileopen['FSName']='ext3'
- self.fileopen['Build']='878'
- self.fileopen['ProdVer']='1.8.5.1'
- self.fileopen['OSBuild']='2.6.33'
- # write hardware keys
- hwkey = 0
- pmac = self.get_macaddress().decode("hex");
- self.fileopen['Disk'] = self.fo_convert5to8(pmac[1:])
- # get primary used default mac address
- self.fileopen['Machine'] = self.fo_convert5to8(pmac[1:])
- # get uuid
- # check for reversed offline handler 6AB83F4Ah + AFh 6AB83F4Ah
- if 'LILA' in self.fileopen:
- pass
- if 'Ident4ID' in self.fileopen:
- self.fileopen['User'] = getpass.getuser()
- self.fileopen['SaUser'] = getpass.getuser()
- try:
- cuser = winreg.HKEY_CURRENT_USER
- FOW3_UUID = 'Software\\Fileopen'
- regkey = winreg.OpenKey(cuser, FOW3_UUID)
- userkey = winreg.QueryValueEx(regkey, 'Fowp3Uuid')[0]
-# if self.genkey_cryptmach(userkey)[0:4] != 'ec20':
- self.fileopen['Uuid'] = self.genkey_cryptmach(userkey)[4:]
-## elif self.genkey_cryptmach(userkey)[0:4] != 'ec20':
-## self.fileopen['Uuid'] = self.genkey_cryptmach(userkey,1)[4:]
-## else:
- except:
- raise ADEPTError('Cannot find FowP3Uuid file - reason might be Adobe (Reader) X.'\
- 'Read the FAQs for more information how to solve the problem.')
- else:
- 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'\
- + urllib.quote(os.path.normpath(INPUTFILEPATH))
- # clear the link
- #INPUTFILEPATH = ''
-## # get volume name (urllib quote necessairy?) urllib.quote(
-## self.fileopen['VolName'] = win32api.GetVolumeInformation("C:\\")[0]
-## # get volume serial number
-## self.fileopen['VolSN'] = str(win32api.GetVolumeInformation("C:\\")[1])
- return
-
- # Windows processing
- def fo_win_sethwids(self):
- # Windows specific attributes
- self.fileopen['OSType']='Windows'
- self.fileopen['OSName']='Vista'
- self.fileopen['OSData']='Service%20Pack%204'
- self.fileopen['AcroProduct']='Reader'
- self.fileopen['AcroReader']='Yes'
- self.fileopen['OSBuild']='7600'
- self.fileopen['AcroVersion']='9.1024'
- self.fileopen['Build']='879'
- # write hardware keys
- hwkey = 0
- # get the os type and save it in ostype
- try:
- import win32api
- import win32security
- import win32file
- import _winreg as winreg
- except:
- raise ADEPTError('PyWin Extension (Win32API module) needed.\n'+\
- 'Download from http://sourceforge.net/projects/pywin32/files/ ')
- try:
- v0 = win32api.GetVolumeInformation('C:\\')
- v1 = win32api.GetSystemInfo()[6]
- # fix for possible negative integer (Python problem)
- volserial = v0[1] & 0xffffffff
- lowcpu = v1 & 255
- highcpu = (v1 >> 8) & 255
- # changed to int
- volserial = struct.pack('<I', int(volserial))
- lowcpu = struct.pack('B', lowcpu)
- highcpu = struct.pack('B', highcpu)
- encrypteddisk = volserial + lowcpu + highcpu
- self.fileopen['Disk'] = self.fo_convert5to8(encrypteddisk)
- except:
- # no c system drive available empty disk attribute
- self.fileopen['Disk'] = ''
- # get primary used default mac address
- pmac = self.get_macaddress().decode("hex");
- self.fileopen['Machine'] = self.fo_convert5to8(pmac[1:])
- if 'LIFF' in self.fileopen:
- if 'Yes' in self.fileopen['LIFF']:
- hostname = socket.gethostname()
- self.fileopen['HostIsDomain']='Yes'
- if '1' in self.fileopen['LIFF']:
- self.fileopen['PhysHostname']= hostname
- self.fileopen['LogiHostname']= hostname
- self.fileopen['SaRefDomain']= hostname
- # default users
- self.user = win32api.GetUserName().lower()
- self.sauser = win32api.GetUserName()
- # get uuid
- # check for reversed offline handler
- if 'LILA' in self.fileopen and self.fileopen['LILA'] == 'Yes':
-## self.fileopen['User'] = win32api.GetUserName().lower()
-## self.fileopen['SaUser'] = win32api.GetUserName()
-
- # get sid / sasid
- try:
- psid = win32security.LookupAccountName("",self.sauser)[0]
- psid = win32security.ConvertSidToStringSid(psid)
- self.fileopen['SaSID'] = psid
- self.fileopen['User'] = urllib.quote(self.user)
- self.fileopen['SaUser'] = urllib.quote(self.sauser)
- # didn't work use a generic one
- except:
- self.fileopen['SaSID'] = 'S-1-5-21-1380067357-584463869-1343024091-1000'
- #if 'Ident4d' in self.fileopen or 'LILA' in self.fileopen:
- # always calculate the right uuid
- userkey = []
- try:
- cuser = winreg.HKEY_CURRENT_USER
- FOW3_UUID = 'Software\\Fileopen'
- regkey = winreg.OpenKey(cuser, FOW3_UUID)
- userkey.append(winreg.QueryValueEx(regkey, 'Fowp3Uuid')[0])
- except:
- pass
- try:
- fopath = os.environ['AppData']+'\\FileOpen\\'
- fofilename = 'Fowpmadi.txt'
- f = open(fopath+fofilename, 'rb')
- userkey.append(f.read()[0:40])
- f.close()
- except:
- pass
- if not userkey:
- raise ADEPTError('Cannot find FowP3Uuid in registry or file.\n'\
- +'Did Adobe (Reader) open the pdf file?')
- cresult = self.genkey_cryptmach(userkey)
- if cresult != False:
- self.fileopen['Uuid'] = cresult
- # kind of a long shot we'll see about it
- else:
- self.fileopen['Uuid'] = str(uuid.uuid1())
-## else:
-## self.fileopen['Uuid'] = str(uuid.uuid1())
- # get time stamp
- self.fileopen['Stamp'] = str(time.time())[:-3]
- # get fileopen input pdf name + path
- # print INPUTFILEPATH
- self.fileopen['DocPathUrl'] = 'file%3a%2f%2f%2f'\
- + urllib.quote(INPUTFILEPATH)
- # determine voltype
- voltype = ('Unknown', 'Invalid', 'Removable', 'Fixed', 'Remote', 'CDRom', 'RamDisk')
- dletter = os.path.splitdrive(INPUTFILEPATH)[0] + '\\'
- self.fileopen['VolType'] = voltype[win32file.GetDriveType(dletter)]
- # get volume name (urllib quote necessairy?) urllib.quote(
- self.fileopen['VolName'] = urllib.quote(win32api.GetVolumeInformation(dletter)[0])
- # get volume serial number (fix for possible negative numbers)
- self.fileopen['VolSN'] = str(win32api.GetVolumeInformation(dletter)[1])
- # no c volume so skip it
- self.fileopen['FSName'] = win32api.GetVolumeInformation(dletter)[4]
- # get previous mac address or disk handling
- userkey = []
- try:
- cuser = winreg.HKEY_CURRENT_USER
- FOW3_UUID = 'Software\\Fileopen'
- regkey = winreg.OpenKey(cuser, FOW3_UUID)
- userkey.append(winreg.QueryValueEx(regkey, 'Fowp3Madi')[0])
- except:
- pass
- try:
- fopath = os.environ['AppData']+'\\FileOpen\\'
- fofilename = 'Fowpmadi.txt'
- f = open(fopath+fofilename, 'rb')
- userkey.append(f.read()[40:])
- f.close()
- except:
- pass
- if not userkey:
- raise ADEPTError('Cannot find FowP3Madi in registry or file.\n'\
- +'Did Adobe Reader open the pdf file?')
- cresult = self.genkey_cryptmach(userkey)
- if cresult != False:
- machdisk = self.genkey_cryptmach(userkey)
- machine = machdisk[:8]
- disk = machdisk[8:]
- # did not find the required information, false it
- else:
- machdisk = False
- machine = False
- disk = False
- if machine != self.fileopen['Machine'] and machdisk != False:
- self.fileopen['PrevMach'] = machine
- if disk != self.fileopen['Disk'] and machdisk != False:
- self.fileopen['PrevDisk'] = disk
- 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)
-
- def genkey_cryptmach(self, data):
- # nested subfunction
- def genkeysub(uname, mode=False):
- key_string = '37A4DA49DE82064939A60B1D8D7B5F0F8873B6D93E'.decode('hex')
- m = hashlib.md5()
- m.update(key_string[:3])
- m.update(uname[:13]) # max 13 characters 13 - sizeof(username)
- if (13 - len(uname)) > 0 and mode == True:
- m.update(key_string[:(13-len(uname))])
- md5sum = m.digest()[0:16]
- # print md5sum.encode('hex')
- # normal ident4id calculation
- retval = []
- for sdata in data:
- retval.append(ARC4.new(md5sum).decrypt(sdata))
- for rval in retval:
- if rval[:4] == 'ec20':
- return rval[4:]
- return False
- # start normal execution
- # list for username variants
- unamevars = []
- # fill username variants list
- unamevars.append(self.user)
- unamevars.append(self.user + chr(0))
- unamevars.append(self.user.lower())
- unamevars.append(self.user.lower() + chr(0))
- unamevars.append(self.user.upper())
- unamevars.append(self.user.upper() + chr(0))
- # go through it
- for uname in unamevars:
- result = genkeysub(uname, True)
- if result != False:
- return result
- result = genkeysub(uname)
- if result != False:
- return result
- # didn't find it, return false
- return False
-## raise ADEPTError('Unsupported Ident4D Decryption,\n'+\
-## 'report the bug to the ineptpdf script forum')
-
- KEYWORD_OBJ = PSKeywordTable.intern('obj')
-
- def getobj(self, objid):
- if not self.ready:
- raise PDFException('PDFDocument not initialized')
- #assert self.xrefs
- if objid in self.objs:
- genno = 0
- obj = self.objs[objid]
- else:
- for xref in self.xrefs:
- try:
- (stmid, index) = xref.getpos(objid)
- break
- except KeyError:
- pass
- else:
- #if STRICT:
- # raise PDFSyntaxError('Cannot locate objid=%r' % objid)
- return None
- if stmid:
- if gen_xref_stm:
- return PDFObjStmRef(objid, stmid, index)
-# Stuff from pdfminer: extract objects from object stream
- stream = stream_value(self.getobj(stmid))
- if stream.dic.get('Type') is not LITERAL_OBJSTM:
- if STRICT:
- raise PDFSyntaxError('Not a stream object: %r' % stream)
- try:
- n = stream.dic['N']
- except KeyError:
- if STRICT:
- raise PDFSyntaxError('N is not defined: %r' % stream)
- n = 0
-
- if stmid in self.parsed_objs:
- objs = self.parsed_objs[stmid]
- else:
- parser = PDFObjStrmParser(stream.get_data(), self)
- objs = []
- try:
- while 1:
- (_,obj) = parser.nextobject()
- objs.append(obj)
- except PSEOF:
- pass
- self.parsed_objs[stmid] = objs
- genno = 0
- i = n*2+index
- try:
- obj = objs[i]
- except IndexError:
- raise PDFSyntaxError('Invalid object number: objid=%r' % (objid))
- if isinstance(obj, PDFStream):
- obj.set_objid(objid, 0)
-###
- else:
- self.parser.seek(index)
- (_,objid1) = self.parser.nexttoken() # objid
- (_,genno) = self.parser.nexttoken() # genno
- #assert objid1 == objid, (objid, objid1)
- (_,kwd) = self.parser.nexttoken()
- # #### hack around malformed pdf files
- # assert objid1 == objid, (objid, objid1)
-## if objid1 != objid:
-## x = []
-## while kwd is not self.KEYWORD_OBJ:
-## (_,kwd) = self.parser.nexttoken()
-## x.append(kwd)
-## if x:
-## objid1 = x[-2]
-## genno = x[-1]
-##
- if kwd is not self.KEYWORD_OBJ:
- raise PDFSyntaxError(
- 'Invalid object spec: offset=%r' % index)
- (_,obj) = self.parser.nextobject()
- if isinstance(obj, PDFStream):
- obj.set_objid(objid, genno)
- if self.decipher:
- obj = decipher_all(self.decipher, objid, genno, obj)
- self.objs[objid] = obj
- return obj
-
-# helper class for cookie retrival
-class WinBrowserCookie():
- def __init__(self):
- pass
- def getcookie(self, cname, chost):
- # check firefox db
- fprofile = os.environ['AppData']+r'\Mozilla\Firefox'
- pinifile = 'profiles.ini'
- fini = os.path.normpath(fprofile + '\\' + pinifile)
- try:
- with open(fini,'r') as ffini:
- firefoxini = ffini.read()
- # Firefox not installed or on an USB stick
- except:
- return None
- for pair in firefoxini.split('\n'):
- try:
- key, value = pair.split('=',1)
- if key == 'Path':
- fprofile = os.path.normpath(fprofile+'//'+value+'//'+'cookies.sqlite')
- break
- # asdf
- except:
- continue
- if os.path.isfile(fprofile):
- try:
- con = sqlite3.connect(fprofile,1)
- except:
- raise ADEPTError('Firefox Cookie data base locked. Close Firefox and try again')
- cur = con.cursor()
- try:
- cur.execute("select value from moz_cookies where name=? and host=?", (cname, chost))
- except Exception:
- raise ADEPTError('Firefox Cookie database is locked. Close Firefox and try again')
- try:
- return cur.fetchone()[0]
- except Exception:
- # sometimes is a dot in front of the host
- chost = '.'+chost
- cur.execute("select value from moz_cookies where name=? and host=?", (cname, chost))
- try:
- return cur.fetchone()[0]
- except:
- return None
-
-class PDFObjStmRef(object):
- maxindex = 0
- def __init__(self, objid, stmid, index):
- self.objid = objid
- self.stmid = stmid
- self.index = index
- if index > PDFObjStmRef.maxindex:
- PDFObjStmRef.maxindex = index
-
-
-## PDFParser
-##
-class PDFParser(PSStackParser):
-
- def __init__(self, doc, fp):
- PSStackParser.__init__(self, fp)
- self.doc = doc
- self.doc.set_parser(self)
- return
-
- def __repr__(self):
- return '<PDFParser>'
-
- KEYWORD_R = PSKeywordTable.intern('R')
- KEYWORD_ENDOBJ = PSKeywordTable.intern('endobj')
- KEYWORD_STREAM = PSKeywordTable.intern('stream')
- KEYWORD_XREF = PSKeywordTable.intern('xref')
- KEYWORD_STARTXREF = PSKeywordTable.intern('startxref')
- def do_keyword(self, pos, token):
- if token in (self.KEYWORD_XREF, self.KEYWORD_STARTXREF):
- self.add_results(*self.pop(1))
- return
- if token is self.KEYWORD_ENDOBJ:
- self.add_results(*self.pop(4))
- return
-
- if token is self.KEYWORD_R:
- # reference to indirect object
- try:
- ((_,objid), (_,genno)) = self.pop(2)
- (objid, genno) = (int(objid), int(genno))
- obj = PDFObjRef(self.doc, objid, genno)
- self.push((pos, obj))
- except PSSyntaxError:
- pass
- return
-
- if token is self.KEYWORD_STREAM:
- # stream object
- ((_,dic),) = self.pop(1)
- dic = dict_value(dic)
- try:
- objlen = int_value(dic['Length'])
- except KeyError:
- if STRICT:
- raise PDFSyntaxError('/Length is undefined: %r' % dic)
- objlen = 0
- self.seek(pos)
- try:
- (_, line) = self.nextline() # 'stream'
- except PSEOF:
- if STRICT:
- raise PDFSyntaxError('Unexpected EOF')
- return
- pos += len(line)
- self.fp.seek(pos)
- data = self.fp.read(objlen)
- self.seek(pos+objlen)
- while 1:
- try:
- (linepos, line) = self.nextline()
- except PSEOF:
- if STRICT:
- raise PDFSyntaxError('Unexpected EOF')
- break
- if 'endstream' in line:
- i = line.index('endstream')
- objlen += i
- data += line[:i]
- break
- objlen += len(line)
- data += line
- self.seek(pos+objlen)
- obj = PDFStream(dic, data, self.doc.decipher)
- self.push((pos, obj))
- return
-
- # others
- self.push((pos, token))
- return
-
- def find_xref(self):
- # search the last xref table by scanning the file backwards.
- prev = None
- for line in self.revreadlines():
- line = line.strip()
- if line == 'startxref': break
- if line:
- prev = line
- else:
- raise PDFNoValidXRef('Unexpected EOF')
- return int(prev)
-
- # read xref table
- def read_xref_from(self, start, xrefs):
- self.seek(start)
- self.reset()
- try:
- (pos, token) = self.nexttoken()
- except PSEOF:
- raise PDFNoValidXRef('Unexpected EOF')
- if isinstance(token, int):
- # XRefStream: PDF-1.5
- if GEN_XREF_STM == 1:
- global gen_xref_stm
- gen_xref_stm = True
- self.seek(pos)
- self.reset()
- xref = PDFXRefStream()
- xref.load(self)
- else:
- if token is not self.KEYWORD_XREF:
- raise PDFNoValidXRef('xref not found: pos=%d, token=%r' %
- (pos, token))
- self.nextline()
- xref = PDFXRef()
- xref.load(self)
- xrefs.append(xref)
- trailer = xref.trailer
- if 'XRefStm' in trailer:
- pos = int_value(trailer['XRefStm'])
- self.read_xref_from(pos, xrefs)
- if 'Prev' in trailer:
- # find previous xref
- pos = int_value(trailer['Prev'])
- self.read_xref_from(pos, xrefs)
- return
-
- # read xref tables and trailers
- def read_xref(self):
- xrefs = []
- trailerpos = None
- try:
- pos = self.find_xref()
- self.read_xref_from(pos, xrefs)
- except PDFNoValidXRef:
- # fallback
- self.seek(0)
- pat = re.compile(r'^(\d+)\s+(\d+)\s+obj\b')
- offsets = {}
- xref = PDFXRef()
- while 1:
- try:
- (pos, line) = self.nextline()
- except PSEOF:
- break
- if line.startswith('trailer'):
- trailerpos = pos # remember last trailer
- m = pat.match(line)
- if not m: continue
- (objid, genno) = m.groups()
- offsets[int(objid)] = (0, pos)
- if not offsets: raise
- xref.offsets = offsets
- if trailerpos:
- self.seek(trailerpos)
- xref.load_trailer(self)
- xrefs.append(xref)
- return xrefs
-
-## PDFObjStrmParser
-##
-class PDFObjStrmParser(PDFParser):
-
- def __init__(self, data, doc):
- PSStackParser.__init__(self, StringIO(data))
- self.doc = doc
- return
-
- def flush(self):
- self.add_results(*self.popall())
- return
-
- KEYWORD_R = KWD('R')
- def do_keyword(self, pos, token):
- if token is self.KEYWORD_R:
- # reference to indirect object
- try:
- ((_,objid), (_,genno)) = self.pop(2)
- (objid, genno) = (int(objid), int(genno))
- obj = PDFObjRef(self.doc, objid, genno)
- self.push((pos, obj))
- except PSSyntaxError:
- pass
- return
- # others
- self.push((pos, token))
- return
-
-###
-### My own code, for which there is none else to blame
-
-class PDFSerializer(object):
- def __init__(self, inf, keypath):
- global GEN_XREF_STM, gen_xref_stm
- gen_xref_stm = GEN_XREF_STM > 1
- self.version = inf.read(8)
- inf.seek(0)
- self.doc = doc = PDFDocument()
- parser = PDFParser(doc, inf)
- doc.initialize(keypath)
- self.objids = objids = set()
- for xref in reversed(doc.xrefs):
- trailer = xref.trailer
- for objid in xref.objids():
- objids.add(objid)
- trailer = dict(trailer)
- trailer.pop('Prev', None)
- trailer.pop('XRefStm', None)
- if 'Encrypt' in trailer:
- objids.remove(trailer.pop('Encrypt').objid)
- self.trailer = trailer
-
- def dump(self, outf):
- self.outf = outf
- self.write(self.version)
- self.write('\n%\xe2\xe3\xcf\xd3\n')
- doc = self.doc
- objids = self.objids
- xrefs = {}
- maxobj = max(objids)
- trailer = dict(self.trailer)
- trailer['Size'] = maxobj + 1
- for objid in objids:
- obj = doc.getobj(objid)
- if isinstance(obj, PDFObjStmRef):
- xrefs[objid] = obj
- continue
- if obj is not None:
- try:
- genno = obj.genno
- except AttributeError:
- genno = 0
- xrefs[objid] = (self.tell(), genno)
- self.serialize_indirect(objid, obj)
- startxref = self.tell()
-
- if not gen_xref_stm:
- self.write('xref\n')
- self.write('0 %d\n' % (maxobj + 1,))
- for objid in xrange(0, maxobj + 1):
- if objid in xrefs:
- # force the genno to be 0
- self.write("%010d 00000 n \n" % xrefs[objid][0])
- else:
- self.write("%010d %05d f \n" % (0, 65535))
-
- self.write('trailer\n')
- self.serialize_object(trailer)
- self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
-
- else: # Generate crossref stream.
-
- # Calculate size of entries
- maxoffset = max(startxref, maxobj)
- maxindex = PDFObjStmRef.maxindex
- fl2 = 2
- power = 65536
- while maxoffset >= power:
- fl2 += 1
- power *= 256
- fl3 = 1
- power = 256
- while maxindex >= power:
- fl3 += 1
- power *= 256
-
- index = []
- first = None
- prev = None
- data = []
- # Put the xrefstream's reference in itself
- startxref = self.tell()
- maxobj += 1
- xrefs[maxobj] = (startxref, 0)
- for objid in sorted(xrefs):
- if first is None:
- first = objid
- elif objid != prev + 1:
- index.extend((first, prev - first + 1))
- first = objid
- prev = objid
- objref = xrefs[objid]
- if isinstance(objref, PDFObjStmRef):
- f1 = 2
- f2 = objref.stmid
- f3 = objref.index
- else:
- f1 = 1
- f2 = objref[0]
- # we force all generation numbers to be 0
- # f3 = objref[1]
- f3 = 0
-
- data.append(struct.pack('>B', f1))
- data.append(struct.pack('>L', f2)[-fl2:])
- data.append(struct.pack('>L', f3)[-fl3:])
- index.extend((first, prev - first + 1))
- data = zlib.compress(''.join(data))
- dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index,
- 'W': [1, fl2, fl3], 'Length': len(data),
- 'Filter': LITERALS_FLATE_DECODE[0],
- 'Root': trailer['Root'],}
- if 'Info' in trailer:
- dic['Info'] = trailer['Info']
- xrefstm = PDFStream(dic, data)
- self.serialize_indirect(maxobj, xrefstm)
- self.write('startxref\n%d\n%%%%EOF' % startxref)
- def write(self, data):
- self.outf.write(data)
- self.last = data[-1:]
-
- def tell(self):
- return self.outf.tell()
-
- def escape_string(self, string):
- string = string.replace('\\', '\\\\')
- string = string.replace('\n', r'\n')
- string = string.replace('(', r'\(')
- string = string.replace(')', r'\)')
- # get rid of ciando id
- regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}')
- if regularexp.match(string): return ('http://www.ciando.com')
- return string
-
- def serialize_object(self, obj):
- if isinstance(obj, dict):
- # Correct malformed Mac OS resource forks for Stanza
- if 'ResFork' in obj and 'Type' in obj and 'Subtype' not in obj \
- and isinstance(obj['Type'], int):
- obj['Subtype'] = obj['Type']
- del obj['Type']
- # end - hope this doesn't have bad effects
- self.write('<<')
- for key, val in obj.items():
- self.write('/%s' % key)
- self.serialize_object(val)
- self.write('>>')
- elif isinstance(obj, list):
- self.write('[')
- for val in obj:
- self.serialize_object(val)
- self.write(']')
- elif isinstance(obj, str):
- self.write('(%s)' % self.escape_string(obj))
- elif isinstance(obj, bool):
- if self.last.isalnum():
- self.write(' ')
- self.write(str(obj).lower())
- elif isinstance(obj, (int, long, float)):
- if self.last.isalnum():
- self.write(' ')
- self.write(str(obj))
- elif isinstance(obj, PDFObjRef):
- if self.last.isalnum():
- self.write(' ')
- self.write('%d %d R' % (obj.objid, 0))
- elif isinstance(obj, PDFStream):
- ### If we don't generate cross ref streams the object streams
- ### are no longer useful, as we have extracted all objects from
- ### them. Therefore leave them out from the output.
- if obj.dic.get('Type') == LITERAL_OBJSTM and not gen_xref_stm:
- self.write('(deleted)')
- else:
- data = obj.get_decdata()
- self.serialize_object(obj.dic)
- self.write('stream\n')
- self.write(data)
- self.write('\nendstream')
- else:
- data = str(obj)
- if data[0].isalnum() and self.last.isalnum():
- self.write(' ')
- self.write(data)
-
- def serialize_indirect(self, objid, obj):
- self.write('%d 0 obj' % (objid,))
- self.serialize_object(obj)
- if self.last.isalnum():
- self.write('\n')
- self.write('endobj\n')
-
-def cli_main(argv=sys.argv):
- progname = os.path.basename(argv[0])
- if RSA is None:
- print "%s: This script requires PyCrypto, which must be installed " \
- "separately. Read the top-of-script comment for details." % \
- (progname,)
- return 1
- if len(argv) != 4:
- print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
- return 1
- keypath, inpath, outpath = argv[1:]
- with open(inpath, 'rb') as inf:
- serializer = PDFSerializer(inf, keypath)
- # hope this will fix the 'bad file descriptor' problem
- with open(outpath, 'wb') as outf:
- # help construct to make sure the method runs to the end
- serializer.dump(outf)
- return 0
-
-
-class DecryptionDialog(Tkinter.Frame):
- def __init__(self, root):
- # debug mode debugging
- global DEBUG_MODE
- Tkinter.Frame.__init__(self, root, border=5)
- ltext='Select file for decryption\n(Ignore Password / Key file option for Fileopen/APS PDFs)'
- self.status = Tkinter.Label(self, text=ltext)
- self.status.pack(fill=Tkconstants.X, expand=1)
- body = Tkinter.Frame(self)
- body.pack(fill=Tkconstants.X, expand=1)
- sticky = Tkconstants.E + Tkconstants.W
- body.grid_columnconfigure(1, weight=2)
- Tkinter.Label(body, text='Password\nor Key file').grid(row=0)
- self.keypath = Tkinter.Entry(body, width=30)
- self.keypath.grid(row=0, column=1, sticky=sticky)
- if os.path.exists('adeptkey.der'):
- self.keypath.insert(0, 'adeptkey.der')
- button = Tkinter.Button(body, text="...", command=self.get_keypath)
- button.grid(row=0, column=2)
- Tkinter.Label(body, text='Input file').grid(row=1)
- self.inpath = Tkinter.Entry(body, width=30)
- self.inpath.grid(row=1, column=1, sticky=sticky)
- button = Tkinter.Button(body, text="...", command=self.get_inpath)
- button.grid(row=1, column=2)
- Tkinter.Label(body, text='Output file').grid(row=2)
- self.outpath = Tkinter.Entry(body, width=30)
- self.outpath.grid(row=2, column=1, sticky=sticky)
- debugmode = Tkinter.Checkbutton(self, text = "Debug Mode (writable directory required)", command=self.debug_toggle, height=2, \
- width = 40)
- debugmode.pack()
- button = Tkinter.Button(body, text="...", command=self.get_outpath)
- button.grid(row=2, column=2)
- buttons = Tkinter.Frame(self)
- buttons.pack()
-
-
- botton = Tkinter.Button(
- buttons, text="Decrypt", width=10, command=self.decrypt)
- botton.pack(side=Tkconstants.LEFT)
- Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
- button = Tkinter.Button(
- buttons, text="Quit", width=10, command=self.quit)
- button.pack(side=Tkconstants.RIGHT)
-
-
- def get_keypath(self):
- keypath = tkFileDialog.askopenfilename(
- parent=None, title='Select ADEPT key file',
- defaultextension='.der', filetypes=[('DER-encoded files', '.der'),
- ('All Files', '.*')])
- if keypath:
- keypath = os.path.normpath(os.path.realpath(keypath))
- self.keypath.delete(0, Tkconstants.END)
- self.keypath.insert(0, keypath)
- return
-
- def get_inpath(self):
- inpath = tkFileDialog.askopenfilename(
- parent=None, title='Select ADEPT or FileOpen-encrypted PDF file to decrypt',
- defaultextension='.pdf', filetypes=[('PDF files', '.pdf'),
- ('All files', '.*')])
- if inpath:
- inpath = os.path.normpath(os.path.realpath(inpath))
- self.inpath.delete(0, Tkconstants.END)
- self.inpath.insert(0, inpath)
- return
-
- def debug_toggle(self):
- global DEBUG_MODE
- if DEBUG_MODE == False:
- DEBUG_MODE = True
- else:
- DEBUG_MODE = False
-
- def get_outpath(self):
- outpath = tkFileDialog.asksaveasfilename(
- parent=None, title='Select unencrypted PDF file to produce',
- defaultextension='.pdf', filetypes=[('PDF files', '.pdf'),
- ('All files', '.*')])
- if outpath:
- outpath = os.path.normpath(os.path.realpath(outpath))
- self.outpath.delete(0, Tkconstants.END)
- self.outpath.insert(0, outpath)
- return
-
- def decrypt(self):
- global INPUTFILEPATH
- global KEYFILEPATH
- global PASSWORD
- keypath = self.keypath.get()
- inpath = self.inpath.get()
- outpath = self.outpath.get()
- if not keypath or not os.path.exists(keypath):
- # keyfile doesn't exist
- KEYFILEPATH = False
- PASSWORD = keypath
- if not inpath or not os.path.exists(inpath):
- self.status['text'] = 'Specified input file does not exist'
- return
- if not outpath:
- self.status['text'] = 'Output file not specified'
- return
- if inpath == outpath:
- self.status['text'] = 'Must have different input and output files'
- return
- # patch for non-ascii characters
- INPUTFILEPATH = inpath.encode('utf-8')
- argv = [sys.argv[0], keypath, inpath, outpath]
- self.status['text'] = 'Processing ...'
- try:
- cli_main(argv)
- except Exception, a:
- self.status['text'] = 'Error: ' + str(a)
- return
- self.status['text'] = 'File successfully decrypted.\n'+\
- 'Close this window or decrypt another pdf file.'
- return
-
-def gui_main():
- root = Tkinter.Tk()
- if RSA is None:
- root.withdraw()
- tkMessageBox.showerror(
- "INEPT PDF and FileOpen Decrypter",
- "This script requires PyCrypto, which must be installed "
- "separately. Read the top-of-script comment for details.")
- return 1
- root.title('INEPT PDF Decrypter 8.4.51 (FileOpen/APS-Support)')
- root.resizable(True, False)
- root.minsize(370, 0)
- DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
- root.mainloop()
- return 0
-
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- sys.exit(cli_main())
- sys.exit(gui_main())
diff --git a/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51_ReadMe.txt b/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51_ReadMe.txt
deleted file mode 100644
index 0d2e401..0000000
--- a/Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51_ReadMe.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-ineptpdf 8.4.51
----------------
-
-This is a version of the ineptpdf script produced by TetraChroma that can remove, on Windows, "FileOpen" DRM.
-
-No support for this script is offered at Apprentice Alf's blog.
-
-Trtrachroma's blog is http://tetrachroma.wordpress.com/