From 00ac669f76f5e92265cd5ddcceccb1973e30b601 Mon Sep 17 00:00:00 2001 From: Apprentice Alf Date: Thu, 6 Jan 2011 07:10:38 +0000 Subject: [PATCH] tools v3.1 --- Adobe_EPUB_Tools/README_ineptepub.txt | 18 +- Adobe_EPUB_Tools/ineptepub.pyw | 8 +- Adobe_EPUB_Tools/ineptkey.pyw | 5 +- Adobe_PDF_Tools/README_ineptpdf.txt | 18 + Adobe_PDF_Tools/ineptkey.pyw | 5 +- Adobe_PDF_Tools/ineptpdf.pyw | 8 +- .../README_ignoble_epub.txt | 6 +- Barnes_and_Noble_EPUB_Tools/ignobleepub.pyw | 12 +- Barnes_and_Noble_EPUB_Tools/ignoblekeygen.pyw | 11 +- .../K4MobiDeDRM_plugin/kgenpids.py | 16 +- .../K4MobiDeDRM_plugin/topazextract.py | 6 +- Calibre_Plugins/README-Ineptpdf-plugin.txt | 3 + Calibre_Plugins/README-K4MobiDeDRM-plugin.txt | 2 + .../README-eReaderPDB2PML-plugin.txt | 2 + Calibre_Plugins/README-ignobleepub-plugin.txt | 2 + Calibre_Plugins/README-ineptepub-plugin.txt | 3 + Calibre_Plugins/eReaderPDB2PML_plugin.zip | Bin 13129 -> 13225 bytes .../eReaderPDB2PML_plugin.py | 3 +- .../eReaderPDB2PML_plugin/erdr2pml.py | 60 +- Calibre_Plugins/ignobleepub_plugin.zip | Bin 6502 -> 6544 bytes .../ignobleepub_plugin/ignobleepub_plugin.py | 8 +- Calibre_Plugins/ineptepub_plugin.zip | Bin 10855 -> 10901 bytes Calibre_Plugins/ineptepub_plugin/ade_key.py | 2 +- .../ineptepub_plugin/ineptepub_plugin.py | 9 +- Calibre_Plugins/ineptpdf_plugin.zip | Bin 21812 -> 21834 bytes Calibre_Plugins/ineptpdf_plugin/ade_key.py | 2 +- .../ineptpdf_plugin/ineptpdf_plugin.py | 7 +- Calibre_Plugins/k4mobidedrm_plugin.zip | Bin 42817 -> 43219 bytes .../k4mobidedrm_plugin/k4mobidedrm_plugin.py | 7 +- .../k4mobidedrm_plugin/k4mutils.py | 18 +- .../k4mobidedrm_plugin/k4pcutils.py | 9 +- .../k4mobidedrm_plugin/mobidedrm.py | 83 +- DeDRM_Macintosh_Application/DeDRM.app.txt | 68 +- .../DeDRM.app/Contents/Info.plist | 12 +- .../Contents/Resources/Scripts/main.scpt | Bin 203536 -> 205564 bytes .../DeDRM.app/Contents/Resources/erdr2pml.py | 58 +- .../Contents/Resources/ignobleepub.pyw | 12 +- .../Contents/Resources/ignoblekeygen.pyw | 11 +- .../Contents/Resources/ineptepub.pyw | 8 +- .../DeDRM.app/Contents/Resources/ineptkey.pyw | 5 +- .../DeDRM.app/Contents/Resources/ineptpdf.pyw | 2221 +++++++++++++++++ .../Contents/Resources/k4mobidedrm.py | 3 +- .../DeDRM.app/Contents/Resources/k4mutils.py | 18 +- .../DeDRM.app/Contents/Resources/k4pcutils.py | 14 +- .../DeDRM.app/Contents/Resources/kgenpids.py | 16 +- .../DeDRM.app/Contents/Resources/mobidedrm.py | 83 +- .../Contents/Resources/topazextract.py | 6 +- KindleBooks_Tools/KindleBooks/KindleBooks.pyw | 2 + .../KindleBooks/lib/k4mobidedrm.py | 7 +- KindleBooks_Tools/KindleBooks/lib/k4mutils.py | 18 +- .../KindleBooks/lib/k4pcutils.py | 9 +- KindleBooks_Tools/KindleBooks/lib/kgenpids.py | 16 +- .../KindleBooks/lib/mobidedrm.py | 83 +- .../KindleBooks/lib/topazextract.py | 6 +- .../Kindle_4_Mac_Unswindle/lib/mobidedrm.py | 295 ++- .../Kindle_4_PC_Unswindle/mobidedrm.py | 295 ++- KindleBooks_Tools/MobiDeDRM.py | 57 +- Mobi_Additional_Tools/FindTopazEbooks.pyw | 216 ++ Mobi_Additional_Tools/KindlePID.pyw | 2 + Mobi_Additional_Tools/Kindleizer.pyw | 2 + Mobi_Additional_Tools/MobiDeDRM.pyw | 199 ++ Mobi_Additional_Tools/lib/kindlepid.py | 2 +- Mobi_Additional_Tools/lib/mobidedrm.py | 295 ++- ReadMe_First.txt | 96 + ePub_Fixer/README_ePub_Fixer.txt | 2 +- ePub_Fixer/ePub_Fixer.pyw | 2 + eReader_PDB_Tools/Pml2HTML.pyw | 2 + eReader_PDB_Tools/eReaderPDB2PML.pyw | 2 + eReader_PDB_Tools/eReaderPDB2PMLZ.pyw | 2 + .../lib/eReaderPDB2PML_plugin.py | 1 - eReader_PDB_Tools/lib/erdr2pml.py | 60 +- 71 files changed, 3915 insertions(+), 624 deletions(-) create mode 100644 Adobe_PDF_Tools/README_ineptpdf.txt create mode 100644 DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptpdf.pyw create mode 100644 Mobi_Additional_Tools/FindTopazEbooks.pyw create mode 100644 Mobi_Additional_Tools/MobiDeDRM.pyw create mode 100644 ReadMe_First.txt diff --git a/Adobe_EPUB_Tools/README_ineptepub.txt b/Adobe_EPUB_Tools/README_ineptepub.txt index d764357..25813a4 100644 --- a/Adobe_EPUB_Tools/README_ineptepub.txt +++ b/Adobe_EPUB_Tools/README_ineptepub.txt @@ -1,6 +1,6 @@ From Apprentice Alf's Blog -Adobe Adept ePub and PDF, .epub, .pdf +Adobe Adept ePub, .epub This directory includes modified versions of the I♥CABBAGES Adobe Adept inept scripts for epubs. These scripts have been modified to work with OpenSSL on Windows as well as Linux and Mac OS X. His original scripts can be found in the clearly labelled folder. If a Windows User has OpenSSL installed, these scripts will make use of it in place of PyCrypto. @@ -11,20 +11,8 @@ http://i-u2665-cabbages.blogspot.com/2009_02_01_archive.html There are two scripts: -The first is called ineptkey_v5.1.pyw. Simply double-click to launch it and it will create a key file that is needed later to actually remove the DRM. This script need only be run once unless you change your ADE account information. +The first is called ineptkey_vX.X.pyw. Simply double-click to launch it and it will create a key file that is needed later to actually remove the DRM. This script need only be run once unless you change your ADE account information. -The second is called in ineptepub_v5.3.pyw. Simply double-click to launch it. It will ask for your previously generated key file and the path to the book you want to remove the DRM from. +The second is called in ineptepub_vX.X.pyw. Simply double-click to launch it. It will ask for your previously generated key file and the path to the book you want to remove the DRM from. Both of these scripts are gui python programs. Python 2.X (32 bit) is already installed in Mac OSX. We recommend ActiveState's Active Python Version 2.X (32 bit) for Windows users. - -The latest version of ineptpdf to use is version 8.4.42, which improves support for some PDF files. - -ineptpdf version 8.4.42 can be found here: - -http://pastebin.com/kuKMXXsC - -It is not included in the tools archive. - -If that link is down, please check out the following website for some of the latest releases of these tools: - -http://ainept.freewebspace.com/ diff --git a/Adobe_EPUB_Tools/ineptepub.pyw b/Adobe_EPUB_Tools/ineptepub.pyw index 442c37a..9d95720 100644 --- a/Adobe_EPUB_Tools/ineptepub.pyw +++ b/Adobe_EPUB_Tools/ineptepub.pyw @@ -1,7 +1,7 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -# ineptepub.pyw, version 5.4 +# ineptepub.pyw, version 5.5 # Copyright © 2009-2010 i♥cabbages # Released under the terms of the GNU General Public Licence, version 3 or @@ -26,6 +26,7 @@ # 5.2 - Fix ctypes error causing segfaults on some systems # 5.3 - add support for OpenSSL on Windows, fix bug with some versions of libcrypto 0.9.8 prior to path level o # 5.4 - add support for encoding to 'utf-8' when building up list of files to decrypt from encryption.xml +# 5.5 - On Windows try PyCrypto first, OpenSSL next """ Decrypt Adobe ADEPT-encrypted EPUB books. @@ -259,7 +260,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): AES = RSA = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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, RSA = loader() break diff --git a/Adobe_EPUB_Tools/ineptkey.pyw b/Adobe_EPUB_Tools/ineptkey.pyw index bd66e78..fd90508 100644 --- a/Adobe_EPUB_Tools/ineptkey.pyw +++ b/Adobe_EPUB_Tools/ineptkey.pyw @@ -1,7 +1,7 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -# ineptkey.pyw, version 5 +# ineptkey.pyw, version 5.3 # Copyright © 2009-2010 i♥cabbages # Released under the terms of the GNU General Public Licence, version 3 or @@ -32,6 +32,7 @@ # 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 """ Retrieve Adobe ADEPT user key. @@ -115,7 +116,7 @@ if sys.platform.startswith('win'): def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto): try: AES = loader() break diff --git a/Adobe_PDF_Tools/README_ineptpdf.txt b/Adobe_PDF_Tools/README_ineptpdf.txt new file mode 100644 index 0000000..2b03d83 --- /dev/null +++ b/Adobe_PDF_Tools/README_ineptpdf.txt @@ -0,0 +1,18 @@ +From Apprentice Alf's Blog + +Adobe Adept PDF, .pdf + +This directory includes modified versions of the I♥CABBAGES Adobe Adept inept scripts for pdfs. These scripts have been modified to work with OpenSSL on Windows as well as Linux and Mac OS X. If a Windows User has OpenSSL installed, these scripts will make use of it in place of PyCrypto. + +The wonderful I♥CABBAGES has produced scripts that will remove the DRM from ePubs and PDFs encryped with Adobe’s DRM. These scripts require installation of the PyCrypto python package *or* the OpenSSL library on Windows. For Mac OS X and Linux boxes, these scripts use the already installed OpenSSL libcrypto so there is no additional requirements for these platforms. + +For more info, see the author's blog: +http://i-u2665-cabbages.blogspot.com/2009_02_01_archive.html + +There are two scripts: + +The first is called ineptkey_vX.X.pyw. Simply double-click to launch it and it will create a key file that is needed later to actually remove the DRM. This script need only be run once unless you change your ADE account information. + +The second is called in ineptpdf_vX.X.pyw. Simply double-click to launch it. It will ask for your previously generated key file and the path to the book you want to remove the DRM from. + +Both of these scripts are gui python programs. Python 2.X (32 bit) is already installed in Mac OSX. We recommend ActiveState's Active Python Version 2.X (32 bit) for Windows users. diff --git a/Adobe_PDF_Tools/ineptkey.pyw b/Adobe_PDF_Tools/ineptkey.pyw index bd66e78..fd90508 100644 --- a/Adobe_PDF_Tools/ineptkey.pyw +++ b/Adobe_PDF_Tools/ineptkey.pyw @@ -1,7 +1,7 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -# ineptkey.pyw, version 5 +# ineptkey.pyw, version 5.3 # Copyright © 2009-2010 i♥cabbages # Released under the terms of the GNU General Public Licence, version 3 or @@ -32,6 +32,7 @@ # 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 """ Retrieve Adobe ADEPT user key. @@ -115,7 +116,7 @@ if sys.platform.startswith('win'): def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto): try: AES = loader() break diff --git a/Adobe_PDF_Tools/ineptpdf.pyw b/Adobe_PDF_Tools/ineptpdf.pyw index b896f01..d73e069 100644 --- a/Adobe_PDF_Tools/ineptpdf.pyw +++ b/Adobe_PDF_Tools/ineptpdf.pyw @@ -1,5 +1,5 @@ #! /usr/bin/env python -# ineptpdf.pyw, version 7.6 +# ineptpdf.pyw, version 7.7 # To run this program install Python 2.6 from http://www.python.org/download/ # and OpenSSL (already installed on Mac OS X and Linux) OR @@ -29,6 +29,7 @@ # implemented ARC4 interface to OpenSSL # fixed minor typos # 7.6 - backported AES and other fixes from version 8.4.48 +# 7.7 - On Windows try PyCrypto first and OpenSSL next """ Decrypts Adobe ADEPT-encrypted PDF files. @@ -319,7 +320,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): ARC4 = RSA = AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + cryptolist = (_load_crypto_libcrypto, _load_crypto_pycrypto) + if sys.platform.startswith('win'): + cryptolist = (_load_crypto_pycrypto, _load_crypto_libcrypto) + for loader in cryptolist: try: ARC4, RSA, AES = loader() break diff --git a/Barnes_and_Noble_EPUB_Tools/README_ignoble_epub.txt b/Barnes_and_Noble_EPUB_Tools/README_ignoble_epub.txt index afcbfe3..ae07006 100644 --- a/Barnes_and_Noble_EPUB_Tools/README_ignoble_epub.txt +++ b/Barnes_and_Noble_EPUB_Tools/README_ignoble_epub.txt @@ -5,13 +5,13 @@ Barnes and Noble EPUB ebooks use a form of Social DRM which requires information For more info, see the author's blog: http://i-u2665-cabbages.blogspot.com/2009_12_01_archive.html -The original scripts by IHeartCabbages are available here as well. These scripts have been modified to allow the use of OpenSSL in place of PyCrypto to make them easier to run on Linux and Mac OS X. +The original scripts by IHeartCabbages are available here as well. These scripts have been modified to allow the use of OpenSSL in place of PyCrypto to make them easier to run on Linux and Mac OS X, as well as to fix some minor bugs/ There are 2 scripts: -The first is ignoblekeygen_v2.pyw. Double-click to launch it and provide the required information, and this program will generate a key file needed to remove the DRM from the books. This key file need only be generated once unless either you change your credit card number or your name on the credit card (or if you use a different credit card to purchase your book). +The first is ignoblekeygen_vX.X.pyw. Double-click to launch it and provide the required information, and this program will generate a key file needed to remove the DRM from the books. This key file need only be generated once unless either you change your credit card number or your name on the credit card (or if you use a different credit card to purchase your book). -The second is ignobleepub_v3.pyw. Double-click it and it will ask for your key file and the path to the book to remove the DRM from. +The second is ignobleepub_vX.X.pyw. Double-click it and it will ask for your key file and the path to the book to remove the DRM from. All of these scripts are gui python programs. Python 2.X (32 bit) is already installed in Mac OSX. We recommend ActiveState's Active Python Version 2.X (32 bit) for Windows users. diff --git a/Barnes_and_Noble_EPUB_Tools/ignobleepub.pyw b/Barnes_and_Noble_EPUB_Tools/ignobleepub.pyw index 469713a..0afc2bc 100644 --- a/Barnes_and_Noble_EPUB_Tools/ignobleepub.pyw +++ b/Barnes_and_Noble_EPUB_Tools/ignobleepub.pyw @@ -1,6 +1,6 @@ #! /usr/bin/python -# ignobleepub.pyw, version 3 +# ignobleepub.pyw, version 3.3 # To run this program install Python 2.6 from # and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto @@ -12,6 +12,9 @@ # 2 - Added OS X support by using OpenSSL when available # 3 - screen out improper key lengths to prevent segfaults on Linux # 3.1 - Allow Windows versions of libcrypto to be found +# 3.2 - add support for encoding to 'utf-8' when building up list of files to cecrypt from encryption.xml +# 3.3 - On Windows try PyCrypto first and OpenSSL next + from __future__ import with_statement @@ -105,15 +108,18 @@ def _load_crypto_pycrypto(): def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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() +AES = _load_crypto() diff --git a/Barnes_and_Noble_EPUB_Tools/ignoblekeygen.pyw b/Barnes_and_Noble_EPUB_Tools/ignoblekeygen.pyw index 479c11d..b2607ea 100644 --- a/Barnes_and_Noble_EPUB_Tools/ignoblekeygen.pyw +++ b/Barnes_and_Noble_EPUB_Tools/ignoblekeygen.pyw @@ -1,6 +1,6 @@ #! /usr/bin/python -# ignoblekeygen.pyw, version 2 +# ignoblekeygen.pyw, version 2.2 # To run this program install Python 2.6 from # and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto @@ -11,7 +11,7 @@ # 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 """ Generate Barnes & Noble EPUB user key from name and credit card number. """ @@ -102,11 +102,12 @@ def _load_crypto_pycrypto(): return AES - - def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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 diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py b/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py index 5c44bfa..6dcbf73 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/kgenpids.py @@ -83,7 +83,8 @@ def parseKindleInfo(kInfoFile): DB[splito[0]] =splito[1] return DB -# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). +# Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): global kindleDatabase global charMap1 @@ -95,12 +96,14 @@ def getKindleInfoValueForHash(hashedKey): cleartext = CryptUnprotectData(encryptedValue) return decode(cleartext, charMap1) -# Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the string in "key" (plaintext). +# Return the decoded and decrypted record def getKindleInfoValueForKey(key): global charMap2 return getKindleInfoValueForHash(encodeHash(key,charMap2)) -# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. +# Find if the original string for a hashed/encoded string is known. +# If so return the original string othwise return an empty string. def findNameForHash(hash): global charMap2 names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"] @@ -222,7 +225,7 @@ def pidFromSerial(s, l): # Parse the EXTH header records and use the Kindle serial number to calculate the book pid. def getKindlePid(pidlst, rec209, token, serialnum): - if rec209 != None: + if rec209 != None and token != None: # Compute book PID pidHash = SHA1(serialnum+rec209+token) bookPID = encodePID(pidHash) @@ -248,6 +251,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): kindleDatabase = parseKindleInfo(kInfoFile) except Exception, message: print(message) + kindleDatabase = None pass if kindleDatabase == None : @@ -272,8 +276,8 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): pidlst.append(devicePID) # Compute book PID - if rec209 == None: - print "\nNo EXTH record type 209 - Perhaps not a K4 file?" + if rec209 == None or token == None: + print "\nNo EXTH record type 209 or token - Perhaps not a K4 file?" return pidlst # Get the kindle account token diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py b/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py index e371d76..732bbae 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py @@ -164,9 +164,10 @@ class TopazBook: def getPIDMetaInfo(self): keysRecord = None - KeysRecordRecord = None + keysRecordRecord = None if 'keys' in self.bookMetadata: keysRecord = self.bookMetadata['keys'] + if keysRecord in self.bookMetadata: keysRecordRecord = self.bookMetadata[keysRecord] return keysRecord, keysRecordRecord @@ -395,6 +396,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 print " Creating HTML ZIP Archive" @@ -424,7 +426,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 diff --git a/Calibre_Plugins/README-Ineptpdf-plugin.txt b/Calibre_Plugins/README-Ineptpdf-plugin.txt index 13c938b..4d668fc 100644 --- a/Calibre_Plugins/README-Ineptpdf-plugin.txt +++ b/Calibre_Plugins/README-Ineptpdf-plugin.txt @@ -10,6 +10,9 @@ Installation: Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file (ineptpdf_vXX_plugin.zip) and click the 'Add' button. you're done. +Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. + + Configuration: When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to diff --git a/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt b/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt index ee9dc06..9d392f3 100644 --- a/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt +++ b/Calibre_Plugins/README-K4MobiDeDRM-plugin.txt @@ -7,6 +7,8 @@ This plugin is meant to remove the DRM from .prc, .azw, .azw1, and .tpz ebooks. Installation: Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file (K4MobiDeDRM_vXX_plugin.zip) and click the 'Add' button. You're done. +Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. + Configuration: Highlight the plugin (K4MobiDeDRM under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter a comma separated list of your 10 digit PIDs. Include in this list (again separated by commas) any 16 digit serial numbers the standalone Kindles you may have (these typically begin "B0...") This is not needed if you only want to decode "Kindle for PC" or "Kindle for Mac" books. diff --git a/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt b/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt index 75dfda5..573f8ec 100644 --- a/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt +++ b/Calibre_Plugins/README-eReaderPDB2PML-plugin.txt @@ -7,6 +7,8 @@ This plugin is meant to convert secure Ereader files (PDB) to unsecured PMLZ fil Installation: Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file (eReaderPDB2PML_vXX_plugin.zip) and click the 'Add' button. You're done. +Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. + Configuration: Highlight the plugin (eReader PDB 2 PML under the "File type plugins" category) and click the "Customize Plugin" button on Calibre's Preferences->Plugins page. Enter your name and last 8 digits of the credit card number separated by a comma: Your Name,12341234 diff --git a/Calibre_Plugins/README-ignobleepub-plugin.txt b/Calibre_Plugins/README-ignobleepub-plugin.txt index 6fd92cd..15de927 100644 --- a/Calibre_Plugins/README-ignobleepub-plugin.txt +++ b/Calibre_Plugins/README-ignobleepub-plugin.txt @@ -12,6 +12,8 @@ Installation: Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file (ignobleepub_vXX_plugin.zip) and click the 'Add' button. you're done. +Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. + Configuration: 1) The easiest way to configure the plugin is to enter your name (Barnes & Noble account name) and credit card number (the one used to purchase the books) into the plugin's customization window. It's the same info you would enter into the ignoblekeygen script. Highlight the plugin (Ignoble Epub DeDRM) and click the "Customize Plugin" button on diff --git a/Calibre_Plugins/README-ineptepub-plugin.txt b/Calibre_Plugins/README-ineptepub-plugin.txt index 3777939..56f95b8 100644 --- a/Calibre_Plugins/README-ineptepub-plugin.txt +++ b/Calibre_Plugins/README-ineptepub-plugin.txt @@ -10,6 +10,9 @@ Installation: Go to Calibre's Preferences page... click on the Plugins button. Use the file dialog button to select the plugin's zip file (ineptepub_vXX_plugin.zip) and click the 'Add' button. you're done. +Please note: Calibre does not provide any immediate feedback to indicate that adding the plugin was a success. You can always click on the File-Type plugins to see if the plugin was added. + + Configuration: When first run, the plugin will attempt to find your Adobe Digital Editions installation (on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and save it in Calibre's configuration directory. It will use that file on subsequent runs. If there are already '*.der' files in the directory, the plugin won't attempt to diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin.zip b/Calibre_Plugins/eReaderPDB2PML_plugin.zip index f496b348d84df98173373e72759935dd2586efda..7174e7236a79c1f431c4e8dc6b86909039943800 100644 GIT binary patch delta 8509 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 diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py b/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py index 405ef7c..5585cf5 100644 --- a/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py +++ b/Calibre_Plugins/eReaderPDB2PML_plugin/eReaderPDB2PML_plugin.py @@ -42,7 +42,7 @@ class eRdrDeDRM(FileTypePlugin): Credit given to The Dark Reverser for the original standalone script.' supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on author = 'DiapDealer' # The author of this plugin - version = (0, 0, 3) # The version number of this plugin + version = (0, 0, 4) # The version number of this plugin file_types = set(['pdb']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import @@ -76,7 +76,6 @@ class eRdrDeDRM(FileTypePlugin): if pmlfilepath and pmlfilepath != 1: import zipfile - import shutil print " Creating PMLZ file" myZipFile = zipfile.ZipFile(pmlzfile.name,'w',zipfile.ZIP_STORED, False) list = os.listdir(outdir) diff --git a/Calibre_Plugins/eReaderPDB2PML_plugin/erdr2pml.py b/Calibre_Plugins/eReaderPDB2PML_plugin/erdr2pml.py index f4ad3c4..ce6945d 100644 --- a/Calibre_Plugins/eReaderPDB2PML_plugin/erdr2pml.py +++ b/Calibre_Plugins/eReaderPDB2PML_plugin/erdr2pml.py @@ -56,32 +56,9 @@ # 0.15 - enabled high-ascii to pml character encoding. DropBook now works on Mac. # 0.16 - convert to use openssl DES (very very fast) or pure python DES if openssl's libcrypto is not available # 0.17 - added support for pycrypto's DES as well +# 0.18 - on Windows try PyCrypto first and OpenSSL next -Des = None - -import openssl_des -Des = openssl_des.load_libcrypto() - -# if that did not work then try pycrypto version of DES -if Des == None: - import pycrypto_des - Des = pycrypto_des.load_pycrypto() - -# if that did not work then use pure python implementation -# of DES and try to speed it up with Psycho -if Des == None: - import python_des - Des = python_des.Des - # Import Psyco if available - try: - # http://psyco.sourceforge.net - import psyco - psyco.full() - except ImportError: - pass - - -__version__='0.17' +__version__='0.18' class Unbuffered: def __init__(self, stream): @@ -97,6 +74,37 @@ sys.stdout=Unbuffered(sys.stdout) import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile +Des = None +if sys.platform.startswith('win'): + # first try with pycrypto + import pycrypto_des + Des = pycrypto_des.load_pycrypto() + if Des == None: + # they try with openssl + import openssl_des + Des = openssl_des.load_libcrypto() +else: + # first try with openssl + import openssl_des + Des = openssl_des.load_libcrypto() + if Des == None: + # then try with pycrypto + import pycrypto_des + Des = pycrypto_des.load_pycrypto() + +# if that did not work then use pure python implementation +# of DES and try to speed it up with Psycho +if Des == None: + import python_des + Des = python_des.Des + # Import Psyco if available + try: + # http://psyco.sourceforge.net + import psyco + psyco.full() + except ImportError: + pass + try: from hashlib import sha1 except ImportError: @@ -460,7 +468,7 @@ def main(argv=None): myZipFile.write(imagePath, localname) myZipFile.close() # remove temporary directory - shutil.rmtree(outdir) + shutil.rmtree(outdir, True) end_time = time.time() search_time = end_time - start_time diff --git a/Calibre_Plugins/ignobleepub_plugin.zip b/Calibre_Plugins/ignobleepub_plugin.zip index 369835303239c589095408a586584ad9413de8ab..5ef5c414857509329b3999fa3273073d77c92728 100644 GIT binary patch delta 5086 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 A0ssI2 delta 5020 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 diff --git a/Calibre_Plugins/ignobleepub_plugin/ignobleepub_plugin.py b/Calibre_Plugins/ignobleepub_plugin/ignobleepub_plugin.py index e0a8da7..1e79c26 100644 --- a/Calibre_Plugins/ignobleepub_plugin/ignobleepub_plugin.py +++ b/Calibre_Plugins/ignobleepub_plugin/ignobleepub_plugin.py @@ -45,6 +45,7 @@ # 0.1.1 - Allow Windows users to make use of openssl if they have it installed. # - Incorporated SomeUpdates zipfix routine. # 0.1.2 - bug fix for non-ascii file names in encryption.xml +# 0.1.3 - Try PyCrypto on Windows first """ Decrypt Barnes & Noble ADEPT encrypted EPUB books. @@ -169,7 +170,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): _aes = _aes2 = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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, _aes2 = loader() break @@ -267,7 +271,7 @@ class IgnobleDeDRM(FileTypePlugin): Credit given to I <3 Cabbages for the original stand-alone scripts.' supported_platforms = ['linux', 'osx', 'windows'] author = 'DiapDealer' - version = (0, 1, 2) + version = (0, 1, 3) minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions. file_types = set(['epub']) on_import = True diff --git a/Calibre_Plugins/ineptepub_plugin.zip b/Calibre_Plugins/ineptepub_plugin.zip index a0047a7d40c30616167e7589a9d472377962d7f7..4a8f8eb18f0c28007b2d56a3afc1b700a90b03f0 100644 GIT binary patch delta 7421 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 delta 7357 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 diff --git a/Calibre_Plugins/ineptepub_plugin/ade_key.py b/Calibre_Plugins/ineptepub_plugin/ade_key.py index eb9ae3d..4b743f7 100644 --- a/Calibre_Plugins/ineptepub_plugin/ade_key.py +++ b/Calibre_Plugins/ineptepub_plugin/ade_key.py @@ -79,7 +79,7 @@ if iswindows: def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto): try: AES = loader() break diff --git a/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py b/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py index da36a87..beb924e 100644 --- a/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py +++ b/Calibre_Plugins/ineptepub_plugin/ineptepub_plugin.py @@ -46,7 +46,7 @@ # 0.1.2 - Removed Carbon dependency for Mac users. Fixes an issue that was a # result of Calibre changing to python 2.7. # 0.1.3 - bug fix for epubs with non-ascii chars in file names - +# 0.1.4 - default to try PyCrypto first on Windows """ @@ -285,7 +285,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): _aes = _rsa = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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, _rsa = loader() break @@ -368,7 +371,7 @@ class IneptDeDRM(FileTypePlugin): Credit given to I <3 Cabbages for the original stand-alone scripts.' supported_platforms = ['linux', 'osx', 'windows'] author = 'DiapDealer' - version = (0, 1, 3) + version = (0, 1, 4) minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions. file_types = set(['epub']) on_import = True diff --git a/Calibre_Plugins/ineptpdf_plugin.zip b/Calibre_Plugins/ineptpdf_plugin.zip index df017d7448687e5c1ede2ef9f5273e537b13fe07..eccc1b868ce104a177592c078e2383d5046f78f4 100644 GIT binary patch delta 20899 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 F{sSb1E(ibs delta 20876 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 n00000009610HlF24gix+4iyFk00YC~9FrtfAO?0+00000afMam diff --git a/Calibre_Plugins/ineptpdf_plugin/ade_key.py b/Calibre_Plugins/ineptpdf_plugin/ade_key.py index eb9ae3d..4b743f7 100644 --- a/Calibre_Plugins/ineptpdf_plugin/ade_key.py +++ b/Calibre_Plugins/ineptpdf_plugin/ade_key.py @@ -79,7 +79,7 @@ if iswindows: def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto): try: AES = loader() break diff --git a/Calibre_Plugins/ineptpdf_plugin/ineptpdf_plugin.py b/Calibre_Plugins/ineptpdf_plugin/ineptpdf_plugin.py index c2092de..d519820 100644 --- a/Calibre_Plugins/ineptpdf_plugin/ineptpdf_plugin.py +++ b/Calibre_Plugins/ineptpdf_plugin/ineptpdf_plugin.py @@ -336,7 +336,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): ARC4 = RSA = AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + cryptolist = (_load_crypto_libcrypto, _load_crypto_pycrypto) + if sys.platform.startswith('win'): + cryptolist = (_load_crypto_pycrypto, _load_crypto_libcrypto) + for loader in cryptolist: try: ARC4, RSA, AES = loader() break @@ -2113,7 +2116,7 @@ class IneptPDFDeDRM(FileTypePlugin): Credit given to I <3 Cabbages for the original stand-alone scripts.' supported_platforms = ['linux', 'osx', 'windows'] author = 'DiapDealer' - version = (0, 1, 1) + version = (0, 1, 2) minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions. file_types = set(['pdf']) on_import = True diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip index 2a4dcee9618b6676d916ca4088c56152ffb0c557..aff65bdb16b4a64b446c3d02e58fc6eada74a995 100644 GIT binary patch delta 18541 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

