]> xmof Git - DeDRM.git/commitdiff
Mostly Mac fixes. mobidedrm.py now works, and k4mobidedrm for at least some input...
authorApprentice Harper <apprenticeharper@gmail.com>
Sun, 4 Oct 2020 19:36:12 +0000 (20:36 +0100)
committerApprentice Harper <apprenticeharper@gmail.com>
Sun, 4 Oct 2020 19:36:12 +0000 (20:36 +0100)
17 files changed:
DeDRM_plugin/androidkindlekey.py
DeDRM_plugin/convert2xml.py
DeDRM_plugin/erdr2pml.py
DeDRM_plugin/genbook.py
DeDRM_plugin/ignobleepub.py
DeDRM_plugin/ignoblekey.py
DeDRM_plugin/ignoblekeyfetch.py
DeDRM_plugin/ignoblekeygen.py
DeDRM_plugin/ignoblepdf.py
DeDRM_plugin/ineptepub.py
DeDRM_plugin/k4mobidedrm.py
DeDRM_plugin/kfxdedrm.py
DeDRM_plugin/kgenpids.py
DeDRM_plugin/kindlekey.py
DeDRM_plugin/mobidedrm.py
DeDRM_plugin/topazextract.py
DeDRM_plugin/zipfix.py

index 4759e1bd8d2da5242cf6b940af2cc855304714de..dcd4d04e0546c9598578580edfeb49f2dc840220 100644 (file)
@@ -95,10 +95,8 @@ def unicode_argv():
         # this should never happen
         return ["kindlekey.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 class DrmException(Exception):
     pass
@@ -336,7 +334,7 @@ def cli_main():
     sys.stderr=SafeUnbuffered(sys.stderr)
     argv=unicode_argv()
     progname = os.path.basename(argv[0])
-    print("{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
+    print("{0} v{1}\nCopyright © 2010-2020 Thom, Apprentice Harper et al.".format(progname,__version__))
 
     try:
         opts, args = getopt.getopt(argv[1:], "hb:")
@@ -386,48 +384,48 @@ def cli_main():
 
 def gui_main():
     try:
-        import Tkinter
-        import Tkconstants
-        import tkMessageBox
-        import tkFileDialog
+        import tkinter
+        import tkinter.constants
+        import tkinter.messagebox
+        import tkinter.filedialog
     except:
-        print("Tkinter not installed")
+        print("tkinter not installed")
         return cli_main()
 
-    class DecryptionDialog(Tkinter.Frame):
+    class DecryptionDialog(tkinter.Frame):
         def __init__(self, root):
-            Tkinter.Frame.__init__(self, root, border=5)
-            self.status = Tkinter.Label(self, text="Select backup.ab file")
-            self.status.pack(fill=Tkconstants.X, expand=1)
-            body = Tkinter.Frame(self)
-            body.pack(fill=Tkconstants.X, expand=1)
-            sticky = Tkconstants.E + Tkconstants.W
+            tkinter.Frame.__init__(self, root, border=5)
+            self.status = tkinter.Label(self, text="Select backup.ab file")
+            self.status.pack(fill=tkinter.constants.X, expand=1)
+            body = tkinter.Frame(self)
+            body.pack(fill=tkinter.constants.X, expand=1)
+            sticky = tkinter.constants.E + tkinter.constants.W
             body.grid_columnconfigure(1, weight=2)
-            Tkinter.Label(body, text="Backup file").grid(row=0, column=0)
-            self.keypath = Tkinter.Entry(body, width=40)
+            tkinter.Label(body, text="Backup file").grid(row=0, column=0)
+            self.keypath = tkinter.Entry(body, width=40)
             self.keypath.grid(row=0, column=1, sticky=sticky)
             self.keypath.insert(2, "backup.ab")
-            button = Tkinter.Button(body, text="...", command=self.get_keypath)
+            button = tkinter.Button(body, text="...", command=self.get_keypath)
             button.grid(row=0, column=2)
-            buttons = Tkinter.Frame(self)
+            buttons = tkinter.Frame(self)
             buttons.pack()
-            button2 = Tkinter.Button(
+            button2 = tkinter.Button(
                 buttons, text="Extract", width=10, command=self.generate)
-            button2.pack(side=Tkconstants.LEFT)
-            Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
-            button3 = Tkinter.Button(
+            button2.pack(side=tkinter.constants.LEFT)
+            tkinter.Frame(buttons, width=10).pack(side=tkinter.constants.LEFT)
+            button3 = tkinter.Button(
                 buttons, text="Quit", width=10, command=self.quit)
-            button3.pack(side=Tkconstants.RIGHT)
+            button3.pack(side=tkinter.constants.RIGHT)
 
         def get_keypath(self):
-            keypath = tkFileDialog.askopenfilename(
+            keypath = tkinter.filedialog.askopenfilename(
                 parent=None, title="Select backup.ab file",
                 defaultextension=".ab",
                 filetypes=[('adb backup com.amazon.kindle', '.ab'),
                            ('All Files', '.*')])
             if keypath:
                 keypath = os.path.normpath(keypath)
-                self.keypath.delete(0, Tkconstants.END)
+                self.keypath.delete(0, tkinter.constants.END)
                 self.keypath.insert(0, keypath)
             return
 
@@ -447,7 +445,7 @@ def gui_main():
                     with open(outfile, 'w') as keyfileout:
                         keyfileout.write(key)
                     success = True
-                    tkMessageBox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
+                    tkinter.messagebox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
             except Exception as e:
                 self.status['text'] = "Error: {0}".format(e.args[0])
                 return
@@ -455,11 +453,11 @@ def gui_main():
 
     argv=unicode_argv()
     progpath, progname = os.path.split(argv[0])
-    root = Tkinter.Tk()
+    root = tkinter.Tk()
     root.title("Kindle for Android Key Extraction v.{0}".format(__version__))
     root.resizable(True, False)
     root.minsize(300, 0)
-    DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1)
+    DecryptionDialog(root).pack(fill=tkinter.constants.X, expand=1)
     root.mainloop()
     return 0
 
index a0bf188216f010de9e87e8549d1b8a30a841a532..3249db5dfe0780b7f19a8e213f2cd1a2f6f47888 100644 (file)
@@ -5,18 +5,25 @@
 # For use with Topaz Scripts Version 2.6
 # Python 3, September 2020
 
-class Unbuffered:
+# Wrap a stream so that output gets flushed immediately
+# and also make sure that any unicode strings get
+# encoded using "replace" before writing them.
+class SafeUnbuffered:
     def __init__(self, stream):
         self.stream = stream
+        self.encoding = stream.encoding
+        if self.encoding == None:
+            self.encoding = "utf-8"
     def write(self, data):
+        if isinstance(data, str):
+            data = data.encode(self.encoding,"replace")
         self.stream.buffer.write(data)
         self.stream.buffer.flush()
+
     def __getattr__(self, attr):
         return getattr(self.stream, attr)
 
 import sys
-sys.stdout=Unbuffered(sys.stdout)
-
 import csv
 import os
 import getopt
@@ -834,6 +841,8 @@ def usage():
 #
 
 def main(argv):
+    sys.stdout=SafeUnbuffered(sys.stdout)
+    sys.stderr=SafeUnbuffered(sys.stderr)
     dictFile = ""
     pageFile = ""
     debug = False
index bd3eeff960c7744b134c0ea65614e4e7c3170e62..6c65ac22a70192810e7e83f407df19be0c933478 100644 (file)
@@ -128,10 +128,8 @@ def unicode_argv():
         # this should never happen
         return ["mobidedrm.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 Des = None
 if iswindows:
@@ -516,7 +514,7 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
             # remove temporary directory
             shutil.rmtree(outdir, True)
             print("Output is {0}".format(pmlzname))
-        else: 
+        else:
             print("Output is in {0}".format(outdir))
         print("done")
     except ValueError as e:
index fdf37074f99074868bd2a687894e2e868ca2c181..dca569756fbb1330de0f2b7d46cf88a9c1fc7b05 100644 (file)
@@ -4,18 +4,25 @@
 # Python 3 for calibre 5.0
 from __future__ import print_function
 
-class Unbuffered:
+# Wrap a stream so that output gets flushed immediately
+# and also make sure that any unicode strings get
+# encoded using "replace" before writing them.
+class SafeUnbuffered:
     def __init__(self, stream):
         self.stream = stream
+        self.encoding = stream.encoding
+        if self.encoding == None:
+            self.encoding = "utf-8"
     def write(self, data):
+        if isinstance(data, str):
+            data = data.encode(self.encoding,"replace")
         self.stream.buffer.write(data)
         self.stream.buffer.flush()
+
     def __getattr__(self, attr):
         return getattr(self.stream, attr)
 
 import sys
-sys.stdout=Unbuffered(sys.stdout)
-
 import csv
 import os
 import getopt
@@ -687,6 +694,8 @@ def usage():
 
 
 def main(argv):
+    sys.stdout=SafeUnbuffered(sys.stdout)
+    sys.stderr=SafeUnbuffered(sys.stderr)
     bookDir = ''
     if len(argv) == 0:
         argv = sys.argv
index d10b20960b343fb83e820e89284b25ab111f79d2..66c1e99f42f6a176da4d48b881764d114092745b 100644 (file)
@@ -95,10 +95,8 @@ def unicode_argv():
                     range(start, argc.value)]
         return ["ineptepub.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class IGNOBLEError(Exception):
index b47a07a445f9fd79bf1d6597cd131796403bee8b..ce3fe0c121c1c5eeea6c331eec60d93ef821dd9f 100644 (file)
@@ -83,10 +83,8 @@ def unicode_argv():
         # this should never happen
         return ["ignoblekey.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 class DrmException(Exception):
     pass
index a844fd8d06a95ab347a5037e45defbe831b05b70..89e6d627553d486a13f1be6c911eac8851ecef53 100644 (file)
@@ -90,10 +90,8 @@ def unicode_argv():
         # this should never happen
         return ["ignoblekeyfetch.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class IGNOBLEError(Exception):
@@ -109,18 +107,17 @@ def fetch_key(email, password):
     import random
     random = "%030x" % random.randrange(16**30)
 
-    import urllib, urllib2, re
+    import urllib.parse, urllib.request, re
 
     # try the URL from nook for PC
     fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
-    fetch_url += urllib.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress="
-    fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
+    fetch_url += urllib.parse.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress="
+    fetch_url += urllib.parse.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
     #print fetch_url
 
     found = ''
     try:
-        req = urllib2.Request(fetch_url)
-        response = urllib2.urlopen(req)
+        response = urllib.request.urlopen(fetch_url)
         the_page = response.read()
         #print the_page
         found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
@@ -129,14 +126,13 @@ def fetch_key(email, password):
     if len(found)!=28:
         # try the URL from android devices
         fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword="
-        fetch_url += urllib.quote(password,'')+"&devID=hobbes_9.3.50818_"+random+"&emailAddress="
-        fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
+        fetch_url += urllib.parse.quote(password,'')+"&devID=hobbes_9.3.50818_"+random+"&emailAddress="
+        fetch_url += urllib.parse.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
         #print fetch_url
 
         found = ''
         try:
-            req = urllib2.Request(fetch_url)
-            response = urllib2.urlopen(req)
+            response = urllib.request.urlopen(fetch_url)
             the_page = response.read()
             #print the_page
             found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
index 489f2b9fac7529794f93a498fbcd4c7f56933f55..59a80857009d7a2ac9b6000e30751b3bdcdf25af 100644 (file)
@@ -42,6 +42,7 @@ __version__ = "3.0"
 import sys
 import os
 import hashlib
+import base64
 
 # Wrap a stream so that output gets flushed immediately
 # and also make sure that any unicode strings get
@@ -99,10 +100,8 @@ def unicode_argv():
         # this should never happen
         return ["ignoblekeygen.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class IGNOBLEError(Exception):
@@ -195,23 +194,24 @@ def normalize_name(name):
 
 def generate_key(name, ccn):
     # remove spaces and case from name and CC numbers.
-    if type(name)==bytes:
+    name = normalize_name(name)
+    ccn = normalize_name(ccn)
+
+    if type(name)==str:
         name = name.encode('utf-8')
-    if type(ccn)==bytes:
+    if type(ccn)==str:
         ccn = ccn.encode('utf-8')
 
-    name = normalize_name(name) + '\x00'
-    ccn = normalize_name(ccn) + '\x00'
+    name = name + b'\x00'
+    ccn = ccn + b'\x00'
 
     name_sha = hashlib.sha1(name).digest()[:16]
     ccn_sha = hashlib.sha1(ccn).digest()[:16]
     both_sha = hashlib.sha1(name + ccn).digest()
     aes = AES(ccn_sha, name_sha)
-    crypt = aes.encrypt(both_sha + ('\x0c' * 0x0c))
+    crypt = aes.encrypt(both_sha + (b'\x0c' * 0x0c))
     userkey = hashlib.sha1(crypt).digest()
-    return userkey.encode('base64')
-
-
+    return base64.b64encode(userkey)
 
 
 def cli_main():
index 2b193cb728d1882d07b45eca93238c4f04d3c2a5..d594428865c31c41f8cdfc8cabdd75e658a42358 100644 (file)
@@ -85,10 +85,8 @@ def unicode_argv():
                     xrange(start, argc.value)]
         return ["ignoblepdf.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class IGNOBLEError(Exception):
@@ -241,7 +239,10 @@ ARC4, AES = _load_crypto()
 try:
     from cStringIO import StringIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from StringIO import StringIO
+    except ImportError:
+        from io import StringIO
 
 
 # Do we generate cross reference streams on output?
@@ -546,7 +547,7 @@ class PSBaseParser(object):
         except ValueError:
             pass
         return (self.parse_main, j)
-        
+
     def parse_decimal(self, s, i):
         m = END_NUMBER.search(s, i)
         if not m:
index f4b9ca3d75d2f3e634ad0e315556a1633351b1a3..11661e7ab06fc65dd0b5bc96e98f57a1771c8cde 100644 (file)
@@ -102,10 +102,8 @@ def unicode_argv():
                     range(start, argc.value)]
         return ["ineptepub.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class ADEPTError(Exception):
index 18addc2d17beff4cc8875b655a7712666adc29b6..d7775d663a5af92c229a35614c81712e6a807134 100644 (file)
@@ -146,10 +146,8 @@ def unicode_argv():
         # this should never happen
         return ["mobidedrm.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 # cleanup unicode filenames
 # borrowed from calibre from calibre/src/calibre/__init__.py
@@ -337,7 +335,7 @@ def cli_main():
         if o == "-p":
             if a == None :
                 raise DrmException("Invalid parameter for -p")
-            pids = a.split(',')
+            pids = a.encode('utf-8').split(b',')
         if o == "-s":
             if a == None :
                 raise DrmException("Invalid parameter for -s")
index 875c4a183f778027008252133606e1652b57683f..6c1d86aa470999c2c527c337a6588b75e52a58b4 100644 (file)
@@ -18,11 +18,6 @@ except ImportError:
     except ImportError:
         from io import StringIO
 
-try:
-    from calibre_plugins.dedrm import ion
-except ImportError:
-    import ion
-
 
 __license__ = 'GPL v3'
 __version__ = '2.0'
@@ -38,6 +33,10 @@ class KFXZipBook:
         return (None, None)
 
     def processBook(self, totalpids):
+        try:
+            import ion
+        except:
+            from calibre_plugins.dedrm import ion
         with zipfile.ZipFile(self.infile, 'r') as zf:
             for filename in zf.namelist():
                 with zf.open(filename) as fh:
index b1006411befb41206b2a2d326f8ee969907ea107..373598e237aa5a00aacf498fa9fbf094ccea7908 100644 (file)
@@ -205,7 +205,7 @@ def getK4Pids(rec209, token, kindleDatabase):
 
     try:
         # Get the kindle account token, if present
-        kindleAccountToken = bytearray.fromhex((kindleDatabase[1])['kindle.account.tokens']).decode()
+        kindleAccountToken = bytearray.fromhex((kindleDatabase[1])[b'kindle.account.tokens']).decode()
 
     except KeyError:
         kindleAccountToken=""
@@ -219,37 +219,37 @@ def getK4Pids(rec209, token, kindleDatabase):
         # See if we have the info to generate the DSN
         try:
             # Get the Mazama Random number
-            MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])['MazamaRandomNumber']).decode()
+            MazamaRandomNumber = bytearray.fromhex((kindleDatabase[1])[b'MazamaRandomNumber']).decode()
             #print "Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
 
             try:
                 # Get the SerialNumber token, if present
-                IDString = bytearray.fromhex((kindleDatabase[1])['SerialNumber']).decode()
+                IDString = bytearray.fromhex((kindleDatabase[1])[b'SerialNumber']).decode()
                 print("Got SerialNumber from database {0}".format(kindleDatabase[0]))
             except KeyError:
                  # Get the IDString we added
-                IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode()
+                IDString = bytearray.fromhex((kindleDatabase[1])[b'IDString']).decode()
 
             try:
                 # Get the UsernameHash token, if present
-                encodedUsername = bytearray.fromhex((kindleDatabase[1])['UsernameHash']).decode()
+                encodedUsername = bytearray.fromhex((kindleDatabase[1])[b'UsernameHash']).decode()
                 print("Got UsernameHash from database {0}".format(kindleDatabase[0]))
             except KeyError:
                 # Get the UserName we added
-                UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode()
+                UserName = bytearray.fromhex((kindleDatabase[1])[b'UserName']).decode()
                 # encode it
-                encodedUsername = encodeHash(UserName.encode(),charMap1)
+                encodedUsername = encodeHash(UserName,charMap1)
                 #print "encodedUsername",encodedUsername.encode('hex')
         except KeyError:
             print("Keys not found in the database {0}.".format(kindleDatabase[0]))
             return pids
 
         # Get the ID string used
-        encodedIDString = encodeHash(IDString.encode(),charMap1)
+        encodedIDString = encodeHash(IDString,charMap1)
         #print "encodedIDString",encodedIDString.encode('hex')
 
         # concat, hash and encode to calculate the DSN
-        DSN = encode(SHA1((MazamaRandomNumber+encodedIDString+encodedUsername).encode()),charMap1)
+        DSN = encode(SHA1(MazamaRandomNumber+encodedIDString+encodedUsername),charMap1)
         #print "DSN",DSN.encode('hex')
         pass
 
index 05b114231f0442cf5858d8a3572ad6a86ec7d1b6..6c81f4c67d50eea6352533a6001806fda9dd2a99 100644 (file)
@@ -39,6 +39,7 @@ import sys, os, re
 from struct import pack, unpack, unpack_from
 import json
 import getopt
+import traceback
 
 try:
     RegError
@@ -58,10 +59,11 @@ class SafeUnbuffered:
         if self.encoding == None:
             self.encoding = "utf-8"
     def write(self, data):
-        if isinstance(data,unicode):
+        if isinstance(data, str):
             data = data.encode(self.encoding,"replace")
-        self.stream.write(data)
-        self.stream.flush()
+        self.stream.buffer.write(data)
+        self.stream.buffer.flush()
+
     def __getattr__(self, attr):
         return getattr(self.stream, attr)
 
@@ -99,15 +101,13 @@ def unicode_argv():
             # Remove Python executable and commands if present
             start = argc.value - len(sys.argv)
             return [argv[i] for i in
-                    xrange(start, argc.value)]
+                    range(start, argc.value)]
         # if we don't have any arguments at all, just pass back script name
         # this should never happen
         return ["kindlekey.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = "utf-8"
-        return arg
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 class DrmException(Exception):
     pass
@@ -155,13 +155,13 @@ def primes(n):
 
 # Encode the bytes in data with the characters in map
 def encode(data, map):
-    result = ''
+    result = b''
     for char in data:
-        value = ord(char)
+        value = char
         Q = (value ^ 0x80) // len(map)
         R = value % len(map)
-        result += map[Q]
-        result += map[R]
+        result += bytes(map[Q])
+        result += bytes(map[R])
     return result
 
 # Hash the bytes in data and then encode the digest with the characters in map
@@ -170,7 +170,7 @@ def encodeHash(data,map):
 
 # Decode the string in data with the characters in map. Returns the decoded bytes
 def decode(data,map):
-    result = ''
+    result = b''
     for i in range (0,len(data)-1,2):
         high = map.find(data[i])
         low = map.find(data[i+1])
@@ -833,12 +833,12 @@ if iswindows:
 
     # Various character maps used to decrypt kindle info values.
     # Probably supposed to act as obfuscation
-    charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
-    charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
+    charMap2 = b"AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
+    charMap5 = b"AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
     # New maps in K4PC 1.9.0
-    testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
-    testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
-    testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
+    testMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
+    testMap6 = b"9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
+    testMap8 = b"YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
 
     # interface with Windows OS Routines
     class DataBlob(Structure):
@@ -900,9 +900,9 @@ if iswindows:
                 # double the buffer size
                 buffer = create_unicode_buffer(len(buffer) * 2)
                 size.value = len(buffer)
-            
+
             # replace any non-ASCII values with 0xfffd
-            for i in xrange(0,len(buffer)):
+            for i in range(0,len(buffer)):
                 if buffer[i]>"\u007f":
                     #print "swapping char "+str(i)+" ("+buffer[i]+")"
                     buffer[i] = "\ufffd"
@@ -985,7 +985,7 @@ if iswindows:
                 found = True
                 print('Found K4PC 1.25+ kinf2018 file: ' + kinfopath.encode('ascii','ignore'))
                 kInfoFiles.append(kinfopath)
-                
+
             # look for (K4PC 1.9.0 and later) .kinf2011 file
             kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
             if os.path.isfile(kinfopath):
@@ -1023,28 +1023,28 @@ if iswindows:
     # database of keynames and values
     def getDBfromFile(kInfoFile):
         names = [\
-            'kindle.account.tokens',\
-            'kindle.cookie.item',\
-            'eulaVersionAccepted',\
-            'login_date',\
-            'kindle.token.item',\
-            'login',\
-            'kindle.key.item',\
-            'kindle.name.info',\
-            'kindle.device.info',\
-            'MazamaRandomNumber',\
-            'max_date',\
-            'SIGVERIF',\
-            'build_version',\
-            'SerialNumber',\
-            'UsernameHash',\
-            'kindle.directedid.info',\
-            'DSN',\
-            'kindle.accounttype.info',\
-            'krx.flashcardsplugin.data.encryption_key',\
-            'krx.notebookexportplugin.data.encryption_key',\
-            'proxy.http.password',\
-            'proxy.http.username'
+            b'kindle.account.tokens',\
+            b'kindle.cookie.item',\
+            b'eulaVersionAccepted',\
+            b'login_date',\
+            b'kindle.token.item',\
+            b'login',\
+            b'kindle.key.item',\
+            b'kindle.name.info',\
+            b'kindle.device.info',\
+            b'MazamaRandomNumber',\
+            b'max_date',\
+            b'SIGVERIF',\
+            b'build_version',\
+            b'SerialNumber',\
+            b'UsernameHash',\
+            b'kindle.directedid.info',\
+            b'DSN',\
+            b'kindle.accounttype.info',\
+            b'krx.flashcardsplugin.data.encryption_key',\
+            b'krx.notebookexportplugin.data.encryption_key',\
+            b'proxy.http.password',\
+            b'proxy.http.username'
             ]
         DB = {}
         with open(kInfoFile, 'rb') as infoReader:
@@ -1053,7 +1053,7 @@ if iswindows:
         # the .kinf file uses "/" to separate it into records
         # so remove the trailing "/" to make it easy to use split
         data = data[:-1]
-        items = data.split('/')
+        items = data.split(b'/')
 
         # starts with an encoded and encrypted header blob
         headerblob = items.pop(0)
@@ -1095,7 +1095,7 @@ if iswindows:
             # read and store in rcnt records of data
             # that make up the contents value
             edlst = []
-            for i in xrange(rcnt):
+            for i in range(rcnt):
                 item = items.pop(0)
                 edlst.append(item)
 
@@ -1276,8 +1276,8 @@ elif isosx:
     LibCrypto = _load_crypto()
 
     # Various character maps used to decrypt books. Probably supposed to act as obfuscation
-    charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
-    charMap2 = 'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM'
+    charMap1 = b'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
+    charMap2 = b'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM'
 
     # For kinf approach of K4Mac 1.6.X or later
     # On K4PC charMap5 = 'AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE'
@@ -1285,7 +1285,7 @@ elif isosx:
     charMap5 = charMap2
 
     # new in K4M 1.9.X
-    testMap8 = 'YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD'
+    testMap8 = b'YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD'
 
     # uses a sub process to get the Hard Drive Serial Number using ioreg
     # returns serial numbers of all internal hard drive drives
@@ -1299,11 +1299,11 @@ elif isosx:
         p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
         out1, out2 = p.communicate()
         #print out1
-        reslst = out1.split('\n')
+        reslst = out1.split(b'\n')
         cnt = len(reslst)
-        for j in xrange(cnt):
+        for j in range(cnt):
             resline = reslst[j]
-            pp = resline.find('\"Serial Number\" = \"')
+            pp = resline.find(b'\"Serial Number\" = \"')
             if pp >= 0:
                 sernum = resline[pp+19:-1]
                 sernums.append(sernum.strip())
@@ -1315,12 +1315,12 @@ elif isosx:
         cmdline = cmdline.encode(sys.getfilesystemencoding())
         p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
         out1, out2 = p.communicate()
-        reslst = out1.split('\n')
+        reslst = out1.split(b'\n')
         cnt = len(reslst)
-        for j in xrange(cnt):
+        for j in range(cnt):
             resline = reslst[j]
-            if resline.startswith('/dev'):
-                (devpart, mpath) = resline.split(' on ')[:2]
+            if resline.startswith(b'/dev'):
+                (devpart, mpath) = resline.split(b' on ')[:2]
                 dpart = devpart[5:]
                 names.append(dpart)
         return names
@@ -1336,11 +1336,11 @@ elif isosx:
         p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
         out1, out2 = p.communicate()
         #print out1
-        reslst = out1.split('\n')
+        reslst = out1.split(b'\n')
         cnt = len(reslst)
-        for j in xrange(cnt):
+        for j in range(cnt):
             resline = reslst[j]
-            pp = resline.find('\"UUID\" = \"')
+            pp = resline.find(b'\"UUID\" = \"')
             if pp >= 0:
                 uuidnum = resline[pp+10:-1]
                 uuidnum = uuidnum.strip()
@@ -1356,16 +1356,16 @@ elif isosx:
         cmdline = cmdline.encode(sys.getfilesystemencoding())
         p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
         out1, out2 = p.communicate()
-        reslst = out1.split('\n')
+        reslst = out1.split(b'\n')
         cnt = len(reslst)
-        for j in xrange(cnt):
+        for j in range(cnt):
             resline = reslst[j]
-            pp = resline.find('Ethernet Address: ')
+            pp = resline.find(b'Ethernet Address: ')
             if pp >= 0:
                 #print resline
                 macnum = resline[pp+18:]
                 macnum = macnum.strip()
-                maclst = macnum.split(':')
+                maclst = macnum.split(b':')
                 n = len(maclst)
                 if n != 6:
                     continue
@@ -1373,7 +1373,7 @@ elif isosx:
                 # now munge it up the way Kindle app does
                 # by xoring it with 0xa5 and swapping elements 3 and 4
                 for i in range(6):
-                    maclst[i] = int('0x' + maclst[i], 0)
+                    maclst[i] = int(b'0x' + maclst[i], 0)
                 mlst = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
                 mlst[5] = maclst[5] ^ 0xa5
                 mlst[4] = maclst[3] ^ 0xa5
@@ -1381,7 +1381,7 @@ elif isosx:
                 mlst[2] = maclst[2] ^ 0xa5
                 mlst[1] = maclst[1] ^ 0xa5
                 mlst[0] = maclst[0] ^ 0xa5
-                macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
+                macnum = b'%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
                 #print 'munged mac', macnum
                 macnums.append(macnum)
         return macnums
@@ -1391,7 +1391,7 @@ elif isosx:
     def GetUserName():
         username = os.getenv('USER')
         #print "Username:",username
-        return username
+        return username.encode('utf-8')
 
     def GetIDStrings():
         # Return all possible ID Strings
@@ -1400,7 +1400,7 @@ elif isosx:
         strings.extend(GetVolumesSerialNumbers())
         strings.extend(GetDiskPartitionNames())
         strings.extend(GetDiskPartitionUUIDs())
-        strings.append('9999999999')
+        strings.append(b'9999999999')
         #print "ID Strings:\n",strings
         return strings
 
@@ -1408,8 +1408,8 @@ elif isosx:
     # unprotect the new header blob in .kinf2011
     # used in Kindle for Mac Version >= 1.9.0
     def UnprotectHeaderData(encryptedData):
-        passwdData = 'header_key_data'
-        salt = 'HEADER.2011'
+        passwdData = b'header_key_data'
+        salt = b'HEADER.2011'
         iter = 0x80
         keylen = 0x100
         crp = LibCrypto()
@@ -1424,7 +1424,7 @@ elif isosx:
     # implements an Pseudo Mac Version of Windows built-in Crypto routine
     class CryptUnprotectData(object):
         def __init__(self, entropy, IDString):
-            sp = GetUserName() + '+@#$%+' + IDString
+            sp = GetUserName() + b'+@#$%+' + IDString
             passwdData = encode(SHA256(sp),charMap2)
             salt = entropy
             self.crp = LibCrypto()
@@ -1503,59 +1503,79 @@ elif isosx:
     # database of keynames and values
     def getDBfromFile(kInfoFile):
         names = [\
-            'kindle.account.tokens',\
-            'kindle.cookie.item',\
-            'eulaVersionAccepted',\
-            'login_date',\
-            'kindle.token.item',\
-            'login',\
-            'kindle.key.item',\
-            'kindle.name.info',\
-            'kindle.device.info',\
-            'MazamaRandomNumber',\
-            'max_date',\
-            'SIGVERIF',\
-            'build_version',\
-            'SerialNumber',\
-            'UsernameHash',\
-            'kindle.directedid.info',\
-            'DSN'
-            ]
+            b'kindle.account.tokens',\
+            b'kindle.cookie.item',\
+            b'eulaVersionAccepted',\
+            b'login_date',\
+            b'kindle.token.item',\
+            b'login',\
+            b'kindle.key.item',\
+            b'kindle.name.info',\
+            b'kindle.device.info',\
+            b'MazamaRandomNumber',\
+            b'max_date',\
+            b'SIGVERIF',\
+            b'build_version',\
+            b'SerialNumber',\
+            b'UsernameHash',\
+            b'kindle.directedid.info',\
+            b'DSN'
+            b'kindle.accounttype.info',\
+            b'krx.flashcardsplugin.data.encryption_key',\
+            b'krx.notebookexportplugin.data.encryption_key',\
+            b'proxy.http.password',\
+            b'proxy.http.username'
+           ]
         with open(kInfoFile, 'rb') as infoReader:
             filedata = infoReader.read()
 
         data = filedata[:-1]
-        items = data.split('/')
+        items = data.split(b'/')
         IDStrings = GetIDStrings()
+        print ("trying username ", GetUserName())
         for IDString in IDStrings:
-            #print "trying IDString:",IDString
+            print ("trying IDString:",IDString)
             try:
                 DB = {}
-                items = data.split('/')
-               
+                items = data.split(b'/')
+
                 # the headerblob is the encrypted information needed to build the entropy string
                 headerblob = items.pop(0)
+                #print ("headerblob: ",headerblob)
                 encryptedValue = decode(headerblob, charMap1)
+                #print ("encryptedvalue: ",encryptedValue)
                 cleartext = UnprotectHeaderData(encryptedValue)
+                print ("cleartext: ",cleartext)
 
                 # now extract the pieces in the same way
-                pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
+                pattern = re.compile(rb'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
                 for m in re.finditer(pattern, cleartext):
                     version = int(m.group(1))
                     build = m.group(2)
                     guid = m.group(4)
 
+                print ("version",version)
+                print ("build",build)
+                print ("guid",guid,"\n")
+
                 if version == 5:  # .kinf2011: identical to K4PC, except the build number gets multiplied
-                    entropy = str(0x2df * int(build)) + guid
+                    entropy = bytes(0x2df * int(build)) + guid
                     cud = CryptUnprotectData(entropy,IDString)
+                    print ("entropy",entropy)
+                    print ("cud",cud)
 
                 elif version == 6:  # .kinf2018: identical to K4PC
-                    salt = str(0x6d8 * int(build)) + guid
-                    sp = GetUserName() + '+@#$%+' + IDString
+                    salt = bytes(0x6d8 * int(build)) + guid
+                    sp = GetUserName() + b'+@#$%+' + IDString
                     passwd = encode(SHA256(sp), charMap5)
                     key = LibCrypto().keyivgen(passwd, salt, 10000, 0x400)[:32]
 
-                # loop through the item records until all are processed
+                    print ("salt",salt)
+                    print ("sp",sp)
+                    print ("passwd",passwd)
+                    print ("key",key)
+
+               # loop through the item records until all are processed
                 while len(items) > 0:
 
                     # get the first item record
@@ -1564,7 +1584,7 @@ elif isosx:
                     # the first 32 chars of the first record of a group
                     # is the MD5 hash of the key name encoded by charMap5
                     keyhash = item[0:32]
-                    keyname = 'unknown'
+                    keyname = b'unknown'
 
                     # unlike K4PC the keyhash is not used in generating entropy
                     # entropy = SHA1(keyhash) + added_entropy
@@ -1580,16 +1600,16 @@ elif isosx:
                     # read and store in rcnt records of data
                     # that make up the contents value
                     edlst = []
-                    for i in xrange(rcnt):
+                    for i in range(rcnt):
                         item = items.pop(0)
                         edlst.append(item)
 
-                    keyname = 'unknown'
+                    keyname = b'unknown'
                     for name in names:
                         if encodeHash(name,testMap8) == keyhash:
                             keyname = name
                             break
-                    if keyname == 'unknown':
+                    if keyname == b'unknown':
                         keyname = keyhash
 
                     # the testMap8 encoded contents data has had a length
@@ -1603,7 +1623,7 @@ elif isosx:
                     # (in other words split 'about' 2/3rds of the way through)
 
                     # move first offsets chars to end to align for decode by testMap8
-                    encdata = ''.join(edlst)
+                    encdata = b''.join(edlst)
                     contlen = len(encdata)
 
                     # now properly split and recombine
@@ -1643,7 +1663,9 @@ elif isosx:
 
                 if len(DB)>6:
                     break
-            except:
+
+            except Exception:
+                print (traceback.format_exc())
                 pass
         if len(DB)>6:
             # store values used in decryption
@@ -1709,7 +1731,7 @@ def cli_main():
     sys.stderr=SafeUnbuffered(sys.stderr)
     argv=unicode_argv()
     progname = os.path.basename(argv[0])
-    print("{0} v{1}\nCopyright © 2010-2016 by some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__))
+    print("{0} v{1}\nCopyright © 2010-2020 by some_updates, Apprentice Harper et al.".format(progname,__version__))
 
     try:
         opts, args = getopt.getopt(argv[1:], "hk:")
@@ -1800,6 +1822,7 @@ def gui_main():
     return 0
 
 if __name__ == '__main__':
+    print ("here")
     if len(sys.argv) > 1:
         sys.exit(cli_main())
     sys.exit(gui_main())
index 05c7b072d58af9d24716b25a8746adb0138f033d..fff0969283c7e47c8c3d84a6a12d05a756e6bcc8 100644 (file)
@@ -137,10 +137,8 @@ def unicode_argv():
         # this should never happen
         return ["mobidedrm.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = 'utf-8'
-        return sys.argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 
 class DrmException(Exception):
@@ -246,7 +244,7 @@ class MobiBook:
         pass
 
     def __init__(self, infile):
-        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
+        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__))
 
         try:
             from alfcrypto import Pukall_Cipher
@@ -522,7 +520,7 @@ def cli_main():
     argv=unicode_argv()
     progname = os.path.basename(argv[0])
     if len(argv)<3 or len(argv)>4:
-        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2017 The Dark Reverser, Apprentice Harper et al.".format(__version__))
+        print("MobiDeDrm v{0:s}.\nCopyright © 2008-2020 The Dark Reverser, Apprentice Harper et al.".format(__version__))
         print("Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks")
         print("Usage:")
         print("    {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname))
@@ -531,7 +529,8 @@ def cli_main():
         infile = argv[1]
         outfile = argv[2]
         if len(argv) == 4:
-            pidlist = argv[3].split(',')
+            # convert from unicode to bytearray before splitting.
+            pidlist = argv[3].encode('utf-8').split(b',')
         else:
             pidlist = []
         try:
index e1b97e068829d3b2dd1aec5caec41b4b953e8877..f56626b770883177817634773990d831c42145e1 100644 (file)
@@ -18,7 +18,10 @@ import zlib, zipfile, tempfile, shutil
 import traceback
 from struct import pack
 from struct import unpack
-from calibre_plugins.dedrm.alfcrypto import Topaz_Cipher
+try:
+    from calibre_plugins.dedrm.alfcrypto import Topaz_Cipher
+except:
+    from alfcrypto import Topaz_Cipher
 
 class SafeUnbuffered:
     def __init__(self, stream):
@@ -70,10 +73,8 @@ def unicode_argv():
         # this should never happen
         return ["mobidedrm.py"]
     else:
-        argvencoding = sys.stdin.encoding
-        if argvencoding == None:
-            argvencoding = 'utf-8'
-        return argv
+        argvencoding = sys.stdin.encoding or "utf-8"
+        return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
 
 #global switch
 debug = False
index 9745495d9c9b0ec860b50e5b67a2b9e4a9aaabff..fb6558cdaf0c4df22ecf4f30959fe3ec4bc82f0b 100644 (file)
@@ -22,7 +22,10 @@ __version__ = "1.1"
 
 import sys
 import zlib
-import calibre_plugins.dedrm.zipfilerugged as zipfilerugged
+try:
+    import zipfilerugged
+except:
+    import calibre_plugins.dedrm.zipfilerugged as zipfilerugged
 import os
 import os.path
 import getopt