BVB Source Codes

DeDRM_tools Show k4mobidedrm.py Source code

Return Download DeDRM_tools: download k4mobidedrm.py Source code - Download DeDRM_tools Source code - Type:.py
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from __future__ import with_statement
  5.  
  6. # k4mobidedrm.py, version 5.3
  7. # Copyright 漏 2009-2015 by ApprenticeHarper et al.
  8.  
  9. # engine to remove drm from Kindle and Mobipocket ebooks
  10. # for personal use for archiving and converting your ebooks
  11.  
  12. # PLEASE DO NOT PIRATE EBOOKS!
  13.  
  14. # We want all authors and publishers, and ebook stores to live
  15. # long and prosperous lives but at the same time  we just want to
  16. # be able to read OUR books on whatever device we want and to keep
  17. # readable for a long, long time
  18.  
  19. # This borrows very heavily from works by CMBDTC, IHeartCabbages, skindle,
  20. #    unswindle, DarkReverser, ApprenticeAlf, DiapDealer, some_updates
  21. #    and many many others
  22. # Special thanks to The Dark Reverser for MobiDeDrm and CMBDTC for cmbdtc_dump
  23. # from which this script borrows most unashamedly.
  24.  
  25.  
  26. # Changelog
  27. #  1.0 - Name change to k4mobidedrm. Adds Mac support, Adds plugin code
  28. #  1.1 - Adds support for additional kindle.info files
  29. #  1.2 - Better error handling for older Mobipocket
  30. #  1.3 - Don't try to decrypt Topaz books
  31. #  1.7 - Add support for Topaz books and Kindle serial numbers. Split code.
  32. #  1.9 - Tidy up after Topaz, minor exception changes
  33. #  2.1 - Topaz fix and filename sanitizing
  34. #  2.2 - Topaz Fix and minor Mac code fix
  35. #  2.3 - More Topaz fixes
  36. #  2.4 - K4PC/Mac key generation fix
  37. #  2.6 - Better handling of non-K4PC/Mac ebooks
  38. #  2.7 - Better trailing bytes handling in mobidedrm
  39. #  2.8 - Moved parsing of kindle.info files to mac & pc util files.
  40. #  3.1 - Updated for new calibre interface. Now __init__ in plugin.
  41. #  3.5 - Now support Kindle for PC/Mac 1.6
  42. #  3.6 - Even better trailing bytes handling in mobidedrm
  43. #  3.7 - Add support for Amazon Print Replica ebooks.
  44. #  3.8 - Improved Topaz support
  45. #  4.1 - Improved Topaz support and faster decryption with alfcrypto
  46. #  4.2 - Added support for Amazon's KF8 format ebooks
  47. #  4.4 - Linux calls to Wine added, and improved configuration dialog
  48. #  4.5 - Linux works again without Wine. Some Mac key file search changes
  49. #  4.6 - First attempt to handle unicode properly
  50. #  4.7 - Added timing reports, and changed search for Mac key files
  51. #  4.8 - Much better unicode handling, matching the updated inept and ignoble scripts
  52. #      - Moved back into plugin, __init__ in plugin now only contains plugin code.
  53. #  4.9 - Missed some invalid characters in cleanup_name
  54. #  5.0 - Extraction of info from Kindle for PC/Mac moved into kindlekey.py
  55. #      - tweaked GetDecryptedBook interface to leave passed parameters unchanged
  56. #  5.1 - moved unicode_argv call inside main for Windows DeDRM compatibility
  57. #  5.2 - Fixed error in command line processing of unicode arguments
  58. #  5.3 - Changed Android support to allow passing of backup .ab files
  59. #  5.4 - Recognise KFX files masquerading as azw, even if we can't decrypt them yet.
  60.  
  61. __version__ = '5.4'
  62.  
  63.  
  64. import sys, os, re
  65. import csv
  66. import getopt
  67. import re
  68. import traceback
  69. import time
  70. import htmlentitydefs
  71. import json
  72.  
  73. class DrmException(Exception):
  74.     pass
  75.  
  76. if 'calibre' in sys.modules:
  77.     inCalibre = True
  78. else:
  79.     inCalibre = False
  80.  
  81. if inCalibre:
  82.     from calibre_plugins.dedrm import mobidedrm
  83.     from calibre_plugins.dedrm import topazextract
  84.     from calibre_plugins.dedrm import kgenpids
  85.     from calibre_plugins.dedrm import androidkindlekey
  86. else:
  87.     import mobidedrm
  88.     import topazextract
  89.     import kgenpids
  90.     import androidkindlekey
  91.  
  92. # Wrap a stream so that output gets flushed immediately
  93. # and also make sure that any unicode strings get
  94. # encoded using "replace" before writing them.
  95. class SafeUnbuffered:
  96.     def __init__(self, stream):
  97.         self.stream = stream
  98.         self.encoding = stream.encoding
  99.         if self.encoding == None:
  100.             self.encoding = "utf-8"
  101.     def write(self, data):
  102.         if isinstance(data,unicode):
  103.             data = data.encode(self.encoding,"replace")
  104.         self.stream.write(data)
  105.         self.stream.flush()
  106.     def __getattr__(self, attr):
  107.         return getattr(self.stream, attr)
  108.  
  109. iswindows = sys.platform.startswith('win')
  110. isosx = sys.platform.startswith('darwin')
  111.  
  112. def unicode_argv():
  113.     if iswindows:
  114.         # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
  115.         # strings.
  116.  
  117.         # Versions 2.x of Python don't support Unicode in sys.argv on
  118.         # Windows, with the underlying Windows API instead replacing multi-byte
  119.         # characters with '?'.
  120.  
  121.  
  122.         from ctypes import POINTER, byref, cdll, c_int, windll
  123.         from ctypes.wintypes import LPCWSTR, LPWSTR
  124.  
  125.         GetCommandLineW = cdll.kernel32.GetCommandLineW
  126.         GetCommandLineW.argtypes = []
  127.         GetCommandLineW.restype = LPCWSTR
  128.  
  129.         CommandLineToArgvW = windll.shell32.CommandLineToArgvW
  130.         CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
  131.         CommandLineToArgvW.restype = POINTER(LPWSTR)
  132.  
  133.         cmd = GetCommandLineW()
  134.         argc = c_int(0)
  135.         argv = CommandLineToArgvW(cmd, byref(argc))
  136.         if argc.value > 0:
  137.             # Remove Python executable and commands if present
  138.             start = argc.value - len(sys.argv)
  139.             return [argv[i] for i in
  140.                     xrange(start, argc.value)]
  141.         # if we don't have any arguments at all, just pass back script name
  142.         # this should never happen
  143.         return [u"mobidedrm.py"]
  144.     else:
  145.         argvencoding = sys.stdin.encoding
  146.         if argvencoding == None:
  147.             argvencoding = "utf-8"
  148.         return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
  149.  
  150. # cleanup unicode filenames
  151. # borrowed from calibre from calibre/src/calibre/__init__.py
  152. # added in removal of control (<32) chars
  153. # and removal of . at start and end
  154. # and with some (heavily edited) code from Paul Durrant's kindlenamer.py
  155. def cleanup_name(name):
  156.     # substitute filename unfriendly characters
  157.     name = name.replace(u"<",u"[").replace(u">",u"]").replace(u" : ",u" 鈥 ").replace(u": ",u" 鈥 ").replace(u":",u"鈥?).replace(u"/",u"_").replace(u"\\",u"_").replace(u"|",u"_").replace(u"\"",u"\'").replace(u"*",u"_").replace(u"?",u"")
  158.     # delete control characters
  159.     name = u"".join(char for char in name if ord(char)>=32)
  160.     # white space to single space, delete leading and trailing while space
  161.     name = re.sub(ur"\s", u" ", name).strip()
  162.     # remove leading dots
  163.     while len(name)>0 and name[0] == u".":
  164.         name = name[1:]
  165.     # remove trailing dots (Windows doesn't like them)
  166.     if name.endswith(u'.'):
  167.         name = name[:-1]
  168.     return name
  169.  
  170. # must be passed unicode
  171. def unescape(text):
  172.     def fixup(m):
  173.         text = m.group(0)
  174.         if text[:2] == u"&#":
  175.             # character reference
  176.             try:
  177.                 if text[:3] == u"&#x":
  178.                     return unichr(int(text[3:-1], 16))
  179.                 else:
  180.                     return unichr(int(text[2:-1]))
  181.             except ValueError:
  182.                 pass
  183.         else:
  184.             # named entity
  185.             try:
  186.                 text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
  187.             except KeyError:
  188.                 pass
  189.         return text # leave as is
  190.     return re.sub(u"&#?\w+;", fixup, text)
  191.  
  192. def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime = time.time()):
  193.     # handle the obvious cases at the beginning
  194.     if not os.path.isfile(infile):
  195.         raise DrmException(u"Input file does not exist.")
  196.  
  197.     mobi = True
  198.     magic8 = open(infile,'rb').read(8)
  199.     if magic8 == '\xeaDRMION\xee':
  200.         raise DrmException(u"KFX format detected. This format cannot be decrypted yet.")
  201.        
  202.     magic3 = magic8[:3]
  203.     if magic3 == 'TPZ':
  204.         mobi = False
  205.  
  206.     if mobi:
  207.         mb = mobidedrm.MobiBook(infile)
  208.     else:
  209.         mb = topazextract.TopazBook(infile)
  210.  
  211.     bookname = unescape(mb.getBookTitle())
  212.     print u"Decrypting {1} ebook: {0}".format(bookname, mb.getBookType())
  213.  
  214.     # copy list of pids
  215.     totalpids = list(pids)
  216.     # extend list of serials with serials from android databases
  217.     for aFile in androidFiles:
  218.         serials.extend(androidkindlekey.get_serials(aFile))
  219.     # extend PID list with book-specific PIDs from seriala and kDatabases
  220.     md1, md2 = mb.getPIDMetaInfo()
  221.     totalpids.extend(kgenpids.getPidList(md1, md2, serials, kDatabases))
  222.     # remove any duplicates
  223.     totalpids = list(set(totalpids))
  224.     print u"Found {1:d} keys to try after {0:.1f} seconds".format(time.time()-starttime, len(totalpids))
  225.     #print totalpids
  226.  
  227.     try:
  228.         mb.processBook(totalpids)
  229.     except:
  230.         mb.cleanup
  231.         raise
  232.  
  233.     print u"Decryption succeeded after {0:.1f} seconds".format(time.time()-starttime)
  234.     return mb
  235.  
  236.  
  237. # kDatabaseFiles is a list of files created by kindlekey
  238. def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
  239.     starttime = time.time()
  240.     kDatabases = []
  241.     for dbfile in kDatabaseFiles:
  242.         kindleDatabase = {}
  243.         try:
  244.             with open(dbfile, 'r') as keyfilein:
  245.                 kindleDatabase = json.loads(keyfilein.read())
  246.             kDatabases.append([dbfile,kindleDatabase])
  247.         except Exception, e:
  248.             print u"Error getting database from file {0:s}: {1:s}".format(dbfile,e)
  249.             traceback.print_exc()
  250.  
  251.  
  252.  
  253.     try:
  254.         book = GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime)
  255.     except Exception, e:
  256.         print u"Error decrypting book after {1:.1f} seconds: {0}".format(e.args[0],time.time()-starttime)
  257.         traceback.print_exc()
  258.         return 1
  259.  
  260.     # if we're saving to the same folder as the original, use file name_
  261.     # if to a different folder, use book name
  262.     if os.path.normcase(os.path.normpath(outdir)) == os.path.normcase(os.path.normpath(os.path.dirname(infile))):
  263.         outfilename = os.path.splitext(os.path.basename(infile))[0]
  264.     else:
  265.         outfilename = cleanup_name(book.getBookTitle())
  266.  
  267.     # avoid excessively long file names
  268.     if len(outfilename)>150:
  269.         outfilename = outfilename[:99]+"--"+outfilename[-49:]
  270.  
  271.     outfilename = outfilename+u"_nodrm"
  272.     outfile = os.path.join(outdir, outfilename + book.getBookExtension())
  273.  
  274.     book.getFile(outfile)
  275.     print u"Saved decrypted book {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
  276.  
  277.     if book.getBookType()==u"Topaz":
  278.         zipname = os.path.join(outdir, outfilename + u"_SVG.zip")
  279.         book.getSVGZip(zipname)
  280.         print u"Saved SVG ZIP Archive for {1:s} after {0:.1f} seconds".format(time.time()-starttime, outfilename)
  281.  
  282.     # remove internal temporary directory of Topaz pieces
  283.     book.cleanup()
  284.     return 0
  285.  
  286.  
  287. def usage(progname):
  288.     print u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks"
  289.     print u"Usage:"
  290.     print u"    {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml|backup.ab> ] <infile> <outdir>".format(progname)
  291.  
  292. #
  293. # Main
  294. #
  295. def cli_main():
  296.     argv=unicode_argv()
  297.     progname = os.path.basename(argv[0])
  298.     print u"K4MobiDeDrm v{0}.\nCopyright 漏 2008-2013 The Dark Reverser et al.".format(__version__)
  299.  
  300.     try:
  301.         opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
  302.     except getopt.GetoptError, err:
  303.         print u"Error in options or arguments: {0}".format(err.args[0])
  304.         usage(progname)
  305.         sys.exit(2)
  306.     if len(args)<2:
  307.         usage(progname)
  308.         sys.exit(2)
  309.  
  310.     infile = args[0]
  311.     outdir = args[1]
  312.     kDatabaseFiles = []
  313.     androidFiles = []
  314.     serials = []
  315.     pids = []
  316.  
  317.     for o, a in opts:
  318.         if o == "-k":
  319.             if a == None :
  320.                 raise DrmException("Invalid parameter for -k")
  321.             kDatabaseFiles.append(a)
  322.         if o == "-p":
  323.             if a == None :
  324.                 raise DrmException("Invalid parameter for -p")
  325.             pids = a.split(',')
  326.         if o == "-s":
  327.             if a == None :
  328.                 raise DrmException("Invalid parameter for -s")
  329.             serials = a.split(',')
  330.         if o == '-a':
  331.             if a == None:
  332.                 raise DrmException("Invalid parameter for -a")
  333.             androidFiles.append(a)
  334.  
  335.     # try with built in Kindle Info files if not on Linux
  336.     k4 = not sys.platform.startswith('linux')
  337.  
  338.     return decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids)
  339.  
  340.  
  341. if __name__ == '__main__':
  342.     sys.stdout=SafeUnbuffered(sys.stdout)
  343.     sys.stderr=SafeUnbuffered(sys.stderr)
  344.     sys.exit(cli_main())
  345.  