S;x9@zr56gGl5x@%111oN0H0rTV%{yAB5gTk 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 delta 18097 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 UibV+i6-NkNWJzH{>HduV1tM0u)Bpeg diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py index c23b897..53567e9 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4mobidedrm_plugin.py @@ -28,7 +28,7 @@ from __future__ import with_statement -__version__ = '1.4' +__version__ = '1.9' class Unbuffered: def __init__(self, stream): @@ -163,6 +163,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 if mobi: @@ -198,7 +199,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 if __name__ == '__main__': @@ -214,7 +215,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 1, 7) # The version number of this plugin + version = (0, 1, 9) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py index 4aa14dd..1b501ba 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4mutils.py @@ -6,7 +6,7 @@ import os import subprocess -class K4MDrmException(Exception): +class DrmException(Exception): pass @@ -18,7 +18,7 @@ def _load_crypto_libcrypto(): libcrypto = find_library('crypto') if libcrypto is None: - raise K4MDrmException('libcrypto not found') + raise DrmException('libcrypto not found') libcrypto = CDLL(libcrypto) AES_MAXNR = 14 @@ -51,19 +51,19 @@ def _load_crypto_libcrypto(): def set_decrypt_key(self, userkey, iv): self._blocksize = len(userkey) if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : - raise K4MDrmException('AES improper key used') + raise DrmException('AES improper key used') return keyctx = self._keyctx = AES_KEY() self.iv = iv rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) if rv < 0: - raise K4MDrmException('Failed to initialize AES key') + raise DrmException('Failed to initialize AES key') def decrypt(self, data): out = create_string_buffer(len(data)) rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0) if rv == 0: - raise K4MDrmException('AES decryption failed') + raise DrmException('AES decryption failed') return out.raw def keyivgen(self, passwd): @@ -81,7 +81,7 @@ def _load_crypto(): LibCrypto = None try: LibCrypto = _load_crypto_libcrypto() - except (ImportError, K4MDrmException): + except (ImportError, DrmException): pass return LibCrypto @@ -185,8 +185,10 @@ def openKindleInfo(kInfoFile=None): if pp >= 0: kinfopath = resline break - if not os.path.exists(kinfopath): - raise K4MDrmException('Error: .kindle-info file can not be found') + if not os.path.isfile(kinfopath): + raise DrmException('Error: .kindle-info file can not be found') return open(kinfopath,'r') else: + if not os.path.isfile(kinfoFile): + raise DrmException('Error: kindle-info file can not be found') return open(kInfoFile, 'r') diff --git a/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py b/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py index 3f95660..efc310d 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/k4pcutils.py @@ -99,7 +99,12 @@ CryptUnprotectData = CryptUnprotectData() def openKindleInfo(kInfoFile=None): if kInfoFile == None: regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r') + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + raise DrmException('Error: kindle.info file can not be found') + return open(kinfopath,'r') else: + if not os.path.isfile(kInfoFile): + raise DrmException('Error: kindle.info file can not be found') return open(kInfoFile, 'r') diff --git a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py index cc83224..864b545 100644 --- a/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py +++ b/Calibre_Plugins/k4mobidedrm_plugin/mobidedrm.py @@ -42,8 +42,10 @@ # 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. # 0.21 - Added support for multiple pids # 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.22' +__version__ = '0.24' import sys @@ -57,6 +59,7 @@ class Unbuffered: return getattr(self.stream, attr) sys.stdout=Unbuffered(sys.stdout) +import os import struct import binascii @@ -154,8 +157,10 @@ class MobiBook: # initial sanity check on file self.data_file = file(infile, 'rb').read() self.header = self.data_file[0:78] - if self.header[0x3C:0x3C+8] != 'BOOKMOBI': + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 # build up section offset and flag info self.num_sections, = struct.unpack('>H', self.header[76:78]) @@ -168,6 +173,14 @@ class MobiBook: # parse information from section 0 self.sect = self.loadSection(0) self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) @@ -182,18 +195,23 @@ class MobiBook: # if exth region exists parse it for metadata array self.meta_array = {} - exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) - exth = '' - if exth_flag & 0x40: - exth = self.sect[16 + self.mobi_length:] - nitems, = struct.unpack('>I', exth[8:12]) - pos = 12 - for i in xrange(nitems): - type, size = struct.unpack('>II', exth[pos: pos + 8]) - content = exth[pos + 8: pos + size] - self.meta_array[type] = content - pos += size - + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + def getBookTitle(self): title = '' if 503 in self.meta_array: @@ -269,12 +287,12 @@ class MobiBook: def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." return self.data_file - if crypto_type == 1: - raise DrmException("Cannot decode Mobipocket encryption type 1") - if crypto_type != 2: + if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) goodpids = [] @@ -286,23 +304,32 @@ class MobiBook: elif len(pid)==8: goodpids.append(pid) - # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) - if drm_count == 0: - raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") - found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) - if not found_key: - raise DrmException("No key found. Most likely the correct PID has not been given.") + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : + # calculate the keys + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) + if drm_count == 0: + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) + if not found_key: + raise DrmException("No key found. Most likely the correct PID has not been given.") + # kill the drm keys + self.patchSection(0, "\0" * drm_size, drm_ptr) + # kill the drm pointers + self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) if pid=="00000000": print "File has default encryption, no specific PID." else: print "File is encoded with PID "+checksumPid(pid)+"." - # kill the drm keys - self.patchSection(0, "\0" * drm_size, drm_ptr) - # kill the drm pointers - self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) # clear the crypto type self.patchSection(0, "\0" * 2, 0xC) diff --git a/DeDRM_Macintosh_Application/DeDRM.app.txt b/DeDRM_Macintosh_Application/DeDRM.app.txt index 60d2f86..66b743f 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app.txt +++ b/DeDRM_Macintosh_Application/DeDRM.app.txt @@ -55,7 +55,7 @@ on GetTools() set AdobePDFTool to POSIX path of file (path to me as text) & "Contents/Resources/ineptpdf.pyw" set ZipFixTool to POSIX path of file (path to me as text) & "Contents/Resources/zipfix.py" set ProgressApp to POSIX path of file ((path to resource "DeDRM Progress.app") as string) - if not fileexists(eReaderTool) or not fileexists(MobipocketTool) or not fileexists(BNKeyGenTool) or not fileexists(BNePubTool) or not fileexists(AdobeKeyGenTool) or not fileexists(AdobeePubTool) or not folderexists(ProgressApp) then + if not fileexists(eReaderTool) or not fileexists(MobipocketTool) or not fileexists(BNKeyGenTool) or not fileexists(BNePubTool) or not fileexists(AdobeKeyGenTool) or not fileexists(AdobePDFTool) or not fileexists(AdobeePubTool) or not folderexists(ProgressApp) then display dialog "De-drm scripts or support files are missing from this package. Get a fresh copy." buttons {"Bother"} default button 1 with title "DeDRM Applescript" with icon stop return false end if @@ -68,7 +68,7 @@ on unlockmobifile(encryptedFile) try set BOOKMOBI to read file encryptedFile from 61 for 8 end try - if BOOKMOBI is not "BOOKMOBI" then + if BOOKMOBI is not "BOOKMOBI" and BOOKMOBI is not "TEXtREAd" then set TOPAZ to "NOT" try set TOPAZ to read file encryptedFile from 1 for 4 @@ -98,9 +98,9 @@ on unlockmobifile(encryptedFile) set shellcommand to shellcommand & " -k " & quoted form of KindleInfoPath end repeat set Serialstring to GetSerialstring() - if Serialstring is not "" then set shellcommand to shellcommand & " -s " & Serialstring + if Serialstring is not "" then set shellcommand to shellcommand & " -s " & quoted form of Serialstring set PIDstring to GetPIDstring() - if PIDstring is not "" then set shellcommand to shellcommand & " -p " & PIDstring + if PIDstring is not "" then set shellcommand to shellcommand & " -p " & quoted form of PIDstring set shellcommand to shellcommand & " " & (quoted form of encryptedFilePath) & " " & (quoted form of unlockedFileParentFolderPath) --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 try @@ -108,8 +108,34 @@ on unlockmobifile(encryptedFile) on error errmsg set shellresult to errmsg end try + --display dialog shellresult try - if (offset of "Error" in shellresult) > 0 then + repeat + if (totalebooks > 1) or (offset of "No key found" in shellresult) is 0 then + exit repeat + end if + -- ask for another PID as we're only doing one ebook + set newPID to "None" + set DialogPrompt to "Couldn't decode " & fileName & ". Do you have another PID to try?" + try + set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Try This One", "No Extra PID"} with title "DeDRM Applescript" default button 2) + if button returned of dialogresult is "Try This One" then + set newPID to text returned of dialogresult + end if + end try + if newPID is "None" or (length of newPID is not 10 and length of newPID is not 8) then + exit repeat + end if + set shellcommand to "python " & (quoted form of MobipocketTool) & " -p " & quoted form of newPID & " " & (quoted form of encryptedFilePath) & " " & (quoted form of unlockedFileParentFolderPath) + --display dialog "shellcommand: " default answer shellcommand buttons {"OK"} default button 1 giving up after 10 + try + set shellresult to do shell script shellcommand + on error errmsg + set shellresult to errmsg + end try + --display dialog shellresult + end repeat + if (offset of "Error" in shellresult) > 0 or (offset of "No key found" in shellresult) > 0 then set ErrorCount to ErrorCount + 1 set ErrorList to (ErrorList & fileName & fileExtension & " couldn't be decoded: " & shellresult as text) & " @@ -322,6 +348,7 @@ on unlockepubfile(encryptedFile) end repeat if decoded is "NO" then + set shellresult to "no keys" -- now try Adobe ePub repeat with AdeptKey in AdeptKeyList set shellcommand to "python " & (quoted form of AdobeePubTool) & " " & (quoted form of AdeptKey) & " " & (quoted form of fixedFilePath) & " " & (quoted form of unlockedFilePath) @@ -350,6 +377,11 @@ on unlockepubfile(encryptedFile) set ErrorCount to ErrorCount + 1 set ErrorList to (ErrorList & fileName & fileExtension & " couldn't be decoded: no keys. +") + else if (offset of "not an ADEPT EPUB" in shellresult) is not 0 then + set WarningCount to WarningCount + 1 + set WarningList to (WarningList & fileName & " doesn't seem to be encrypted. + ") else set ErrorCount to ErrorCount + 1 @@ -392,7 +424,7 @@ on unlockpdffile(encryptedFile) set decoded to "NO" -- first we must check we have a PDF script - GetIneptPDF(false) + --GetIneptPDF(false) if not fileexists(AdobePDFTool) then set ErrorCount to ErrorCount + 1 set ErrorList to ErrorList & encryptedFile & " is a PDF file and no ineptpdf script found. @@ -591,7 +623,7 @@ on GetPIDs() Enter any additional Mobipocket PIDs for your Mobipocket books one at a time:" set FinishedButton to "No More" end if - set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript 2/6" default button 2) + set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript 2/5" default button 2) if button returned of dialogresult is "Add" then set PID to text returned of dialogresult set PIDlength to length of PID @@ -627,7 +659,7 @@ on GetSerials() Enter any additional Kindle Serial Numbers one at a time:" set FinishedButton to "No More" end if - set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript 3/6" default button 2) + set dialogresult to (display dialog DialogPrompt default answer "" buttons {"Delete All", "Add", FinishedButton} with title "DeDRM Applescript 3/5" default button 2) if button returned of dialogresult is "Add" then set Serial to text returned of dialogresult set Seriallength to length of Serial @@ -735,7 +767,7 @@ Please enter any additional " set DialogPrompt to DialogPrompt & "eReader/Barnes & Noble Name,Number key pairs one at a time. If you're only decoding eReader files, the last 8 digits of the Number will do. The full 15 or 16 are only needed for Barnes & Noble ePubs. Only the last eight will be stored or displayed. Please separate the name and number with a comma and click \"Add\". Or to add a an already generated .b64 file, just click \"Add\" with nothing in the text field." set dialogtitle to "DeDRM Applescript" if (running) then - set dialogtitle to dialogtitle & " 4/6" + set dialogtitle to dialogtitle & " 4/5" end if set dialogresult to (display dialog DialogPrompt default answer bnKeyText buttons {"Delete All", "Add", FinishedButton} with title dialogtitle default button 2) if button returned of dialogresult is "Add" then @@ -823,7 +855,7 @@ on GetAdeptKeyFiles() To add extra key files (.der), click the AddÉ button." set FinishedButton to "No More" end if - set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "AddÉ", FinishedButton} with title "DeDRM Applescript 5/6" default button 2) + set dialogresult to (display dialog DialogPrompt buttons {"Forget All", "AddÉ", FinishedButton} with title "DeDRM Applescript 5/5" default button 2) if button returned of dialogresult is "AddÉ" then try set newFile to (choose file with prompt "Please select an Adept key file") as text @@ -1023,9 +1055,9 @@ on ReadPrefs() try set AdeptKeyList to value of property list item "AdeptKeys" of property list file preferencesFilePath end try - try - set AdobePDFTool to value of property list item "IneptPDF" of property list file preferencesFilePath - end try + --try + --set AdobePDFTool to value of property list item "IneptPDF" of property list file preferencesFilePath + --end try end tell end if set newList to {} @@ -1053,7 +1085,7 @@ on WritePrefs() make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"KindleInfoFiles", value:KindleInfoList} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"bnKeys", value:bnKeys} make new property list item at end of property list items of contents of myPrefs with properties {kind:list, name:"AdeptKeys", value:AdeptKeyList} - make new property list item at end of property list items of contents of myPrefs with properties {kind:string, name:"IneptPDF", value:AdobePDFTool} + --make new property list item at end of property list items of contents of myPrefs with properties {kind:string, name:"IneptPDF", value:AdobePDFTool} end tell end WritePrefs @@ -1103,9 +1135,9 @@ on run if GetTools() then display dialog "Drag&Drop encrypted ebooks onto this AppleScript's icon in the Finder to decode them after you have finished configuring it and it has quit. -Click the Continue button to enter any PIDs for Mobipocket ebooks; serial numbers for Kindle ebooks; name,number key pairs for Barnes & Noble/eReader ebooks; to select extra Barnes & Noble .b64 key files; to select extra Adobe Adept .der key files; and to find th optional ineptpdf.pyw script. +Click the Continue button to enter any PIDs for Mobipocket ebooks; serial numbers for Kindle ebooks; name,number key pairs for Barnes & Noble/eReader ebooks; to select extra Barnes & Noble .b64 key files; or to select extra Adobe Adept .der key files. -***You do not need to enter any extra info if decoding ebooks downloaded to your installation of Kindle for Mac, or Adobe Digital Editions. If you do not have any PIDS; serial numbers; name,number keys, .b64 or .der files to add or want to decode PDF files, just click the Cancel button.*** +***You do not need to enter any extra info if decoding ebooks downloaded to your installation of Kindle for Mac, or Adobe Digital Editions. If you do not have any PIDS; serial numbers; name,number keys, .b64 or .der files, just click the Cancel button.*** Please only use to get access to your own books. Authors, publishers and ebook stores need to make money to produce more ebooks. Don't cheat them. @@ -1117,7 +1149,7 @@ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this For more information, please refer to -" with title "DeDRM Applescript 1/6" buttons {"Cancel", "Continue"} default button 2 +" with title "DeDRM Applescript 1/5" buttons {"Cancel", "Continue"} default button 2 ReadPrefs() GetAdeptKey(true) @@ -1125,7 +1157,7 @@ For more information, please refer to GetSerials() GetKeys(true) GetAdeptKeyFiles() - GetIneptPDF(true) + --GetIneptPDF(true) --GetKindleInfoFiles() WritePrefs() end if diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist index 46ca744..0788a6e 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist @@ -24,7 +24,7 @@ CFBundleExecutable droplet CFBundleGetInfoString - DeDRM 1.4, Copyright © 2010 by Apprentice Alf. + DeDRM 1.6, Copyright © 2010–2011 by Apprentice Alf. CFBundleIconFile droplet CFBundleInfoDictionaryVersion @@ -34,11 +34,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4 - LSMinimumSystemVersion - 10.5.0 + 1.6 CFBundleSignature dplt + LSMinimumSystemVersion + 10.5.0 LSRequiresCarbon WindowState @@ -46,9 +46,9 @@ name ScriptWindowState positionOfDivider - 739 + 686 savedFrame - 1533 -24 1262 818 1440 -150 1680 1050 + 2161 -75 907 765 1440 -150 1680 1050 selectedTabView result diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/Scripts/main.scpt index 1224e93d3708a1d602f7f90de3262fadb4f9fa7e..b400b1067c64d41709838f24543a17d0b5ef3090 100644 GIT binary patch literal 205564 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

xR9h6SPc$p{yVa-x9PT$p^P+ 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 jl77CRpU>&%Gx~uFHl#kGpN| # and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto @@ -12,6 +12,9 @@ # 2 - Added OS X support by using OpenSSL when available # 3 - screen out improper key lengths to prevent segfaults on Linux # 3.1 - Allow Windows versions of libcrypto to be found +# 3.2 - add support for encoding to 'utf-8' when building up list of files to cecrypt from encryption.xml +# 3.3 - On Windows try PyCrypto first and OpenSSL next + from __future__ import with_statement @@ -105,15 +108,18 @@ def _load_crypto_pycrypto(): def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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() +AES = _load_crypto() diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeygen.pyw b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeygen.pyw index 479c11d..b2607ea 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeygen.pyw +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ignoblekeygen.pyw @@ -1,6 +1,6 @@ #! /usr/bin/python -# ignoblekeygen.pyw, version 2 +# ignoblekeygen.pyw, version 2.2 # To run this program install Python 2.6 from # and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto @@ -11,7 +11,7 @@ # 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 """ Generate Barnes & Noble EPUB user key from name and credit card number. """ @@ -102,11 +102,12 @@ def _load_crypto_pycrypto(): return AES - - def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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 diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptepub.pyw b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptepub.pyw index 442c37a..9d95720 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptepub.pyw +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptepub.pyw @@ -1,7 +1,7 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -# ineptepub.pyw, version 5.4 +# ineptepub.pyw, version 5.5 # Copyright © 2009-2010 i♥cabbages # Released under the terms of the GNU General Public Licence, version 3 or @@ -26,6 +26,7 @@ # 5.2 - Fix ctypes error causing segfaults on some systems # 5.3 - add support for OpenSSL on Windows, fix bug with some versions of libcrypto 0.9.8 prior to path level o # 5.4 - add support for encoding to 'utf-8' when building up list of files to decrypt from encryption.xml +# 5.5 - On Windows try PyCrypto first, OpenSSL next """ Decrypt Adobe ADEPT-encrypted EPUB books. @@ -259,7 +260,10 @@ def _load_crypto_pycrypto(): def _load_crypto(): AES = RSA = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + 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, RSA = loader() break diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptkey.pyw b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptkey.pyw index bd66e78..fd90508 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptkey.pyw +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptkey.pyw @@ -1,7 +1,7 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -# ineptkey.pyw, version 5 +# ineptkey.pyw, version 5.3 # Copyright © 2009-2010 i♥cabbages # Released under the terms of the GNU General Public Licence, version 3 or @@ -32,6 +32,7 @@ # 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 """ Retrieve Adobe ADEPT user key. @@ -115,7 +116,7 @@ if sys.platform.startswith('win'): def _load_crypto(): AES = None - for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto): + for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto): try: AES = loader() break diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptpdf.pyw b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptpdf.pyw new file mode 100644 index 0000000..d73e069 --- /dev/null +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/ineptpdf.pyw @@ -0,0 +1,2221 @@ +#! /usr/bin/env python +# ineptpdf.pyw, version 7.7 + +# To run this program install Python 2.6 from http://www.python.org/download/ +# and OpenSSL (already installed on Mac OS X and Linux) OR +# PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto +# (make sure to install the version for Python 2.6). Save this script file as +# ineptpdf.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 +# 5 - Automated decryption of a complete directory +# 6.1 - backward compatibility for 1.7.1 and old adeptkey.der +# 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 +# 7.2 - Correct malformed Mac OS resource forks for Stanza (anon2) +# - Support for cross ref streams on output (decreases file size) +# 7.3 - Correct bug in trailer with cross ref stream that caused the error +# "The root object is missing or invalid" in Adobe Reader. (anon2) +# 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) +# 7.5 - allow support for OpenSSL to replace pycrypto on all platforms +# implemented ARC4 interface to OpenSSL +# fixed minor typos +# 7.6 - backported AES and other fixes from version 8.4.48 +# 7.7 - On Windows try PyCrypto first and OpenSSL next + +""" +Decrypts Adobe ADEPT-encrypted 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 + +class ADEPTError(Exception): + pass + + +import hashlib + +def SHA256(message): + ctx = hashlib.sha256() + ctx.update(message) + return ctx.digest() + + +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 ADEPTError('libcrypto not found') + libcrypto = CDLL(libcrypto) + + AES_MAXNR = 14 + + RSA_NO_PADDING = 3 + + 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) + + class RC4_KEY(Structure): + _fields_ = [('x', c_int), ('y', c_int), ('box', c_int * 256)] + RC4_KEY_p = POINTER(RC4_KEY) + + class RSA(Structure): + pass + RSA_p = POINTER(RSA) + + 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]) + + RC4_set_key = F(None,'RC4_set_key',[RC4_KEY_p, c_int, c_char_p]) + RC4_crypt = F(None,'RC4',[RC4_KEY_p, c_int, c_char_p, c_char_p]) + + d2i_RSAPrivateKey = F(RSA_p, 'd2i_RSAPrivateKey', + [RSA_p, c_char_pp, c_long]) + RSA_size = F(c_int, 'RSA_size', [RSA_p]) + RSA_private_decrypt = F(c_int, 'RSA_private_decrypt', + [c_int, c_char_p, c_char_p, RSA_p, c_int]) + RSA_free = F(None, 'RSA_free', [RSA_p]) + + class RSA(object): + def __init__(self, der): + buf = create_string_buffer(der) + pp = c_char_pp(cast(buf, c_char_p)) + rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der)) + if rsa is None: + raise ADEPTError('Error parsing ADEPT user key DER') + + def decrypt(self, from_): + rsa = self._rsa + to = create_string_buffer(RSA_size(rsa)) + dlen = RSA_private_decrypt(len(from_), from_, to, rsa, + RSA_NO_PADDING) + if dlen < 0: + raise ADEPTError('RSA decryption failed') + return to[1:dlen] + + def __del__(self): + if self._rsa is not None: + RSA_free(self._rsa) + self._rsa = None + + class ARC4(object): + @classmethod + def new(cls, userkey): + self = ARC4() + self._blocksize = len(userkey) + key = self._key = RC4_KEY() + RC4_set_key(key, self._blocksize, userkey) + return self + def __init__(self): + self._blocksize = 0 + self._key = None + def decrypt(self, data): + out = create_string_buffer(len(data)) + RC4_crypt(self._key, len(data), data, out) + return out.raw + + class AES(object): + @classmethod + def new(cls, userkey, mode, iv): + self = AES() + self._blocksize = len(userkey) + # mode is ignored since CBCMODE is only thing supported/used so far + self._mode = mode + if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : + raise ADEPTError('AES improper key used') + return + keyctx = self._keyctx = AES_KEY() + self._iv = iv + rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) + if rv < 0: + raise ADEPTError('Failed to initialize AES key') + return self + def __init__(self): + self._blocksize = 0 + self._keyctx = None + self._iv = 0 + self._mode = 0 + def decrypt(self, data): + out = create_string_buffer(len(data)) + rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self._iv, 0) + if rv == 0: + raise ADEPTError('AES decryption failed') + return out.raw + + return (ARC4, RSA, AES) + + +def _load_crypto_pycrypto(): + from Crypto.PublicKey import RSA as _RSA + from Crypto.Cipher import ARC4 as _ARC4 + from Crypto.Cipher import AES as _AES + + # ASN.1 parsing code from tlslite + 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) + + class ARC4(object): + @classmethod + def new(cls, userkey): + self = ARC4() + self._arc4 = _ARC4.new(userkey) + return self + def __init__(self): + self._arc4 = None + def decrypt(self, data): + return self._arc4.decrypt(data) + + class AES(object): + @classmethod + def new(cls, userkey, mode, iv): + self = AES() + self._aes = _AES.new(userkey, mode, iv) + return self + def __init__(self): + self._aes = None + def decrypt(self, data): + return self._aes.decrypt(data) + + class RSA(object): + def __init__(self, der): + key = ASN1Parser([ord(x) for x in der]) + key = [key.getChild(x).value for x in xrange(1, 4)] + key = [self.bytesToNumber(v) for v in key] + self._rsa = _RSA.construct(key) + + def bytesToNumber(self, bytes): + total = 0L + for byte in bytes: + total = (total << 8) + byte + return total + + def decrypt(self, data): + return self._rsa.decrypt(data) + + return (ARC4, RSA, AES) + +def _load_crypto(): + ARC4 = RSA = 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: + ARC4, RSA, AES = loader() + break + except (ImportError, ADEPTError): + pass + return (ARC4, RSA, AES) +ARC4, RSA, AES = _load_crypto() + + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +# 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 + +# 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 '' % (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 '' % (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 '' % \ + (self.objid, len(self.rawdata), self.dic) + else: + return '' % \ + (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 '' % 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 '' % 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 + 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) + 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 genkey_adobe_ps(self, param): + # nice little offline principal keys dictionary + # global static principal key for German Onleihe / Bibliothek Digital + principalkeys = { 'bibliothek-digital.de': 'rRwGv2tbpKov1krvv7PO0ws9S436/lArPlfipz5Pqhw='.decode('base64')} + self.is_printable = self.is_modifiable = self.is_extractable = True + 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') + edclist = [] + for pair in edcdata.split('\n'): + edclist.append(pair) + # principal key request + for key in principalkeys: + if key in pdrllic: + principalkey = principalkeys[key] + else: + raise ADEPTError('Cannot find principal key for this pdf') + shakey = SHA256(principalkey) + ivector = 16 * chr(0) + 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 ...') + 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]) + pdrlpol = pdrlpol[:cutter] + 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('= 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.') + 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): + self.is_printable = self.is_modifiable = self.is_extractable = True + with open(password, 'rb') as f: + keyder = f.read() + rsa = RSA(keyder) + 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 + + # genkey functions + def genkey_v2(self, objid, genno): + objid = struct.pack(' 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 '' + + 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 OpenSSL or 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): + Tkinter.Frame.__init__(self, root, border=5) + ltext='Select file for decryption\n' + 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='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) + 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 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 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): + 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 + self.status['text'] = 'Specified Adept key file does not exist' + return + 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 + 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", + "This script requires OpenSSL or PyCrypto, which must be installed " + "separately. Read the top-of-script comment for details.") + return 1 + root.title('INEPT PDF Decrypter') + 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/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py index 5059fc4..f9625a6 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mobidedrm.py @@ -163,6 +163,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 if mobi: @@ -198,7 +199,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 if __name__ == '__main__': diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py index 4aa14dd..1b501ba 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4mutils.py @@ -6,7 +6,7 @@ import os import subprocess -class K4MDrmException(Exception): +class DrmException(Exception): pass @@ -18,7 +18,7 @@ def _load_crypto_libcrypto(): libcrypto = find_library('crypto') if libcrypto is None: - raise K4MDrmException('libcrypto not found') + raise DrmException('libcrypto not found') libcrypto = CDLL(libcrypto) AES_MAXNR = 14 @@ -51,19 +51,19 @@ def _load_crypto_libcrypto(): def set_decrypt_key(self, userkey, iv): self._blocksize = len(userkey) if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : - raise K4MDrmException('AES improper key used') + raise DrmException('AES improper key used') return keyctx = self._keyctx = AES_KEY() self.iv = iv rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) if rv < 0: - raise K4MDrmException('Failed to initialize AES key') + raise DrmException('Failed to initialize AES key') def decrypt(self, data): out = create_string_buffer(len(data)) rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0) if rv == 0: - raise K4MDrmException('AES decryption failed') + raise DrmException('AES decryption failed') return out.raw def keyivgen(self, passwd): @@ -81,7 +81,7 @@ def _load_crypto(): LibCrypto = None try: LibCrypto = _load_crypto_libcrypto() - except (ImportError, K4MDrmException): + except (ImportError, DrmException): pass return LibCrypto @@ -185,8 +185,10 @@ def openKindleInfo(kInfoFile=None): if pp >= 0: kinfopath = resline break - if not os.path.exists(kinfopath): - raise K4MDrmException('Error: .kindle-info file can not be found') + if not os.path.isfile(kinfopath): + raise DrmException('Error: .kindle-info file can not be found') return open(kinfopath,'r') else: + if not os.path.isfile(kinfoFile): + raise DrmException('Error: kindle-info file can not be found') return open(kInfoFile, 'r') diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py index 337b992..efc310d 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/k4pcutils.py @@ -19,17 +19,12 @@ advapi32 = windll.advapi32 crypt32 = windll.crypt32 -# # Various character maps used to decrypt books. Probably supposed to act as obfuscation -# charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789" -# -# Exceptions for all the problems that might happen during the script -# class DrmException(Exception): pass @@ -104,7 +99,12 @@ CryptUnprotectData = CryptUnprotectData() def openKindleInfo(kInfoFile=None): if kInfoFile == None: regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r') + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + raise DrmException('Error: kindle.info file can not be found') + return open(kinfopath,'r') else: + if not os.path.isfile(kInfoFile): + raise DrmException('Error: kindle.info file can not be found') return open(kInfoFile, 'r') diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py index 5c44bfa..6dcbf73 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/kgenpids.py @@ -83,7 +83,8 @@ def parseKindleInfo(kInfoFile): DB[splito[0]] =splito[1] return DB -# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). +# Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): global kindleDatabase global charMap1 @@ -95,12 +96,14 @@ def getKindleInfoValueForHash(hashedKey): cleartext = CryptUnprotectData(encryptedValue) return decode(cleartext, charMap1) -# Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the string in "key" (plaintext). +# Return the decoded and decrypted record def getKindleInfoValueForKey(key): global charMap2 return getKindleInfoValueForHash(encodeHash(key,charMap2)) -# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. +# Find if the original string for a hashed/encoded string is known. +# If so return the original string othwise return an empty string. def findNameForHash(hash): global charMap2 names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"] @@ -222,7 +225,7 @@ def pidFromSerial(s, l): # Parse the EXTH header records and use the Kindle serial number to calculate the book pid. def getKindlePid(pidlst, rec209, token, serialnum): - if rec209 != None: + if rec209 != None and token != None: # Compute book PID pidHash = SHA1(serialnum+rec209+token) bookPID = encodePID(pidHash) @@ -248,6 +251,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): kindleDatabase = parseKindleInfo(kInfoFile) except Exception, message: print(message) + kindleDatabase = None pass if kindleDatabase == None : @@ -272,8 +276,8 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): pidlst.append(devicePID) # Compute book PID - if rec209 == None: - print "\nNo EXTH record type 209 - Perhaps not a K4 file?" + if rec209 == None or token == None: + print "\nNo EXTH record type 209 or token - Perhaps not a K4 file?" return pidlst # Get the kindle account token diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py index cc83224..864b545 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/mobidedrm.py @@ -42,8 +42,10 @@ # 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. # 0.21 - Added support for multiple pids # 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.22' +__version__ = '0.24' import sys @@ -57,6 +59,7 @@ class Unbuffered: return getattr(self.stream, attr) sys.stdout=Unbuffered(sys.stdout) +import os import struct import binascii @@ -154,8 +157,10 @@ class MobiBook: # initial sanity check on file self.data_file = file(infile, 'rb').read() self.header = self.data_file[0:78] - if self.header[0x3C:0x3C+8] != 'BOOKMOBI': + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 # build up section offset and flag info self.num_sections, = struct.unpack('>H', self.header[76:78]) @@ -168,6 +173,14 @@ class MobiBook: # parse information from section 0 self.sect = self.loadSection(0) self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) @@ -182,18 +195,23 @@ class MobiBook: # if exth region exists parse it for metadata array self.meta_array = {} - exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) - exth = '' - if exth_flag & 0x40: - exth = self.sect[16 + self.mobi_length:] - nitems, = struct.unpack('>I', exth[8:12]) - pos = 12 - for i in xrange(nitems): - type, size = struct.unpack('>II', exth[pos: pos + 8]) - content = exth[pos + 8: pos + size] - self.meta_array[type] = content - pos += size - + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + def getBookTitle(self): title = '' if 503 in self.meta_array: @@ -269,12 +287,12 @@ class MobiBook: def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." return self.data_file - if crypto_type == 1: - raise DrmException("Cannot decode Mobipocket encryption type 1") - if crypto_type != 2: + if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) goodpids = [] @@ -286,23 +304,32 @@ class MobiBook: elif len(pid)==8: goodpids.append(pid) - # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) - if drm_count == 0: - raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") - found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) - if not found_key: - raise DrmException("No key found. Most likely the correct PID has not been given.") + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : + # calculate the keys + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) + if drm_count == 0: + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) + if not found_key: + raise DrmException("No key found. Most likely the correct PID has not been given.") + # kill the drm keys + self.patchSection(0, "\0" * drm_size, drm_ptr) + # kill the drm pointers + self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) if pid=="00000000": print "File has default encryption, no specific PID." else: print "File is encoded with PID "+checksumPid(pid)+"." - # kill the drm keys - self.patchSection(0, "\0" * drm_size, drm_ptr) - # kill the drm pointers - self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) # clear the crypto type self.patchSection(0, "\0" * 2, 0xC) diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py index e371d76..732bbae 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py @@ -164,9 +164,10 @@ class TopazBook: def getPIDMetaInfo(self): keysRecord = None - KeysRecordRecord = None + keysRecordRecord = None if 'keys' in self.bookMetadata: keysRecord = self.bookMetadata['keys'] + if keysRecord in self.bookMetadata: keysRecordRecord = self.bookMetadata[keysRecord] return keysRecord, keysRecordRecord @@ -395,6 +396,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 print " Creating HTML ZIP Archive" @@ -424,7 +426,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 diff --git a/KindleBooks_Tools/KindleBooks/KindleBooks.pyw b/KindleBooks_Tools/KindleBooks/KindleBooks.pyw index 222bb27..d4f37fb 100644 --- a/KindleBooks_Tools/KindleBooks/KindleBooks.pyw +++ b/KindleBooks_Tools/KindleBooks/KindleBooks.pyw @@ -110,6 +110,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py b/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py index 5059fc4..b7fa317 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4mobidedrm.py @@ -28,7 +28,7 @@ from __future__ import with_statement -__version__ = '1.4' +__version__ = '1.9' class Unbuffered: def __init__(self, stream): @@ -163,6 +163,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 if mobi: @@ -198,7 +199,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 if __name__ == '__main__': @@ -214,7 +215,7 @@ if not __name__ == "__main__" and inCalibre: Provided by the work of many including DiapDealer, SomeUpdates, IHeartCabbages, CMBDTC, Skindle, DarkReverser, ApprenticeAlf, etc.' supported_platforms = ['osx', 'windows', 'linux'] # Platforms this plugin will run on author = 'DiapDealer, SomeUpdates' # The author of this plugin - version = (0, 1, 7) # The version number of this plugin + version = (0, 1, 9) # The version number of this plugin file_types = set(['prc','mobi','azw','azw1','tpz']) # The file types that this plugin will be applied to on_import = True # Run this plugin during the import priority = 210 # run this plugin before mobidedrm, k4pcdedrm, k4dedrm diff --git a/KindleBooks_Tools/KindleBooks/lib/k4mutils.py b/KindleBooks_Tools/KindleBooks/lib/k4mutils.py index 4aa14dd..1b501ba 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4mutils.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4mutils.py @@ -6,7 +6,7 @@ import os import subprocess -class K4MDrmException(Exception): +class DrmException(Exception): pass @@ -18,7 +18,7 @@ def _load_crypto_libcrypto(): libcrypto = find_library('crypto') if libcrypto is None: - raise K4MDrmException('libcrypto not found') + raise DrmException('libcrypto not found') libcrypto = CDLL(libcrypto) AES_MAXNR = 14 @@ -51,19 +51,19 @@ def _load_crypto_libcrypto(): def set_decrypt_key(self, userkey, iv): self._blocksize = len(userkey) if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : - raise K4MDrmException('AES improper key used') + raise DrmException('AES improper key used') return keyctx = self._keyctx = AES_KEY() self.iv = iv rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) if rv < 0: - raise K4MDrmException('Failed to initialize AES key') + raise DrmException('Failed to initialize AES key') def decrypt(self, data): out = create_string_buffer(len(data)) rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0) if rv == 0: - raise K4MDrmException('AES decryption failed') + raise DrmException('AES decryption failed') return out.raw def keyivgen(self, passwd): @@ -81,7 +81,7 @@ def _load_crypto(): LibCrypto = None try: LibCrypto = _load_crypto_libcrypto() - except (ImportError, K4MDrmException): + except (ImportError, DrmException): pass return LibCrypto @@ -185,8 +185,10 @@ def openKindleInfo(kInfoFile=None): if pp >= 0: kinfopath = resline break - if not os.path.exists(kinfopath): - raise K4MDrmException('Error: .kindle-info file can not be found') + if not os.path.isfile(kinfopath): + raise DrmException('Error: .kindle-info file can not be found') return open(kinfopath,'r') else: + if not os.path.isfile(kinfoFile): + raise DrmException('Error: kindle-info file can not be found') return open(kInfoFile, 'r') diff --git a/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py b/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py index 3f95660..efc310d 100644 --- a/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py +++ b/KindleBooks_Tools/KindleBooks/lib/k4pcutils.py @@ -99,7 +99,12 @@ CryptUnprotectData = CryptUnprotectData() def openKindleInfo(kInfoFile=None): if kInfoFile == None: regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\") - path = winreg.QueryValueEx(regkey, 'Local AppData')[0] - return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r') + path = winreg.QueryValueEx(regkey, 'Local AppData')[0] + kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info' + if not os.path.isfile(kinfopath): + raise DrmException('Error: kindle.info file can not be found') + return open(kinfopath,'r') else: + if not os.path.isfile(kInfoFile): + raise DrmException('Error: kindle.info file can not be found') return open(kInfoFile, 'r') diff --git a/KindleBooks_Tools/KindleBooks/lib/kgenpids.py b/KindleBooks_Tools/KindleBooks/lib/kgenpids.py index 5c44bfa..6dcbf73 100644 --- a/KindleBooks_Tools/KindleBooks/lib/kgenpids.py +++ b/KindleBooks_Tools/KindleBooks/lib/kgenpids.py @@ -83,7 +83,8 @@ def parseKindleInfo(kInfoFile): DB[splito[0]] =splito[1] return DB -# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). +# Return the decoded and decrypted record def getKindleInfoValueForHash(hashedKey): global kindleDatabase global charMap1 @@ -95,12 +96,14 @@ def getKindleInfoValueForHash(hashedKey): cleartext = CryptUnprotectData(encryptedValue) return decode(cleartext, charMap1) -# Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record +# Get a record from the Kindle.info file for the string in "key" (plaintext). +# Return the decoded and decrypted record def getKindleInfoValueForKey(key): global charMap2 return getKindleInfoValueForHash(encodeHash(key,charMap2)) -# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. +# Find if the original string for a hashed/encoded string is known. +# If so return the original string othwise return an empty string. def findNameForHash(hash): global charMap2 names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"] @@ -222,7 +225,7 @@ def pidFromSerial(s, l): # Parse the EXTH header records and use the Kindle serial number to calculate the book pid. def getKindlePid(pidlst, rec209, token, serialnum): - if rec209 != None: + if rec209 != None and token != None: # Compute book PID pidHash = SHA1(serialnum+rec209+token) bookPID = encodePID(pidHash) @@ -248,6 +251,7 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): kindleDatabase = parseKindleInfo(kInfoFile) except Exception, message: print(message) + kindleDatabase = None pass if kindleDatabase == None : @@ -272,8 +276,8 @@ def getK4Pids(pidlst, rec209, token, kInfoFile=None): pidlst.append(devicePID) # Compute book PID - if rec209 == None: - print "\nNo EXTH record type 209 - Perhaps not a K4 file?" + if rec209 == None or token == None: + print "\nNo EXTH record type 209 or token - Perhaps not a K4 file?" return pidlst # Get the kindle account token diff --git a/KindleBooks_Tools/KindleBooks/lib/mobidedrm.py b/KindleBooks_Tools/KindleBooks/lib/mobidedrm.py index cc83224..864b545 100644 --- a/KindleBooks_Tools/KindleBooks/lib/mobidedrm.py +++ b/KindleBooks_Tools/KindleBooks/lib/mobidedrm.py @@ -42,8 +42,10 @@ # 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. # 0.21 - Added support for multiple pids # 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.22' +__version__ = '0.24' import sys @@ -57,6 +59,7 @@ class Unbuffered: return getattr(self.stream, attr) sys.stdout=Unbuffered(sys.stdout) +import os import struct import binascii @@ -154,8 +157,10 @@ class MobiBook: # initial sanity check on file self.data_file = file(infile, 'rb').read() self.header = self.data_file[0:78] - if self.header[0x3C:0x3C+8] != 'BOOKMOBI': + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 # build up section offset and flag info self.num_sections, = struct.unpack('>H', self.header[76:78]) @@ -168,6 +173,14 @@ class MobiBook: # parse information from section 0 self.sect = self.loadSection(0) self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) @@ -182,18 +195,23 @@ class MobiBook: # if exth region exists parse it for metadata array self.meta_array = {} - exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) - exth = '' - if exth_flag & 0x40: - exth = self.sect[16 + self.mobi_length:] - nitems, = struct.unpack('>I', exth[8:12]) - pos = 12 - for i in xrange(nitems): - type, size = struct.unpack('>II', exth[pos: pos + 8]) - content = exth[pos + 8: pos + size] - self.meta_array[type] = content - pos += size - + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + def getBookTitle(self): title = '' if 503 in self.meta_array: @@ -269,12 +287,12 @@ class MobiBook: def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." return self.data_file - if crypto_type == 1: - raise DrmException("Cannot decode Mobipocket encryption type 1") - if crypto_type != 2: + if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) goodpids = [] @@ -286,23 +304,32 @@ class MobiBook: elif len(pid)==8: goodpids.append(pid) - # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) - if drm_count == 0: - raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") - found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) - if not found_key: - raise DrmException("No key found. Most likely the correct PID has not been given.") + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : + # calculate the keys + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) + if drm_count == 0: + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) + if not found_key: + raise DrmException("No key found. Most likely the correct PID has not been given.") + # kill the drm keys + self.patchSection(0, "\0" * drm_size, drm_ptr) + # kill the drm pointers + self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) if pid=="00000000": print "File has default encryption, no specific PID." else: print "File is encoded with PID "+checksumPid(pid)+"." - # kill the drm keys - self.patchSection(0, "\0" * drm_size, drm_ptr) - # kill the drm pointers - self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) # clear the crypto type self.patchSection(0, "\0" * 2, 0xC) diff --git a/KindleBooks_Tools/KindleBooks/lib/topazextract.py b/KindleBooks_Tools/KindleBooks/lib/topazextract.py index e371d76..732bbae 100644 --- a/KindleBooks_Tools/KindleBooks/lib/topazextract.py +++ b/KindleBooks_Tools/KindleBooks/lib/topazextract.py @@ -164,9 +164,10 @@ class TopazBook: def getPIDMetaInfo(self): keysRecord = None - KeysRecordRecord = None + keysRecordRecord = None if 'keys' in self.bookMetadata: keysRecord = self.bookMetadata['keys'] + if keysRecord in self.bookMetadata: keysRecordRecord = self.bookMetadata[keysRecord] return keysRecord, keysRecordRecord @@ -395,6 +396,7 @@ def main(argv=sys.argv): myzip = zipfile.ZipFile(zipname,'w',zipfile.ZIP_DEFLATED, False) zipUpDir(myzip, tempdir, '') myzip.close() + shutil.rmtree(tempdir, True) return 1 print " Creating HTML ZIP Archive" @@ -424,7 +426,7 @@ def main(argv=sys.argv): zipUpDir(myzip3, tempdir, 'img') myzip3.close() - shutil.rmtree(tempdir) + shutil.rmtree(tempdir, True) return 0 diff --git a/KindleBooks_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py b/KindleBooks_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py index 183432c..864b545 100644 --- a/KindleBooks_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py +++ b/KindleBooks_Tools/Kindle_4_Mac_Unswindle/lib/mobidedrm.py @@ -24,7 +24,7 @@ # 0.14 - Working out when the extra data flags are present has been problematic # Versions 7 through 9 have tried to tweak the conditions, but have been # only partially successful. Closer examination of lots of sample -# files reveals that a confusin has arisen because trailing data entries +# files reveals that a confusion has arisen because trailing data entries # are not encrypted, but it turns out that the multibyte entries # in utf8 file are encrypted. (Although neither kind gets compressed.) # This knowledge leads to a simplification of the test for the @@ -39,13 +39,15 @@ # Removed the disabled Calibre plug-in code # Permit use of 8-digit PIDs # 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. -# 0.20 - Corretion: It seems that multibyte entries are encrypted in a v6 file. +# 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. +# 0.21 - Added support for multiple pids +# 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.20' +__version__ = '0.24' import sys -import struct -import binascii class Unbuffered: def __init__(self, stream): @@ -55,10 +57,20 @@ class Unbuffered: self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) +sys.stdout=Unbuffered(sys.stdout) + +import os +import struct +import binascii class DrmException(Exception): pass + +# +# MobiBook Utility Routines +# + # Implementation of Pukall Cipher 1 def PC1(key, src, decryption=True): sum1 = 0; @@ -70,7 +82,6 @@ def PC1(key, src, decryption=True): wkey = [] for i in xrange(8): wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) - dst = "" for i in xrange(len(src)): temp1 = 0; @@ -131,7 +142,9 @@ def getSizeOfTrailingDataEntries(ptr, size, flags): num += (ord(ptr[size - num - 1]) & 0x3) + 1 return num -class DrmStripper: + + +class MobiBook: def loadSection(self, section): if (section + 1 == self.num_sections): endoff = len(self.data_file) @@ -140,6 +153,93 @@ class DrmStripper: off = self.sections[section][0] return self.data_file[off:endoff] + def __init__(self, infile): + # initial sanity check on file + self.data_file = file(infile, 'rb').read() + self.header = self.data_file[0:78] + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': + raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 + + # build up section offset and flag info + self.num_sections, = struct.unpack('>H', self.header[76:78]) + self.sections = [] + for i in xrange(self.num_sections): + offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8]) + flags, val = a1, a2<<16|a3<<8|a4 + self.sections.append( (offset, flags, val) ) + + # parse information from section 0 + self.sect = self.loadSection(0) + self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return + self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) + print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) + self.extra_data_flags = 0 + if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5): + self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4]) + print "Extra Data Flags = %d" % self.extra_data_flags + if self.mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below + # so clear that byte so that we leave it to be decrypted. + self.extra_data_flags &= 0xFFFE + + # if exth region exists parse it for metadata array + self.meta_array = {} + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + + def getBookTitle(self): + title = '' + if 503 in self.meta_array: + title = self.meta_array[503] + else : + toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) + tend = toff + tlen + title = self.sect[toff:tend] + if title == '': + title = self.header[:32] + title = title.split("\0")[0] + return title + + def getPIDMetaInfo(self): + rec209 = None + token = None + if 209 in self.meta_array: + rec209 = self.meta_array[209] + data = rec209 + # Parse the 209 data to find the the exth record with the token data. + # The last character of the 209 data points to the record with the token. + # Always 208 from my experience, but I'll leave the logic in case that changes. + for i in xrange(len(data)): + if ord(data[i]) != 0: + if self.meta_array[ord(data[i])] != None: + token = self.meta_array[ord(data[i])] + return rec209, token + def patch(self, off, new): self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):] @@ -152,134 +252,131 @@ class DrmStripper: assert off + in_off + len(new) <= endoff self.patch(off + in_off, new) - def parseDRM(self, data, count, pid): - pid = pid.ljust(16,'\0') - keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" - temp_key = PC1(keyvec1, pid, False) - temp_key_sum = sum(map(ord,temp_key)) & 0xff + def parseDRM(self, data, count, pidlist): found_key = None - for i in xrange(count): - verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1: - found_key = finalkey + keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" + for pid in pidlist: + bigpid = pid.ljust(16,'\0') + temp_key = PC1(keyvec1, bigpid, False) + temp_key_sum = sum(map(ord,temp_key)) & 0xff + found_key = None + for i in xrange(count): + verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver and (flags & 0x1F) == 1: + found_key = finalkey + break + if found_key != None: break if not found_key: # Then try the default encoding that doesn't require a PID + pid = "00000000" temp_key = keyvec1 temp_key_sum = sum(map(ord,temp_key)) & 0xff for i in xrange(count): verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum: - found_key = finalkey - break - return found_key + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver: + found_key = finalkey + break + return [found_key,pid] - def __init__(self, data_file, pid): - if len(pid)==10: - if checksumPid(pid[0:-2]) != pid: - raise DrmException("invalid PID checksum") - pid = pid[0:-2] - elif len(pid)==8: - print "PID without checksum given. With checksum PID is "+checksumPid(pid) - else: - raise DrmException("Invalid PID length") - - self.data_file = data_file - header = data_file[0:72] - if header[0x3C:0x3C+8] != 'BOOKMOBI': - raise DrmException("invalid file format") - self.num_sections, = struct.unpack('>H', data_file[76:78]) - - self.sections = [] - for i in xrange(self.num_sections): - offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8]) - flags, val = a1, a2<<16|a3<<8|a4 - self.sections.append( (offset, flags, val) ) - - sect = self.loadSection(0) - records, = struct.unpack('>H', sect[0x8:0x8+2]) - mobi_length, = struct.unpack('>L',sect[0x14:0x18]) - mobi_version, = struct.unpack('>L',sect[0x68:0x6C]) - extra_data_flags = 0 - print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length) - if (mobi_length >= 0xE4) and (mobi_version >= 5): - extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) - print "Extra Data Flags = %d" %extra_data_flags - if mobi_version < 7: - # multibyte utf8 data is included in the encryption for mobi_version 6 and below - # so clear that byte so that we leave it to be decrypted. - extra_data_flags &= 0xFFFE - - crypto_type, = struct.unpack('>H', sect[0xC:0xC+2]) + def processBook(self, pidlist): + crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." - else: - if crypto_type == 1: - raise DrmException("cannot decode Mobipocket encryption type 1") - if crypto_type != 2: - raise DrmException("unknown encryption type: %d" % crypto_type) + return self.data_file + if crypto_type != 2 and crypto_type != 1: + raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + goodpids = [] + for pid in pidlist: + if len(pid)==10: + if checksumPid(pid[0:-2]) != pid: + print "Warning: PID " + pid + " has incorrect checksum, should have been "+checksumPid(pid[0:-2]) + goodpids.append(pid[0:-2]) + elif len(pid)==8: + goodpids.append(pid) + + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16]) + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) if drm_count == 0: - raise DrmException("no PIDs found in this file") - found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid) + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) if not found_key: - raise DrmException("no key found. maybe the PID is incorrect") - + raise DrmException("No key found. Most likely the correct PID has not been given.") # kill the drm keys self.patchSection(0, "\0" * drm_size, drm_ptr) # kill the drm pointers self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) - # clear the crypto type - self.patchSection(0, "\0" * 2, 0xC) + + if pid=="00000000": + print "File has default encryption, no specific PID." + else: + print "File is encoded with PID "+checksumPid(pid)+"." - # decrypt sections - print "Decrypting. Please wait . . .", - new_data = self.data_file[:self.sections[1][0]] - for i in xrange(1, records+1): - data = self.loadSection(i) - extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags) - if i%100 == 0: - print ".", - # print "record %d, extra_size %d" %(i,extra_size) - new_data += PC1(found_key, data[0:len(data) - extra_size]) - if extra_size > 0: - new_data += data[-extra_size:] - #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size])) - if self.num_sections > records+1: - new_data += self.data_file[self.sections[records+1][0]:] - self.data_file = new_data - print "done" + # clear the crypto type + self.patchSection(0, "\0" * 2, 0xC) - def getResult(self): + # decrypt sections + print "Decrypting. Please wait . . .", + new_data = self.data_file[:self.sections[1][0]] + for i in xrange(1, self.records+1): + data = self.loadSection(i) + extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags) + if i%100 == 0: + print ".", + # print "record %d, extra_size %d" %(i,extra_size) + new_data += PC1(found_key, data[0:len(data) - extra_size]) + if extra_size > 0: + new_data += data[-extra_size:] + if self.num_sections > self.records+1: + new_data += self.data_file[self.sections[self.records+1][0]:] + self.data_file = new_data + print "done" return self.data_file def getUnencryptedBook(infile,pid): - sys.stdout=Unbuffered(sys.stdout) - data_file = file(infile, 'rb').read() - strippedFile = DrmStripper(data_file, pid) - return strippedFile.getResult() + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook([pid]) + +def getUnencryptedBookWithList(infile,pidlist): + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook(pidlist) def main(argv=sys.argv): - sys.stdout=Unbuffered(sys.stdout) print ('MobiDeDrm v%(__version__)s. ' 'Copyright 2008-2010 The Dark Reverser.' % globals()) if len(argv)<4: print "Removes protection from Mobipocket books" print "Usage:" - print " %s " % sys.argv[0] + print " %s " % sys.argv[0] return 1 else: infile = argv[1] outfile = argv[2] - pid = argv[3] + pidlist = argv[3].split(',') try: - stripped_file = getUnencryptedBook(infile, pid) + stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) except DrmException, e: print "Error: %s" % e diff --git a/KindleBooks_Tools/Kindle_4_PC_Unswindle/mobidedrm.py b/KindleBooks_Tools/Kindle_4_PC_Unswindle/mobidedrm.py index 183432c..864b545 100644 --- a/KindleBooks_Tools/Kindle_4_PC_Unswindle/mobidedrm.py +++ b/KindleBooks_Tools/Kindle_4_PC_Unswindle/mobidedrm.py @@ -24,7 +24,7 @@ # 0.14 - Working out when the extra data flags are present has been problematic # Versions 7 through 9 have tried to tweak the conditions, but have been # only partially successful. Closer examination of lots of sample -# files reveals that a confusin has arisen because trailing data entries +# files reveals that a confusion has arisen because trailing data entries # are not encrypted, but it turns out that the multibyte entries # in utf8 file are encrypted. (Although neither kind gets compressed.) # This knowledge leads to a simplification of the test for the @@ -39,13 +39,15 @@ # Removed the disabled Calibre plug-in code # Permit use of 8-digit PIDs # 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. -# 0.20 - Corretion: It seems that multibyte entries are encrypted in a v6 file. +# 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. +# 0.21 - Added support for multiple pids +# 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.20' +__version__ = '0.24' import sys -import struct -import binascii class Unbuffered: def __init__(self, stream): @@ -55,10 +57,20 @@ class Unbuffered: self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) +sys.stdout=Unbuffered(sys.stdout) + +import os +import struct +import binascii class DrmException(Exception): pass + +# +# MobiBook Utility Routines +# + # Implementation of Pukall Cipher 1 def PC1(key, src, decryption=True): sum1 = 0; @@ -70,7 +82,6 @@ def PC1(key, src, decryption=True): wkey = [] for i in xrange(8): wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) - dst = "" for i in xrange(len(src)): temp1 = 0; @@ -131,7 +142,9 @@ def getSizeOfTrailingDataEntries(ptr, size, flags): num += (ord(ptr[size - num - 1]) & 0x3) + 1 return num -class DrmStripper: + + +class MobiBook: def loadSection(self, section): if (section + 1 == self.num_sections): endoff = len(self.data_file) @@ -140,6 +153,93 @@ class DrmStripper: off = self.sections[section][0] return self.data_file[off:endoff] + def __init__(self, infile): + # initial sanity check on file + self.data_file = file(infile, 'rb').read() + self.header = self.data_file[0:78] + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': + raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 + + # build up section offset and flag info + self.num_sections, = struct.unpack('>H', self.header[76:78]) + self.sections = [] + for i in xrange(self.num_sections): + offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8]) + flags, val = a1, a2<<16|a3<<8|a4 + self.sections.append( (offset, flags, val) ) + + # parse information from section 0 + self.sect = self.loadSection(0) + self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return + self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) + print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) + self.extra_data_flags = 0 + if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5): + self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4]) + print "Extra Data Flags = %d" % self.extra_data_flags + if self.mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below + # so clear that byte so that we leave it to be decrypted. + self.extra_data_flags &= 0xFFFE + + # if exth region exists parse it for metadata array + self.meta_array = {} + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + + def getBookTitle(self): + title = '' + if 503 in self.meta_array: + title = self.meta_array[503] + else : + toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) + tend = toff + tlen + title = self.sect[toff:tend] + if title == '': + title = self.header[:32] + title = title.split("\0")[0] + return title + + def getPIDMetaInfo(self): + rec209 = None + token = None + if 209 in self.meta_array: + rec209 = self.meta_array[209] + data = rec209 + # Parse the 209 data to find the the exth record with the token data. + # The last character of the 209 data points to the record with the token. + # Always 208 from my experience, but I'll leave the logic in case that changes. + for i in xrange(len(data)): + if ord(data[i]) != 0: + if self.meta_array[ord(data[i])] != None: + token = self.meta_array[ord(data[i])] + return rec209, token + def patch(self, off, new): self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):] @@ -152,134 +252,131 @@ class DrmStripper: assert off + in_off + len(new) <= endoff self.patch(off + in_off, new) - def parseDRM(self, data, count, pid): - pid = pid.ljust(16,'\0') - keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" - temp_key = PC1(keyvec1, pid, False) - temp_key_sum = sum(map(ord,temp_key)) & 0xff + def parseDRM(self, data, count, pidlist): found_key = None - for i in xrange(count): - verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1: - found_key = finalkey + keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" + for pid in pidlist: + bigpid = pid.ljust(16,'\0') + temp_key = PC1(keyvec1, bigpid, False) + temp_key_sum = sum(map(ord,temp_key)) & 0xff + found_key = None + for i in xrange(count): + verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver and (flags & 0x1F) == 1: + found_key = finalkey + break + if found_key != None: break if not found_key: # Then try the default encoding that doesn't require a PID + pid = "00000000" temp_key = keyvec1 temp_key_sum = sum(map(ord,temp_key)) & 0xff for i in xrange(count): verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum: - found_key = finalkey - break - return found_key + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver: + found_key = finalkey + break + return [found_key,pid] - def __init__(self, data_file, pid): - if len(pid)==10: - if checksumPid(pid[0:-2]) != pid: - raise DrmException("invalid PID checksum") - pid = pid[0:-2] - elif len(pid)==8: - print "PID without checksum given. With checksum PID is "+checksumPid(pid) - else: - raise DrmException("Invalid PID length") - - self.data_file = data_file - header = data_file[0:72] - if header[0x3C:0x3C+8] != 'BOOKMOBI': - raise DrmException("invalid file format") - self.num_sections, = struct.unpack('>H', data_file[76:78]) - - self.sections = [] - for i in xrange(self.num_sections): - offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8]) - flags, val = a1, a2<<16|a3<<8|a4 - self.sections.append( (offset, flags, val) ) - - sect = self.loadSection(0) - records, = struct.unpack('>H', sect[0x8:0x8+2]) - mobi_length, = struct.unpack('>L',sect[0x14:0x18]) - mobi_version, = struct.unpack('>L',sect[0x68:0x6C]) - extra_data_flags = 0 - print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length) - if (mobi_length >= 0xE4) and (mobi_version >= 5): - extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) - print "Extra Data Flags = %d" %extra_data_flags - if mobi_version < 7: - # multibyte utf8 data is included in the encryption for mobi_version 6 and below - # so clear that byte so that we leave it to be decrypted. - extra_data_flags &= 0xFFFE - - crypto_type, = struct.unpack('>H', sect[0xC:0xC+2]) + def processBook(self, pidlist): + crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." - else: - if crypto_type == 1: - raise DrmException("cannot decode Mobipocket encryption type 1") - if crypto_type != 2: - raise DrmException("unknown encryption type: %d" % crypto_type) + return self.data_file + if crypto_type != 2 and crypto_type != 1: + raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + goodpids = [] + for pid in pidlist: + if len(pid)==10: + if checksumPid(pid[0:-2]) != pid: + print "Warning: PID " + pid + " has incorrect checksum, should have been "+checksumPid(pid[0:-2]) + goodpids.append(pid[0:-2]) + elif len(pid)==8: + goodpids.append(pid) + + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16]) + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) if drm_count == 0: - raise DrmException("no PIDs found in this file") - found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid) + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) if not found_key: - raise DrmException("no key found. maybe the PID is incorrect") - + raise DrmException("No key found. Most likely the correct PID has not been given.") # kill the drm keys self.patchSection(0, "\0" * drm_size, drm_ptr) # kill the drm pointers self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) - # clear the crypto type - self.patchSection(0, "\0" * 2, 0xC) + + if pid=="00000000": + print "File has default encryption, no specific PID." + else: + print "File is encoded with PID "+checksumPid(pid)+"." - # decrypt sections - print "Decrypting. Please wait . . .", - new_data = self.data_file[:self.sections[1][0]] - for i in xrange(1, records+1): - data = self.loadSection(i) - extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags) - if i%100 == 0: - print ".", - # print "record %d, extra_size %d" %(i,extra_size) - new_data += PC1(found_key, data[0:len(data) - extra_size]) - if extra_size > 0: - new_data += data[-extra_size:] - #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size])) - if self.num_sections > records+1: - new_data += self.data_file[self.sections[records+1][0]:] - self.data_file = new_data - print "done" + # clear the crypto type + self.patchSection(0, "\0" * 2, 0xC) - def getResult(self): + # decrypt sections + print "Decrypting. Please wait . . .", + new_data = self.data_file[:self.sections[1][0]] + for i in xrange(1, self.records+1): + data = self.loadSection(i) + extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags) + if i%100 == 0: + print ".", + # print "record %d, extra_size %d" %(i,extra_size) + new_data += PC1(found_key, data[0:len(data) - extra_size]) + if extra_size > 0: + new_data += data[-extra_size:] + if self.num_sections > self.records+1: + new_data += self.data_file[self.sections[self.records+1][0]:] + self.data_file = new_data + print "done" return self.data_file def getUnencryptedBook(infile,pid): - sys.stdout=Unbuffered(sys.stdout) - data_file = file(infile, 'rb').read() - strippedFile = DrmStripper(data_file, pid) - return strippedFile.getResult() + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook([pid]) + +def getUnencryptedBookWithList(infile,pidlist): + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook(pidlist) def main(argv=sys.argv): - sys.stdout=Unbuffered(sys.stdout) print ('MobiDeDrm v%(__version__)s. ' 'Copyright 2008-2010 The Dark Reverser.' % globals()) if len(argv)<4: print "Removes protection from Mobipocket books" print "Usage:" - print " %s " % sys.argv[0] + print " %s " % sys.argv[0] return 1 else: infile = argv[1] outfile = argv[2] - pid = argv[3] + pidlist = argv[3].split(',') try: - stripped_file = getUnencryptedBook(infile, pid) + stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) except DrmException, e: print "Error: %s" % e diff --git a/KindleBooks_Tools/MobiDeDRM.py b/KindleBooks_Tools/MobiDeDRM.py index 536eb78..864b545 100644 --- a/KindleBooks_Tools/MobiDeDRM.py +++ b/KindleBooks_Tools/MobiDeDRM.py @@ -42,9 +42,10 @@ # 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. # 0.21 - Added support for multiple pids # 0.22 - revised structure to hold MobiBook as a class to allow an extended interface -# 0.23 - fixed problem with older files with no EXTH section +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.23' +__version__ = '0.24' import sys @@ -58,6 +59,7 @@ class Unbuffered: return getattr(self.stream, attr) sys.stdout=Unbuffered(sys.stdout) +import os import struct import binascii @@ -155,8 +157,10 @@ class MobiBook: # initial sanity check on file self.data_file = file(infile, 'rb').read() self.header = self.data_file[0:78] - if self.header[0x3C:0x3C+8] != 'BOOKMOBI': + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 # build up section offset and flag info self.num_sections, = struct.unpack('>H', self.header[76:78]) @@ -169,6 +173,14 @@ class MobiBook: # parse information from section 0 self.sect = self.loadSection(0) self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) @@ -199,7 +211,7 @@ class MobiBook: except: self.meta_array = {} pass - + def getBookTitle(self): title = '' if 503 in self.meta_array: @@ -275,12 +287,12 @@ class MobiBook: def processBook(self, pidlist): crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." return self.data_file - if crypto_type == 1: - raise DrmException("Cannot decode Mobipocket encryption type 1") - if crypto_type != 2: + if crypto_type != 2 and crypto_type != 1: raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) goodpids = [] @@ -292,23 +304,32 @@ class MobiBook: elif len(pid)==8: goodpids.append(pid) - # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) - if drm_count == 0: - raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") - found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) - if not found_key: - raise DrmException("No key found. Most likely the correct PID has not been given.") + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : + # calculate the keys + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) + if drm_count == 0: + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) + if not found_key: + raise DrmException("No key found. Most likely the correct PID has not been given.") + # kill the drm keys + self.patchSection(0, "\0" * drm_size, drm_ptr) + # kill the drm pointers + self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) if pid=="00000000": print "File has default encryption, no specific PID." else: print "File is encoded with PID "+checksumPid(pid)+"." - # kill the drm keys - self.patchSection(0, "\0" * drm_size, drm_ptr) - # kill the drm pointers - self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) # clear the crypto type self.patchSection(0, "\0" * 2, 0xC) diff --git a/Mobi_Additional_Tools/FindTopazEbooks.pyw b/Mobi_Additional_Tools/FindTopazEbooks.pyw new file mode 100644 index 0000000..6a0df30 --- /dev/null +++ b/Mobi_Additional_Tools/FindTopazEbooks.pyw @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +# This is a simple tool to identify all Amazon Topaz ebooks in a specific directory. +# There always seems to be confusion since Topaz books downloaded to K4PC/Mac can have +# almost any extension (.azw, .azw1, .prc, tpz). While the .azw1 and .tpz extensions +# are fairly easy to indentify, the others are not (without opening the files in an editor). + +# To run the tool with the GUI frontend, just double-click on the 'FindTopazFiles.pyw' file +# and select the folder where all of the ebooks in question are located. Then click 'Search'. +# The program will list the file names of the ebooks that are indentified as being Topaz. +# You can then isolate those books and use the Topaz tools to decrypt and convert them. + +# You can also run the script from a command line... supplying the folder to search +# as a parameter: python FindTopazEbooks.pyw "C:\My Folder" (change appropriately for +# your particular O.S.) + +# ** NOTE: This program does NOT decrypt or modify Topaz files in any way. It simply identifies them. + +# PLEASE DO NOT PIRATE EBOOKS! + +# We want all authors and publishers, and eBook stores to live +# long and prosperous lives but at the same time we just want to +# be able to read OUR books on whatever device we want and to keep +# readable for a long, long time + +# This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle, +# unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates +# and many many others + +# Revision history: +# 1 - Initial release. + +from __future__ import with_statement + +__license__ = 'GPL v3' + +import sys +import os +import re +import shutil +import Tkinter +import Tkconstants +import tkFileDialog +import tkMessageBox + + +class ScrolledText(Tkinter.Text): + def __init__(self, master=None, **kw): + self.frame = Tkinter.Frame(master) + self.vbar = Tkinter.Scrollbar(self.frame) + self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y) + kw.update({'yscrollcommand': self.vbar.set}) + Tkinter.Text.__init__(self, self.frame, **kw) + self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True) + self.vbar['command'] = self.yview + # Copy geometry methods of self.frame without overriding Text + # methods = hack! + text_meths = vars(Tkinter.Text).keys() + methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys() + methods = set(methods).difference(text_meths) + for m in methods: + if m[0] != '_' and m != 'config' and m != 'configure': + setattr(self, m, getattr(self.frame, m)) + + def __str__(self): + return str(self.frame) + + +def cli_main(argv=sys.argv, obj=None): + progname = os.path.basename(argv[0]) + if len(argv) != 2: + print "usage: %s DIRECTORY" % (progname,) + return 1 + + if obj == None: + print "\nTopaz search results:\n" + else: + obj.stext.insert(Tkconstants.END,"Topaz search results:\n\n") + + inpath = argv[1] + files = os.listdir(inpath) + filefilter = re.compile("(\.azw$)|(\.azw1$)|(\.prc$)|(\.tpz$)", re.IGNORECASE) + files = filter(filefilter.search, files) + + if files: + topazcount = 0 + totalcount = 0 + for filename in files: + with open(os.path.join(inpath, filename), 'rb') as f: + try: + if f.read().startswith('TPZ'): + f.close() + basename, extension = os.path.splitext(filename) + if obj == None: + print " %s is a Topaz formatted ebook." % filename + """ + if extension == '.azw' or extension == '.prc': + print " renaming to %s" % (basename + '.tpz') + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + else: + msg1 = " %s is a Topaz formatted ebook.\n" % filename + obj.stext.insert(Tkconstants.END,msg1) + """ + if extension == '.azw' or extension == '.prc': + msg2 = " renaming to %s\n" % (basename + '.tpz') + obj.stext.insert(Tkconstants.END,msg2) + shutil.move(os.path.join(inpath, filename), + os.path.join(inpath, basename + '.tpz')) + """ + topazcount += 1 + except: + if obj == None: + print " Error reading %s." % filename + else: + msg = " Error reading or %s.\n" % filename + obj.stext.insert(Tkconstants.END,msg) + pass + totalcount += 1 + if topazcount == 0: + if obj == None: + print "\nNo Topaz books found in %s." % inpath + else: + msg = "\nNo Topaz books found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "\n%i Topaz books found in %s\n%i total books checked.\n" % (topazcount, inpath, totalcount) + else: + msg = "\n%i Topaz books found in %s\n%i total books checked.\n\n" %(topazcount, inpath, totalcount) + obj.stext.insert(Tkconstants.END,msg) + else: + if obj == None: + print "No typical Topaz file extensions found in %s.\n" % inpath + else: + msg = "No typical Topaz file extensions found in %s.\n\n" % inpath + obj.stext.insert(Tkconstants.END,msg) + + return 0 + + +class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + ltext='Search a directory for Topaz eBooks\n' + 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='Directory to Search').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) + msg1 = 'Topaz search results \n\n' + self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, + height=15, width=60, wrap=Tkconstants.WORD) + self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky) + #self.stext.insert(Tkconstants.END,msg1) + buttons = Tkinter.Frame(self) + buttons.pack() + + + self.botton = Tkinter.Button( + buttons, text="Search", width=10, command=self.search) + self.botton.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + self.button = Tkinter.Button( + buttons, text="Quit", width=10, command=self.quit) + self.button.pack(side=Tkconstants.RIGHT) + + def get_inpath(self): + cwd = os.getcwdu() + cwd = cwd.encode('utf-8') + inpath = tkFileDialog.askdirectory( + parent=None, title='Directory to search', + initialdir=cwd, initialfile=None) + if inpath: + inpath = os.path.normpath(inpath) + self.inpath.delete(0, Tkconstants.END) + self.inpath.insert(0, inpath) + return + + + def search(self): + inpath = self.inpath.get() + if not inpath or not os.path.exists(inpath): + self.status['text'] = 'Specified directory does not exist' + return + argv = [sys.argv[0], inpath] + self.status['text'] = 'Searching...' + self.botton.configure(state='disabled') + cli_main(argv, self) + self.status['text'] = 'Search a directory for Topaz files' + self.botton.configure(state='normal') + + return + + +def gui_main(): + root = Tkinter.Tk() + root.title('Topaz eBook Finder') + 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()) \ No newline at end of file diff --git a/Mobi_Additional_Tools/KindlePID.pyw b/Mobi_Additional_Tools/KindlePID.pyw index 771e288..73f7d3f 100644 --- a/Mobi_Additional_Tools/KindlePID.pyw +++ b/Mobi_Additional_Tools/KindlePID.pyw @@ -74,6 +74,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/Mobi_Additional_Tools/Kindleizer.pyw b/Mobi_Additional_Tools/Kindleizer.pyw index a94b7d3..a725626 100644 --- a/Mobi_Additional_Tools/Kindleizer.pyw +++ b/Mobi_Additional_Tools/Kindleizer.pyw @@ -83,6 +83,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/Mobi_Additional_Tools/MobiDeDRM.pyw b/Mobi_Additional_Tools/MobiDeDRM.pyw new file mode 100644 index 0000000..055d050 --- /dev/null +++ b/Mobi_Additional_Tools/MobiDeDRM.pyw @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab + +import sys +sys.path.append('lib') +import os, os.path, urllib +import subprocess +from subprocess import Popen, PIPE, STDOUT +import subasyncio +from subasyncio import Process +import Tkinter +import Tkconstants +import tkFileDialog +import tkMessageBox +from scrolltextwidget import ScrolledText + +class MainDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.root = root + self.interval = 2000 + self.p2 = None + self.status = Tkinter.Label(self, text='Remove Encryption from a Mobi eBook') + 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='Mobi eBook input file').grid(row=0, sticky=Tkconstants.E) + self.mobipath = Tkinter.Entry(body, width=50) + self.mobipath.grid(row=0, column=1, sticky=sticky) + cwd = os.getcwdu() + cwd = cwd.encode('utf-8') + self.mobipath.insert(0, cwd) + button = Tkinter.Button(body, text="...", command=self.get_mobipath) + button.grid(row=0, column=2) + + Tkinter.Label(body, text='Name for Unencrypted Output File').grid(row=1, sticky=Tkconstants.E) + self.outpath = Tkinter.Entry(body, width=50) + self.outpath.grid(row=1, column=1, sticky=sticky) + self.outpath.insert(0, '') + button = Tkinter.Button(body, text="...", command=self.get_outpath) + button.grid(row=1, column=2) + + Tkinter.Label(body, text='10 Character PID').grid(row=2, sticky=Tkconstants.E) + self.pidnum = Tkinter.StringVar() + self.pidinfo = Tkinter.Entry(body, width=12, textvariable=self.pidnum) + self.pidinfo.grid(row=2, column=1, sticky=sticky) + + msg1 = 'Conversion Log \n\n' + self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD) + self.stext.grid(row=3, column=0, columnspan=2,sticky=sticky) + self.stext.insert(Tkconstants.END,msg1) + + buttons = Tkinter.Frame(self) + buttons.pack() + self.sbotton = Tkinter.Button( + buttons, text="Start", width=10, command=self.convertit) + self.sbotton.pack(side=Tkconstants.LEFT) + + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + self.qbutton = Tkinter.Button( + buttons, text="Quit", width=10, command=self.quitting) + self.qbutton.pack(side=Tkconstants.RIGHT) + + # read from subprocess pipe without blocking + # invoked every interval via the widget "after" + # option being used, so need to reset it for the next time + def processPipe(self): + poll = self.p2.wait('nowait') + if poll != None: + text = self.p2.readerr() + text += self.p2.read() + msg = text + '\n\n' + 'Encryption successfully removed\n' + if poll != 0: + msg = text + '\n\n' + 'Error: Encryption Removal Failed\n' + self.showCmdOutput(msg) + self.p2 = None + self.sbotton.configure(state='normal') + return + text = self.p2.readerr() + text += self.p2.read() + self.showCmdOutput(text) + # make sure we get invoked again by event loop after interval + self.stext.after(self.interval,self.processPipe) + return + + # post output from subprocess in scrolled text widget + def showCmdOutput(self, msg): + if msg and msg !='': + msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') + self.stext.insert(Tkconstants.END,msg) + self.stext.yview_pickplace(Tkconstants.END) + return + + # run as a subprocess via pipes and collect stdout + def mobirdr(self, infile, outfile, pidnum): + # os.putenv('PYTHONUNBUFFERED', '1') + cmdline = 'python ./lib/mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"' + if sys.platform[0:3] == 'win': + search_path = os.environ['PATH'] + search_path = search_path.lower() + if search_path.find('python') >= 0: + cmdline = 'python lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"' + else : + cmdline = 'lib\mobidedrm.py "' + infile + '" "' + outfile + '" "' + pidnum + '"' + + cmdline = cmdline.encode(sys.getfilesystemencoding()) + p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False) + return p2 + + + def get_mobipath(self): + mobipath = tkFileDialog.askopenfilename( + parent=None, title='Select Mobi eBook File', + defaultextension='.prc', filetypes=[('Mobi eBook File', '.prc'), ('Mobi eBook File', '.azw'),('Mobi eBook File', '.mobi'), + ('All Files', '.*')]) + if mobipath: + mobipath = os.path.normpath(mobipath) + self.mobipath.delete(0, Tkconstants.END) + self.mobipath.insert(0, mobipath) + return + + def get_outpath(self): + mobipath = self.mobipath.get() + initname = os.path.basename(mobipath) + p = initname.find('.') + if p >= 0: initname = initname[0:p] + initname += '_nodrm.mobi' + outpath = tkFileDialog.asksaveasfilename( + parent=None, title='Select Unencrypted Mobi File to produce', + defaultextension='.mobi', initialfile=initname, + filetypes=[('Mobi files', '.mobi'), ('All files', '.*')]) + if outpath: + outpath = os.path.normpath(outpath) + self.outpath.delete(0, Tkconstants.END) + self.outpath.insert(0, outpath) + return + + def quitting(self): + # kill any still running subprocess + if self.p2 != None: + if (self.p2.wait('nowait') == None): + self.p2.terminate() + self.root.destroy() + + # actually ready to run the subprocess and get its output + def convertit(self): + # now disable the button to prevent multiple launches + self.sbotton.configure(state='disabled') + mobipath = self.mobipath.get() + outpath = self.outpath.get() + pidnum = self.pidinfo.get() + if not mobipath or not os.path.exists(mobipath): + self.status['text'] = 'Specified Mobi eBook file does not exist' + self.sbotton.configure(state='normal') + return + if not outpath: + self.status['text'] = 'No output file specified' + self.sbotton.configure(state='normal') + return + if not pidnum or pidnum == '': + self.status['text'] = 'No PID specified' + self.sbotton.configure(state='normal') + return + + log = 'Command = "python mobidedrm.py"\n' + log += 'Mobi Path = "'+ mobipath + '"\n' + log += 'Output File = "' + outpath + '"\n' + log += 'PID = "' + pidnum + '"\n' + log += '\n\n' + log += 'Please Wait ...\n\n' + log = log.encode('utf-8') + self.stext.insert(Tkconstants.END,log) + self.p2 = self.mobirdr(mobipath, outpath, pidnum) + + # python does not seem to allow you to create + # your own eventloop which every other gui does - strange + # so need to use the widget "after" command to force + # event loop to run non-gui events every interval + self.stext.after(self.interval,self.processPipe) + return + + +def main(argv=None): + root = Tkinter.Tk() + root.title('Mobi eBook Encryption Removal') + root.resizable(True, False) + root.minsize(300, 0) + MainDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Mobi_Additional_Tools/lib/kindlepid.py b/Mobi_Additional_Tools/lib/kindlepid.py index 15ca5e5..5041bd4 100644 --- a/Mobi_Additional_Tools/lib/kindlepid.py +++ b/Mobi_Additional_Tools/lib/kindlepid.py @@ -68,7 +68,7 @@ def main(argv=sys.argv): print "Usage: kindlepid.py /" return 1 if len(serial)==16: - if serial.startswith("B00"): + if serial.startswith("B"): print "Kindle serial number detected" else: print "Warning: unrecognized serial number. Please recheck input." diff --git a/Mobi_Additional_Tools/lib/mobidedrm.py b/Mobi_Additional_Tools/lib/mobidedrm.py index 183432c..864b545 100644 --- a/Mobi_Additional_Tools/lib/mobidedrm.py +++ b/Mobi_Additional_Tools/lib/mobidedrm.py @@ -24,7 +24,7 @@ # 0.14 - Working out when the extra data flags are present has been problematic # Versions 7 through 9 have tried to tweak the conditions, but have been # only partially successful. Closer examination of lots of sample -# files reveals that a confusin has arisen because trailing data entries +# files reveals that a confusion has arisen because trailing data entries # are not encrypted, but it turns out that the multibyte entries # in utf8 file are encrypted. (Although neither kind gets compressed.) # This knowledge leads to a simplification of the test for the @@ -39,13 +39,15 @@ # Removed the disabled Calibre plug-in code # Permit use of 8-digit PIDs # 0.19 - It seems that multibyte entries aren't encrypted in a v6 file either. -# 0.20 - Corretion: It seems that multibyte entries are encrypted in a v6 file. +# 0.20 - Correction: It seems that multibyte entries are encrypted in a v6 file. +# 0.21 - Added support for multiple pids +# 0.22 - revised structure to hold MobiBook as a class to allow an extended interface +# 0.23 - fixed problem with older files with no EXTH section +# 0.24 - add support for type 1 encryption and 'TEXtREAd' books as well -__version__ = '0.20' +__version__ = '0.24' import sys -import struct -import binascii class Unbuffered: def __init__(self, stream): @@ -55,10 +57,20 @@ class Unbuffered: self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) +sys.stdout=Unbuffered(sys.stdout) + +import os +import struct +import binascii class DrmException(Exception): pass + +# +# MobiBook Utility Routines +# + # Implementation of Pukall Cipher 1 def PC1(key, src, decryption=True): sum1 = 0; @@ -70,7 +82,6 @@ def PC1(key, src, decryption=True): wkey = [] for i in xrange(8): wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1])) - dst = "" for i in xrange(len(src)): temp1 = 0; @@ -131,7 +142,9 @@ def getSizeOfTrailingDataEntries(ptr, size, flags): num += (ord(ptr[size - num - 1]) & 0x3) + 1 return num -class DrmStripper: + + +class MobiBook: def loadSection(self, section): if (section + 1 == self.num_sections): endoff = len(self.data_file) @@ -140,6 +153,93 @@ class DrmStripper: off = self.sections[section][0] return self.data_file[off:endoff] + def __init__(self, infile): + # initial sanity check on file + self.data_file = file(infile, 'rb').read() + self.header = self.data_file[0:78] + if self.header[0x3C:0x3C+8] != 'BOOKMOBI' and self.header[0x3C:0x3C+8] != 'TEXtREAd': + raise DrmException("invalid file format") + self.magic = self.header[0x3C:0x3C+8] + self.crypto_type = -1 + + # build up section offset and flag info + self.num_sections, = struct.unpack('>H', self.header[76:78]) + self.sections = [] + for i in xrange(self.num_sections): + offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', self.data_file[78+i*8:78+i*8+8]) + flags, val = a1, a2<<16|a3<<8|a4 + self.sections.append( (offset, flags, val) ) + + # parse information from section 0 + self.sect = self.loadSection(0) + self.records, = struct.unpack('>H', self.sect[0x8:0x8+2]) + + if self.magic == 'TEXtREAd': + print "Book has format: ", self.magic + self.extra_data_flags = 0 + self.mobi_length = 0 + self.mobi_version = -1 + self.meta_array = {} + return + self.mobi_length, = struct.unpack('>L',self.sect[0x14:0x18]) + self.mobi_version, = struct.unpack('>L',self.sect[0x68:0x6C]) + print "MOBI header version = %d, length = %d" %(self.mobi_version, self.mobi_length) + self.extra_data_flags = 0 + if (self.mobi_length >= 0xE4) and (self.mobi_version >= 5): + self.extra_data_flags, = struct.unpack('>H', self.sect[0xF2:0xF4]) + print "Extra Data Flags = %d" % self.extra_data_flags + if self.mobi_version < 7: + # multibyte utf8 data is included in the encryption for mobi_version 6 and below + # so clear that byte so that we leave it to be decrypted. + self.extra_data_flags &= 0xFFFE + + # if exth region exists parse it for metadata array + self.meta_array = {} + try: + exth_flag, = struct.unpack('>L', self.sect[0x80:0x84]) + exth = 'NONE' + if exth_flag & 0x40: + exth = self.sect[16 + self.mobi_length:] + if (len(exth) >= 4) and (exth[:4] == 'EXTH'): + nitems, = struct.unpack('>I', exth[8:12]) + pos = 12 + for i in xrange(nitems): + type, size = struct.unpack('>II', exth[pos: pos + 8]) + content = exth[pos + 8: pos + size] + self.meta_array[type] = content + pos += size + except: + self.meta_array = {} + pass + + def getBookTitle(self): + title = '' + if 503 in self.meta_array: + title = self.meta_array[503] + else : + toff, tlen = struct.unpack('>II', self.sect[0x54:0x5c]) + tend = toff + tlen + title = self.sect[toff:tend] + if title == '': + title = self.header[:32] + title = title.split("\0")[0] + return title + + def getPIDMetaInfo(self): + rec209 = None + token = None + if 209 in self.meta_array: + rec209 = self.meta_array[209] + data = rec209 + # Parse the 209 data to find the the exth record with the token data. + # The last character of the 209 data points to the record with the token. + # Always 208 from my experience, but I'll leave the logic in case that changes. + for i in xrange(len(data)): + if ord(data[i]) != 0: + if self.meta_array[ord(data[i])] != None: + token = self.meta_array[ord(data[i])] + return rec209, token + def patch(self, off, new): self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):] @@ -152,134 +252,131 @@ class DrmStripper: assert off + in_off + len(new) <= endoff self.patch(off + in_off, new) - def parseDRM(self, data, count, pid): - pid = pid.ljust(16,'\0') - keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" - temp_key = PC1(keyvec1, pid, False) - temp_key_sum = sum(map(ord,temp_key)) & 0xff + def parseDRM(self, data, count, pidlist): found_key = None - for i in xrange(count): - verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1: - found_key = finalkey + keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96" + for pid in pidlist: + bigpid = pid.ljust(16,'\0') + temp_key = PC1(keyvec1, bigpid, False) + temp_key_sum = sum(map(ord,temp_key)) & 0xff + found_key = None + for i in xrange(count): + verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver and (flags & 0x1F) == 1: + found_key = finalkey + break + if found_key != None: break if not found_key: # Then try the default encoding that doesn't require a PID + pid = "00000000" temp_key = keyvec1 temp_key_sum = sum(map(ord,temp_key)) & 0xff for i in xrange(count): verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30]) - cookie = PC1(temp_key, cookie) - ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) - if verification == ver and cksum == temp_key_sum: - found_key = finalkey - break - return found_key + if cksum == temp_key_sum: + cookie = PC1(temp_key, cookie) + ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie) + if verification == ver: + found_key = finalkey + break + return [found_key,pid] - def __init__(self, data_file, pid): - if len(pid)==10: - if checksumPid(pid[0:-2]) != pid: - raise DrmException("invalid PID checksum") - pid = pid[0:-2] - elif len(pid)==8: - print "PID without checksum given. With checksum PID is "+checksumPid(pid) - else: - raise DrmException("Invalid PID length") - - self.data_file = data_file - header = data_file[0:72] - if header[0x3C:0x3C+8] != 'BOOKMOBI': - raise DrmException("invalid file format") - self.num_sections, = struct.unpack('>H', data_file[76:78]) - - self.sections = [] - for i in xrange(self.num_sections): - offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8]) - flags, val = a1, a2<<16|a3<<8|a4 - self.sections.append( (offset, flags, val) ) - - sect = self.loadSection(0) - records, = struct.unpack('>H', sect[0x8:0x8+2]) - mobi_length, = struct.unpack('>L',sect[0x14:0x18]) - mobi_version, = struct.unpack('>L',sect[0x68:0x6C]) - extra_data_flags = 0 - print "MOBI header version = %d, length = %d" %(mobi_version, mobi_length) - if (mobi_length >= 0xE4) and (mobi_version >= 5): - extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4]) - print "Extra Data Flags = %d" %extra_data_flags - if mobi_version < 7: - # multibyte utf8 data is included in the encryption for mobi_version 6 and below - # so clear that byte so that we leave it to be decrypted. - extra_data_flags &= 0xFFFE - - crypto_type, = struct.unpack('>H', sect[0xC:0xC+2]) + def processBook(self, pidlist): + crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2]) + print 'Crypto Type is: ', crypto_type + self.crypto_type = crypto_type if crypto_type == 0: print "This book is not encrypted." - else: - if crypto_type == 1: - raise DrmException("cannot decode Mobipocket encryption type 1") - if crypto_type != 2: - raise DrmException("unknown encryption type: %d" % crypto_type) + return self.data_file + if crypto_type != 2 and crypto_type != 1: + raise DrmException("Cannot decode unknown Mobipocket encryption type %d" % crypto_type) + goodpids = [] + for pid in pidlist: + if len(pid)==10: + if checksumPid(pid[0:-2]) != pid: + print "Warning: PID " + pid + " has incorrect checksum, should have been "+checksumPid(pid[0:-2]) + goodpids.append(pid[0:-2]) + elif len(pid)==8: + goodpids.append(pid) + + if self.crypto_type == 1: + t1_keyvec = "QDCVEPMU675RUBSZ" + if self.magic == 'TEXtREAd': + bookkey_data = self.sect[0x0E:0x0E+16] + else: + bookkey_data = self.sect[0x90:0x90+16] + pid = "00000000" + found_key = PC1(t1_keyvec, bookkey_data) + else : # calculate the keys - drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16]) + drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', self.sect[0xA8:0xA8+16]) if drm_count == 0: - raise DrmException("no PIDs found in this file") - found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid) + raise DrmException("Not yet initialised with PID. Must be opened with Mobipocket Reader first.") + found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids) if not found_key: - raise DrmException("no key found. maybe the PID is incorrect") - + raise DrmException("No key found. Most likely the correct PID has not been given.") # kill the drm keys self.patchSection(0, "\0" * drm_size, drm_ptr) # kill the drm pointers self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8) - # clear the crypto type - self.patchSection(0, "\0" * 2, 0xC) + + if pid=="00000000": + print "File has default encryption, no specific PID." + else: + print "File is encoded with PID "+checksumPid(pid)+"." - # decrypt sections - print "Decrypting. Please wait . . .", - new_data = self.data_file[:self.sections[1][0]] - for i in xrange(1, records+1): - data = self.loadSection(i) - extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags) - if i%100 == 0: - print ".", - # print "record %d, extra_size %d" %(i,extra_size) - new_data += PC1(found_key, data[0:len(data) - extra_size]) - if extra_size > 0: - new_data += data[-extra_size:] - #self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size])) - if self.num_sections > records+1: - new_data += self.data_file[self.sections[records+1][0]:] - self.data_file = new_data - print "done" + # clear the crypto type + self.patchSection(0, "\0" * 2, 0xC) - def getResult(self): + # decrypt sections + print "Decrypting. Please wait . . .", + new_data = self.data_file[:self.sections[1][0]] + for i in xrange(1, self.records+1): + data = self.loadSection(i) + extra_size = getSizeOfTrailingDataEntries(data, len(data), self.extra_data_flags) + if i%100 == 0: + print ".", + # print "record %d, extra_size %d" %(i,extra_size) + new_data += PC1(found_key, data[0:len(data) - extra_size]) + if extra_size > 0: + new_data += data[-extra_size:] + if self.num_sections > self.records+1: + new_data += self.data_file[self.sections[self.records+1][0]:] + self.data_file = new_data + print "done" return self.data_file def getUnencryptedBook(infile,pid): - sys.stdout=Unbuffered(sys.stdout) - data_file = file(infile, 'rb').read() - strippedFile = DrmStripper(data_file, pid) - return strippedFile.getResult() + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook([pid]) + +def getUnencryptedBookWithList(infile,pidlist): + if not os.path.isfile(infile): + raise DrmException('Input File Not Found') + book = MobiBook(infile) + return book.processBook(pidlist) def main(argv=sys.argv): - sys.stdout=Unbuffered(sys.stdout) print ('MobiDeDrm v%(__version__)s. ' 'Copyright 2008-2010 The Dark Reverser.' % globals()) if len(argv)<4: print "Removes protection from Mobipocket books" print "Usage:" - print " %s " % sys.argv[0] + print " %s " % sys.argv[0] return 1 else: infile = argv[1] outfile = argv[2] - pid = argv[3] + pidlist = argv[3].split(',') try: - stripped_file = getUnencryptedBook(infile, pid) + stripped_file = getUnencryptedBookWithList(infile, pidlist) file(outfile, 'wb').write(stripped_file) except DrmException, e: print "Error: %s" % e diff --git a/ReadMe_First.txt b/ReadMe_First.txt new file mode 100644 index 0000000..738fc9a --- /dev/null +++ b/ReadMe_First.txt @@ -0,0 +1,96 @@ + + +Welcome to the tools! + +The set includes tools to remove DRM from eReader PDB books, Barnes and Noble ePubs, Adobe ePubs, Adobe PDFs, and Kindle/Mobi ebooks (including Topaz). + + +This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started. + + +Calibre Users (Mac OS X, Linux, Windows) +------------- +If you are a calibre user, the quickest and easiest way to remove DRM form your ebooks is to open the Calibre_Plugins folder and install each of the plugins following the instructions and configuration directions provided in each plugins README file. + +Once installed and configured, you can simply import a DRM book into Calibre and end up with the DeDRM version in the Calibre database. + +These plugins work for Windows, Mac OS X, and Linux + + + +Mac OS X Users (Mac OS X 10.5 and 10.6) +-------------- +Drag the DeDRM X.X.app droplet to your Desktop. Double-click on it once and it will guide you through collecting the data it needs to remove the DRM. + +To use it simply drag a book onto the droplet, and a DeDRM version will appear. This tools supports dragging and dropping of folders of ebooks as well. + + + +Not a Calibre or a Mac OS X DeDRM User? +---------------------------------------- +There are a number of python based tools that have graphical user interfaces to make them easy to use. To use any of these tools, you need to have Python 2.5, 2.6, or 2.7 for 32 bits installed on your machine as well as a matching PyCrypto or OpenSSL for some tools. + +On Mac OS X (10.5 and 10.6) and Linux (recent versions), your systems already have the proper Python and OpenSSL installed. So nothing need be done, you can already run these tools by double-clicking on the .pyw python scripts. + +Users of Mac OS X 10.3 and 10.4, need to download and install the "32-bit Mac Installer disk Image (2.7.X) for OS X 10.3 and later from http://www.python.org/download/releases/2.7.1/ + +On Windows, you need to install a 32 bit version of Python (even on Windows 64) plus a matching 32 bit version of PyCrypto *OR* OpenSSL. See the end of this document for details. + +The scripts are organized by type of ebook you need to remove the DRM from. Choose from among: + + "Adobe_ePub_Tools" + "Adobe_PDF_Tools" + "Barnes_and_Noble_ePub_Tools" + "eReader_PDB_Tools" + "KindleBooks_Tools" + +by simply opening that folder. + +In the "KindleBooks_Tools" folder the primary tool is in the "KindleBooks" folder. + +If you are a Windows user, or a Linux platform using Wine, or Mac OS X or have trouble running the KindleBooks tools, there are two other tools provided. These are called "Kindle_4_Mac_Unswindle" and "Kindle_4_PC_Unswindle". + +Look for a README inside of the relevant folder to get you started. + + + +Additional Tools +---------------------- +Some additional tools are also provided in the "Mobi_Additional_Tools" folder. There are tools for working with "Kindle for iPhone/iPod_Touch/iPad", finding Topaz ebooks, unpacking Mobi ebooks (without DRM) to get to the Mobi markup language inside, and etc. + +There is also an "ePub_Fixer" folder that can be used to fix broken DRM epubs that sometimes provided by Adobe and Barnes and Noble that actually violate the zip standard. + +Check out their readmes for more info. + + + +Windows and Python Tools +------------------------ +We strongly recommend ActiveState's Active Python 2.6 or 2.7 Community Edition for Windows (x86) 32 bits. This can be downloaded for free from: + + http://www.activestate.com/activepython/downloads + + +In addition, Windows Users need one of PyCrypto OR OpenSSL. + +For OpenSSL: + + Win32 OpenSSL v0.9.8o (8Mb) + http://www.slproweb.com/download/Win32OpenSSL-0_9_8o.exe + (if you get an error message about missing Visual C++ + redistributables... cancel the install and install the + below support program from Microsoft, THEN install OpenSSL) + + Visual C++ 2008 Redistributables (1.7Mb) + http://www.microsoft.com/downloads/details.aspx?familyid=9B2DA534-3E03-4391-8A4D-074B9F2BC1BF + +For PyCrypto: + + There are many places to get PyCrypto installers for Windows. One such place is: + + http://www.voidspace.org.uk/python/modules.shtml + + Please get the latest PyCrypto meant for Windows 32 bit that matches the version of Python you installed (2.7, or 2.6) + +Once Windows users have installed Python 2.X for 32 bits, and the matching OpenSSL OR PyCrypto pieces, they too are ready to run the scripts. + diff --git a/ePub_Fixer/README_ePub_Fixer.txt b/ePub_Fixer/README_ePub_Fixer.txt index f7316f8..b864d07 100644 --- a/ePub_Fixer/README_ePub_Fixer.txt +++ b/ePub_Fixer/README_ePub_Fixer.txt @@ -1,6 +1,6 @@ ePub_Fixer -ePubs are specially crafted zip archives. Unfortunately, many of te DRM encoded Adobe Adept and Barnes & Noble ePubs are not "proper" zip archives in that the names of some files in the zip central directory do NOT match the local name given in archive itself. This type of zip archive is technically incorrect/corrupted and can not be read by many other programs. +ePubs are specially crafted zip archives. Unfortunately, many of the DRM encoded Adobe Adept and Barnes & Noble ePubs are not "proper" zip archives in that the names of some files in the zip central directory do NOT match the local name given in archive itself. This type of zip archive is technically incorrect/corrupted and can not be read by many other programs. ePub_Fixer was designed to fix improperly created zip archives of this type. diff --git a/ePub_Fixer/ePub_Fixer.pyw b/ePub_Fixer/ePub_Fixer.pyw index 6e3a53e..33f3f59 100644 --- a/ePub_Fixer/ePub_Fixer.pyw +++ b/ePub_Fixer/ePub_Fixer.pyw @@ -88,6 +88,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/eReader_PDB_Tools/Pml2HTML.pyw b/eReader_PDB_Tools/Pml2HTML.pyw index b02922e..ead07ae 100644 --- a/eReader_PDB_Tools/Pml2HTML.pyw +++ b/eReader_PDB_Tools/Pml2HTML.pyw @@ -85,6 +85,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/eReader_PDB_Tools/eReaderPDB2PML.pyw b/eReader_PDB_Tools/eReaderPDB2PML.pyw index 16da8cd..e230a44 100644 --- a/eReader_PDB_Tools/eReaderPDB2PML.pyw +++ b/eReader_PDB_Tools/eReaderPDB2PML.pyw @@ -98,6 +98,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/eReader_PDB_Tools/eReaderPDB2PMLZ.pyw b/eReader_PDB_Tools/eReaderPDB2PMLZ.pyw index 635f3fe..82fcfb2 100644 --- a/eReader_PDB_Tools/eReaderPDB2PMLZ.pyw +++ b/eReader_PDB_Tools/eReaderPDB2PMLZ.pyw @@ -89,6 +89,8 @@ class MainDialog(Tkinter.Frame): def showCmdOutput(self, msg): if msg and msg !='': msg = msg.encode('utf-8') + if sys.platform.startswith('win'): + msg = msg.replace('\r\n','\n') self.stext.insert(Tkconstants.END,msg) self.stext.yview_pickplace(Tkconstants.END) return diff --git a/eReader_PDB_Tools/lib/eReaderPDB2PML_plugin.py b/eReader_PDB_Tools/lib/eReaderPDB2PML_plugin.py index 405ef7c..ad172a3 100644 --- a/eReader_PDB_Tools/lib/eReaderPDB2PML_plugin.py +++ b/eReader_PDB_Tools/lib/eReaderPDB2PML_plugin.py @@ -76,7 +76,6 @@ class eRdrDeDRM(FileTypePlugin): if pmlfilepath and pmlfilepath != 1: import zipfile - import shutil print " Creating PMLZ file" myZipFile = zipfile.ZipFile(pmlzfile.name,'w',zipfile.ZIP_STORED, False) list = os.listdir(outdir) diff --git a/eReader_PDB_Tools/lib/erdr2pml.py b/eReader_PDB_Tools/lib/erdr2pml.py index f4ad3c4..ce6945d 100644 --- a/eReader_PDB_Tools/lib/erdr2pml.py +++ b/eReader_PDB_Tools/lib/erdr2pml.py @@ -56,32 +56,9 @@ # 0.15 - enabled high-ascii to pml character encoding. DropBook now works on Mac. # 0.16 - convert to use openssl DES (very very fast) or pure python DES if openssl's libcrypto is not available # 0.17 - added support for pycrypto's DES as well +# 0.18 - on Windows try PyCrypto first and OpenSSL next -Des = None - -import openssl_des -Des = openssl_des.load_libcrypto() - -# if that did not work then try pycrypto version of DES -if Des == None: - import pycrypto_des - Des = pycrypto_des.load_pycrypto() - -# if that did not work then use pure python implementation -# of DES and try to speed it up with Psycho -if Des == None: - import python_des - Des = python_des.Des - # Import Psyco if available - try: - # http://psyco.sourceforge.net - import psyco - psyco.full() - except ImportError: - pass - - -__version__='0.17' +__version__='0.18' class Unbuffered: def __init__(self, stream): @@ -97,6 +74,37 @@ sys.stdout=Unbuffered(sys.stdout) import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile +Des = None +if sys.platform.startswith('win'): + # first try with pycrypto + import pycrypto_des + Des = pycrypto_des.load_pycrypto() + if Des == None: + # they try with openssl + import openssl_des + Des = openssl_des.load_libcrypto() +else: + # first try with openssl + import openssl_des + Des = openssl_des.load_libcrypto() + if Des == None: + # then try with pycrypto + import pycrypto_des + Des = pycrypto_des.load_pycrypto() + +# if that did not work then use pure python implementation +# of DES and try to speed it up with Psycho +if Des == None: + import python_des + Des = python_des.Des + # Import Psyco if available + try: + # http://psyco.sourceforge.net + import psyco + psyco.full() + except ImportError: + pass + try: from hashlib import sha1 except ImportError: @@ -460,7 +468,7 @@ def main(argv=None): myZipFile.write(imagePath, localname) myZipFile.close() # remove temporary directory - shutil.rmtree(outdir) + shutil.rmtree(outdir, True) end_time = time.time() search_time = end_time - start_time

;+s2AbaOoe*E8E3?D*7N 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 Dy^UXi literal 203536 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