downloadk4mobidedrm.py Source code - Download DeDRM_tools Source code
Related Source Codes/Software:
kcp - KCP - A Fast and Reliable ARQ Protocol 2017-04-08
cloc - cloc counts blank lines, comment lines, and physic... 2017-04-08
nvidia-docker - Build and run Docker containers leveraging NVIDIA ... 2017-04-08
quickstart - Angular 2 QuickStart - source from the documentati... 2017-04-08
jQuery-Autocomplete - Ajax Autocomplete for jQuery allows you to easily ... 2017-04-08
fuckitjs - The Original Javascript Error Steamroller 2017-04-09
eslint-plugin-react - React specific linting rules for ESLint 2017-04-09
husky - 2017-04-09
electron-builder - A complete solution to package and build a ready f... 2017-04-09
jsfeat - JavaScript Computer Vision library. 2017-04-16
flakes - Flakes is an Admin Template Framework. A combinati... 2017-04-15
capstone - Capstone disassembly/disassembler framework: Core ... 2017-04-15
nginx-resources - A collection of resources covering Nginx, Nginx + ... 2017-04-15
utron - A lightweight MVC framework for Go(Golang) 2017-04-15
cfssl - CFSSL: Cloudflare's PKI and TLS toolkit ... 2017-04-15
HandBrake - HandBrake's main development repository ... 2017-04-15
Awesome-Networking - A curated list of awesome networking libraries, re... 2017-04-15
react-sortable-hoc - A set of higher-order components to turn any list ... 2017-04-15
jugglingdb - Multi-database ORM for nodejs: redis, mongodb, mys... 2017-04-15

 Back to top