Mostly Mac fixes. mobidedrm.py now works, and k4mobidedrm for at least some input. kindlekey.py should be working too. But lots more changes and testing to do.

pull/1296/head
Apprentice Harper 4 years ago
parent 2eb31c8fb5
commit e31752e334

@ -95,10 +95,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["kindlekey.py"] return ["kindlekey.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class DrmException(Exception): class DrmException(Exception):
pass pass
@ -336,7 +334,7 @@ def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv()
progname = os.path.basename(argv[0]) 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: try:
opts, args = getopt.getopt(argv[1:], "hb:") opts, args = getopt.getopt(argv[1:], "hb:")
@ -386,48 +384,48 @@ def cli_main():
def gui_main(): def gui_main():
try: try:
import Tkinter import tkinter
import Tkconstants import tkinter.constants
import tkMessageBox import tkinter.messagebox
import tkFileDialog import tkinter.filedialog
except: except:
print("Tkinter not installed") print("tkinter not installed")
return cli_main() return cli_main()
class DecryptionDialog(Tkinter.Frame): class DecryptionDialog(tkinter.Frame):
def __init__(self, root): def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5) tkinter.Frame.__init__(self, root, border=5)
self.status = Tkinter.Label(self, text="Select backup.ab file") self.status = tkinter.Label(self, text="Select backup.ab file")
self.status.pack(fill=Tkconstants.X, expand=1) self.status.pack(fill=tkinter.constants.X, expand=1)
body = Tkinter.Frame(self) body = tkinter.Frame(self)
body.pack(fill=Tkconstants.X, expand=1) body.pack(fill=tkinter.constants.X, expand=1)
sticky = Tkconstants.E + Tkconstants.W sticky = tkinter.constants.E + tkinter.constants.W
body.grid_columnconfigure(1, weight=2) body.grid_columnconfigure(1, weight=2)
Tkinter.Label(body, text="Backup file").grid(row=0, column=0) tkinter.Label(body, text="Backup file").grid(row=0, column=0)
self.keypath = Tkinter.Entry(body, width=40) self.keypath = tkinter.Entry(body, width=40)
self.keypath.grid(row=0, column=1, sticky=sticky) self.keypath.grid(row=0, column=1, sticky=sticky)
self.keypath.insert(2, "backup.ab") 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) button.grid(row=0, column=2)
buttons = Tkinter.Frame(self) buttons = tkinter.Frame(self)
buttons.pack() buttons.pack()
button2 = Tkinter.Button( button2 = tkinter.Button(
buttons, text="Extract", width=10, command=self.generate) buttons, text="Extract", width=10, command=self.generate)
button2.pack(side=Tkconstants.LEFT) button2.pack(side=tkinter.constants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) tkinter.Frame(buttons, width=10).pack(side=tkinter.constants.LEFT)
button3 = Tkinter.Button( button3 = tkinter.Button(
buttons, text="Quit", width=10, command=self.quit) buttons, text="Quit", width=10, command=self.quit)
button3.pack(side=Tkconstants.RIGHT) button3.pack(side=tkinter.constants.RIGHT)
def get_keypath(self): def get_keypath(self):
keypath = tkFileDialog.askopenfilename( keypath = tkinter.filedialog.askopenfilename(
parent=None, title="Select backup.ab file", parent=None, title="Select backup.ab file",
defaultextension=".ab", defaultextension=".ab",
filetypes=[('adb backup com.amazon.kindle', '.ab'), filetypes=[('adb backup com.amazon.kindle', '.ab'),
('All Files', '.*')]) ('All Files', '.*')])
if keypath: if keypath:
keypath = os.path.normpath(keypath) keypath = os.path.normpath(keypath)
self.keypath.delete(0, Tkconstants.END) self.keypath.delete(0, tkinter.constants.END)
self.keypath.insert(0, keypath) self.keypath.insert(0, keypath)
return return
@ -447,7 +445,7 @@ def gui_main():
with open(outfile, 'w') as keyfileout: with open(outfile, 'w') as keyfileout:
keyfileout.write(key) keyfileout.write(key)
success = True 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: except Exception as e:
self.status['text'] = "Error: {0}".format(e.args[0]) self.status['text'] = "Error: {0}".format(e.args[0])
return return
@ -455,11 +453,11 @@ def gui_main():
argv=unicode_argv() argv=unicode_argv()
progpath, progname = os.path.split(argv[0]) 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.title("Kindle for Android Key Extraction v.{0}".format(__version__))
root.resizable(True, False) root.resizable(True, False)
root.minsize(300, 0) root.minsize(300, 0)
DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) DecryptionDialog(root).pack(fill=tkinter.constants.X, expand=1)
root.mainloop() root.mainloop()
return 0 return 0

@ -5,18 +5,25 @@
# For use with Topaz Scripts Version 2.6 # For use with Topaz Scripts Version 2.6
# Python 3, September 2020 # 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): def __init__(self, stream):
self.stream = stream self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data, str):
data = data.encode(self.encoding,"replace")
self.stream.buffer.write(data) self.stream.buffer.write(data)
self.stream.buffer.flush() self.stream.buffer.flush()
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
import sys import sys
sys.stdout=Unbuffered(sys.stdout)
import csv import csv
import os import os
import getopt import getopt
@ -834,6 +841,8 @@ def usage():
# #
def main(argv): def main(argv):
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
dictFile = "" dictFile = ""
pageFile = "" pageFile = ""
debug = False debug = False

@ -128,10 +128,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["mobidedrm.py"] return ["mobidedrm.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
Des = None Des = None
if iswindows: if iswindows:
@ -516,7 +514,7 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
# remove temporary directory # remove temporary directory
shutil.rmtree(outdir, True) shutil.rmtree(outdir, True)
print("Output is {0}".format(pmlzname)) print("Output is {0}".format(pmlzname))
else: else:
print("Output is in {0}".format(outdir)) print("Output is in {0}".format(outdir))
print("done") print("done")
except ValueError as e: except ValueError as e:

@ -4,18 +4,25 @@
# Python 3 for calibre 5.0 # Python 3 for calibre 5.0
from __future__ import print_function 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): def __init__(self, stream):
self.stream = stream self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data, str):
data = data.encode(self.encoding,"replace")
self.stream.buffer.write(data) self.stream.buffer.write(data)
self.stream.buffer.flush() self.stream.buffer.flush()
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
import sys import sys
sys.stdout=Unbuffered(sys.stdout)
import csv import csv
import os import os
import getopt import getopt
@ -687,6 +694,8 @@ def usage():
def main(argv): def main(argv):
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
bookDir = '' bookDir = ''
if len(argv) == 0: if len(argv) == 0:
argv = sys.argv argv = sys.argv

@ -95,10 +95,8 @@ def unicode_argv():
range(start, argc.value)] range(start, argc.value)]
return ["ineptepub.py"] return ["ineptepub.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class IGNOBLEError(Exception): class IGNOBLEError(Exception):

@ -83,10 +83,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["ignoblekey.py"] return ["ignoblekey.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class DrmException(Exception): class DrmException(Exception):
pass pass

@ -90,10 +90,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["ignoblekeyfetch.py"] return ["ignoblekeyfetch.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class IGNOBLEError(Exception): class IGNOBLEError(Exception):
@ -109,18 +107,17 @@ def fetch_key(email, password):
import random import random
random = "%030x" % random.randrange(16**30) random = "%030x" % random.randrange(16**30)
import urllib, urllib2, re import urllib.parse, urllib.request, re
# try the URL from nook for PC # try the URL from nook for PC
fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword=" 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.parse.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(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
#print fetch_url #print fetch_url
found = '' found = ''
try: try:
req = urllib2.Request(fetch_url) response = urllib.request.urlopen(fetch_url)
response = urllib2.urlopen(req)
the_page = response.read() the_page = response.read()
#print the_page #print the_page
found = re.search('ccHash>(.+?)</ccHash', the_page).group(1) found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)
@ -129,14 +126,13 @@ def fetch_key(email, password):
if len(found)!=28: if len(found)!=28:
# try the URL from android devices # try the URL from android devices
fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword=" 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.parse.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(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB"
#print fetch_url #print fetch_url
found = '' found = ''
try: try:
req = urllib2.Request(fetch_url) response = urllib.request.urlopen(fetch_url)
response = urllib2.urlopen(req)
the_page = response.read() the_page = response.read()
#print the_page #print the_page
found = re.search('ccHash>(.+?)</ccHash', the_page).group(1) found = re.search('ccHash>(.+?)</ccHash', the_page).group(1)

@ -42,6 +42,7 @@ __version__ = "3.0"
import sys import sys
import os import os
import hashlib import hashlib
import base64
# Wrap a stream so that output gets flushed immediately # Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get # and also make sure that any unicode strings get
@ -99,10 +100,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["ignoblekeygen.py"] return ["ignoblekeygen.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class IGNOBLEError(Exception): class IGNOBLEError(Exception):
@ -195,23 +194,24 @@ def normalize_name(name):
def generate_key(name, ccn): def generate_key(name, ccn):
# remove spaces and case from name and CC numbers. # 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') name = name.encode('utf-8')
if type(ccn)==bytes: if type(ccn)==str:
ccn = ccn.encode('utf-8') ccn = ccn.encode('utf-8')
name = normalize_name(name) + '\x00' name = name + b'\x00'
ccn = normalize_name(ccn) + '\x00' ccn = ccn + b'\x00'
name_sha = hashlib.sha1(name).digest()[:16] name_sha = hashlib.sha1(name).digest()[:16]
ccn_sha = hashlib.sha1(ccn).digest()[:16] ccn_sha = hashlib.sha1(ccn).digest()[:16]
both_sha = hashlib.sha1(name + ccn).digest() both_sha = hashlib.sha1(name + ccn).digest()
aes = AES(ccn_sha, name_sha) 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() userkey = hashlib.sha1(crypt).digest()
return userkey.encode('base64') return base64.b64encode(userkey)
def cli_main(): def cli_main():

@ -85,10 +85,8 @@ def unicode_argv():
xrange(start, argc.value)] xrange(start, argc.value)]
return ["ignoblepdf.py"] return ["ignoblepdf.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv]
class IGNOBLEError(Exception): class IGNOBLEError(Exception):
@ -241,7 +239,10 @@ ARC4, AES = _load_crypto()
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: 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? # Do we generate cross reference streams on output?
@ -546,7 +547,7 @@ class PSBaseParser(object):
except ValueError: except ValueError:
pass pass
return (self.parse_main, j) return (self.parse_main, j)
def parse_decimal(self, s, i): def parse_decimal(self, s, i):
m = END_NUMBER.search(s, i) m = END_NUMBER.search(s, i)
if not m: if not m:

@ -102,10 +102,8 @@ def unicode_argv():
range(start, argc.value)] range(start, argc.value)]
return ["ineptepub.py"] return ["ineptepub.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
class ADEPTError(Exception): class ADEPTError(Exception):

@ -146,10 +146,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["mobidedrm.py"] return ["mobidedrm.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return argv
# cleanup unicode filenames # cleanup unicode filenames
# borrowed from calibre from calibre/src/calibre/__init__.py # borrowed from calibre from calibre/src/calibre/__init__.py
@ -337,7 +335,7 @@ def cli_main():
if o == "-p": if o == "-p":
if a == None : if a == None :
raise DrmException("Invalid parameter for -p") raise DrmException("Invalid parameter for -p")
pids = a.split(',') pids = a.encode('utf-8').split(b',')
if o == "-s": if o == "-s":
if a == None : if a == None :
raise DrmException("Invalid parameter for -s") raise DrmException("Invalid parameter for -s")

@ -18,11 +18,6 @@ except ImportError:
except ImportError: except ImportError:
from io import StringIO from io import StringIO
try:
from calibre_plugins.dedrm import ion
except ImportError:
import ion
__license__ = 'GPL v3' __license__ = 'GPL v3'
__version__ = '2.0' __version__ = '2.0'
@ -38,6 +33,10 @@ class KFXZipBook:
return (None, None) return (None, None)
def processBook(self, totalpids): def processBook(self, totalpids):
try:
import ion
except:
from calibre_plugins.dedrm import ion
with zipfile.ZipFile(self.infile, 'r') as zf: with zipfile.ZipFile(self.infile, 'r') as zf:
for filename in zf.namelist(): for filename in zf.namelist():
with zf.open(filename) as fh: with zf.open(filename) as fh:

@ -205,7 +205,7 @@ def getK4Pids(rec209, token, kindleDatabase):
try: try:
# Get the kindle account token, if present # 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: except KeyError:
kindleAccountToken="" kindleAccountToken=""
@ -219,37 +219,37 @@ def getK4Pids(rec209, token, kindleDatabase):
# See if we have the info to generate the DSN # See if we have the info to generate the DSN
try: try:
# Get the Mazama Random number # 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]) #print "Got MazamaRandomNumber from database {0}".format(kindleDatabase[0])
try: try:
# Get the SerialNumber token, if present # 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])) print("Got SerialNumber from database {0}".format(kindleDatabase[0]))
except KeyError: except KeyError:
# Get the IDString we added # Get the IDString we added
IDString = bytearray.fromhex((kindleDatabase[1])['IDString']).decode() IDString = bytearray.fromhex((kindleDatabase[1])[b'IDString']).decode()
try: try:
# Get the UsernameHash token, if present # 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])) print("Got UsernameHash from database {0}".format(kindleDatabase[0]))
except KeyError: except KeyError:
# Get the UserName we added # Get the UserName we added
UserName = bytearray.fromhex((kindleDatabase[1])['UserName']).decode() UserName = bytearray.fromhex((kindleDatabase[1])[b'UserName']).decode()
# encode it # encode it
encodedUsername = encodeHash(UserName.encode(),charMap1) encodedUsername = encodeHash(UserName,charMap1)
#print "encodedUsername",encodedUsername.encode('hex') #print "encodedUsername",encodedUsername.encode('hex')
except KeyError: except KeyError:
print("Keys not found in the database {0}.".format(kindleDatabase[0])) print("Keys not found in the database {0}.".format(kindleDatabase[0]))
return pids return pids
# Get the ID string used # Get the ID string used
encodedIDString = encodeHash(IDString.encode(),charMap1) encodedIDString = encodeHash(IDString,charMap1)
#print "encodedIDString",encodedIDString.encode('hex') #print "encodedIDString",encodedIDString.encode('hex')
# concat, hash and encode to calculate the DSN # 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') #print "DSN",DSN.encode('hex')
pass pass

@ -39,6 +39,7 @@ import sys, os, re
from struct import pack, unpack, unpack_from from struct import pack, unpack, unpack_from
import json import json
import getopt import getopt
import traceback
try: try:
RegError RegError
@ -58,10 +59,11 @@ class SafeUnbuffered:
if self.encoding == None: if self.encoding == None:
self.encoding = "utf-8" self.encoding = "utf-8"
def write(self, data): def write(self, data):
if isinstance(data,unicode): if isinstance(data, str):
data = data.encode(self.encoding,"replace") data = data.encode(self.encoding,"replace")
self.stream.write(data) self.stream.buffer.write(data)
self.stream.flush() self.stream.buffer.flush()
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.stream, attr) return getattr(self.stream, attr)
@ -99,15 +101,13 @@ def unicode_argv():
# Remove Python executable and commands if present # Remove Python executable and commands if present
start = argc.value - len(sys.argv) start = argc.value - len(sys.argv)
return [argv[i] for i in 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 # if we don't have any arguments at all, just pass back script name
# this should never happen # this should never happen
return ["kindlekey.py"] return ["kindlekey.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = "utf-8"
return arg
class DrmException(Exception): class DrmException(Exception):
pass pass
@ -155,13 +155,13 @@ def primes(n):
# Encode the bytes in data with the characters in map # Encode the bytes in data with the characters in map
def encode(data, map): def encode(data, map):
result = '' result = b''
for char in data: for char in data:
value = ord(char) value = char
Q = (value ^ 0x80) // len(map) Q = (value ^ 0x80) // len(map)
R = value % len(map) R = value % len(map)
result += map[Q] result += bytes(map[Q])
result += map[R] result += bytes(map[R])
return result return result
# Hash the bytes in data and then encode the digest with the characters in map # 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 # Decode the string in data with the characters in map. Returns the decoded bytes
def decode(data,map): def decode(data,map):
result = '' result = b''
for i in range (0,len(data)-1,2): for i in range (0,len(data)-1,2):
high = map.find(data[i]) high = map.find(data[i])
low = map.find(data[i+1]) low = map.find(data[i+1])
@ -833,12 +833,12 @@ if iswindows:
# Various character maps used to decrypt kindle info values. # Various character maps used to decrypt kindle info values.
# Probably supposed to act as obfuscation # Probably supposed to act as obfuscation
charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_" charMap2 = b"AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE" charMap5 = b"AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
# New maps in K4PC 1.9.0 # New maps in K4PC 1.9.0
testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" testMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG" testMap6 = b"9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD" testMap8 = b"YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
# interface with Windows OS Routines # interface with Windows OS Routines
class DataBlob(Structure): class DataBlob(Structure):
@ -900,9 +900,9 @@ if iswindows:
# double the buffer size # double the buffer size
buffer = create_unicode_buffer(len(buffer) * 2) buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer) size.value = len(buffer)
# replace any non-ASCII values with 0xfffd # 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": if buffer[i]>"\u007f":
#print "swapping char "+str(i)+" ("+buffer[i]+")" #print "swapping char "+str(i)+" ("+buffer[i]+")"
buffer[i] = "\ufffd" buffer[i] = "\ufffd"
@ -985,7 +985,7 @@ if iswindows:
found = True found = True
print('Found K4PC 1.25+ kinf2018 file: ' + kinfopath.encode('ascii','ignore')) print('Found K4PC 1.25+ kinf2018 file: ' + kinfopath.encode('ascii','ignore'))
kInfoFiles.append(kinfopath) kInfoFiles.append(kinfopath)
# look for (K4PC 1.9.0 and later) .kinf2011 file # look for (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011' kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if os.path.isfile(kinfopath): if os.path.isfile(kinfopath):
@ -1023,28 +1023,28 @@ if iswindows:
# database of keynames and values # database of keynames and values
def getDBfromFile(kInfoFile): def getDBfromFile(kInfoFile):
names = [\ names = [\
'kindle.account.tokens',\ b'kindle.account.tokens',\
'kindle.cookie.item',\ b'kindle.cookie.item',\
'eulaVersionAccepted',\ b'eulaVersionAccepted',\
'login_date',\ b'login_date',\
'kindle.token.item',\ b'kindle.token.item',\
'login',\ b'login',\
'kindle.key.item',\ b'kindle.key.item',\
'kindle.name.info',\ b'kindle.name.info',\
'kindle.device.info',\ b'kindle.device.info',\
'MazamaRandomNumber',\ b'MazamaRandomNumber',\
'max_date',\ b'max_date',\
'SIGVERIF',\ b'SIGVERIF',\
'build_version',\ b'build_version',\
'SerialNumber',\ b'SerialNumber',\
'UsernameHash',\ b'UsernameHash',\
'kindle.directedid.info',\ b'kindle.directedid.info',\
'DSN',\ b'DSN',\
'kindle.accounttype.info',\ b'kindle.accounttype.info',\
'krx.flashcardsplugin.data.encryption_key',\ b'krx.flashcardsplugin.data.encryption_key',\
'krx.notebookexportplugin.data.encryption_key',\ b'krx.notebookexportplugin.data.encryption_key',\
'proxy.http.password',\ b'proxy.http.password',\
'proxy.http.username' b'proxy.http.username'
] ]
DB = {} DB = {}
with open(kInfoFile, 'rb') as infoReader: with open(kInfoFile, 'rb') as infoReader:
@ -1053,7 +1053,7 @@ if iswindows:
# the .kinf file uses "/" to separate it into records # the .kinf file uses "/" to separate it into records
# so remove the trailing "/" to make it easy to use split # so remove the trailing "/" to make it easy to use split
data = data[:-1] data = data[:-1]
items = data.split('/') items = data.split(b'/')
# starts with an encoded and encrypted header blob # starts with an encoded and encrypted header blob
headerblob = items.pop(0) headerblob = items.pop(0)
@ -1095,7 +1095,7 @@ if iswindows:
# read and store in rcnt records of data # read and store in rcnt records of data
# that make up the contents value # that make up the contents value
edlst = [] edlst = []
for i in xrange(rcnt): for i in range(rcnt):
item = items.pop(0) item = items.pop(0)
edlst.append(item) edlst.append(item)
@ -1276,8 +1276,8 @@ elif isosx:
LibCrypto = _load_crypto() LibCrypto = _load_crypto()
# Various character maps used to decrypt books. Probably supposed to act as obfuscation # Various character maps used to decrypt books. Probably supposed to act as obfuscation
charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M' charMap1 = b'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
charMap2 = 'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM' charMap2 = b'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM'
# For kinf approach of K4Mac 1.6.X or later # For kinf approach of K4Mac 1.6.X or later
# On K4PC charMap5 = 'AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE' # On K4PC charMap5 = 'AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE'
@ -1285,7 +1285,7 @@ elif isosx:
charMap5 = charMap2 charMap5 = charMap2
# new in K4M 1.9.X # 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 # uses a sub process to get the Hard Drive Serial Number using ioreg
# returns serial numbers of all internal hard drive drives # 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) p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate() out1, out2 = p.communicate()
#print out1 #print out1
reslst = out1.split('\n') reslst = out1.split(b'\n')
cnt = len(reslst) cnt = len(reslst)
for j in xrange(cnt): for j in range(cnt):
resline = reslst[j] resline = reslst[j]
pp = resline.find('\"Serial Number\" = \"') pp = resline.find(b'\"Serial Number\" = \"')
if pp >= 0: if pp >= 0:
sernum = resline[pp+19:-1] sernum = resline[pp+19:-1]
sernums.append(sernum.strip()) sernums.append(sernum.strip())
@ -1315,12 +1315,12 @@ elif isosx:
cmdline = cmdline.encode(sys.getfilesystemencoding()) cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate() out1, out2 = p.communicate()
reslst = out1.split('\n') reslst = out1.split(b'\n')
cnt = len(reslst) cnt = len(reslst)
for j in xrange(cnt): for j in range(cnt):
resline = reslst[j] resline = reslst[j]
if resline.startswith('/dev'): if resline.startswith(b'/dev'):
(devpart, mpath) = resline.split(' on ')[:2] (devpart, mpath) = resline.split(b' on ')[:2]
dpart = devpart[5:] dpart = devpart[5:]
names.append(dpart) names.append(dpart)
return names 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) p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate() out1, out2 = p.communicate()
#print out1 #print out1
reslst = out1.split('\n') reslst = out1.split(b'\n')
cnt = len(reslst) cnt = len(reslst)
for j in xrange(cnt): for j in range(cnt):
resline = reslst[j] resline = reslst[j]
pp = resline.find('\"UUID\" = \"') pp = resline.find(b'\"UUID\" = \"')
if pp >= 0: if pp >= 0:
uuidnum = resline[pp+10:-1] uuidnum = resline[pp+10:-1]
uuidnum = uuidnum.strip() uuidnum = uuidnum.strip()
@ -1356,16 +1356,16 @@ elif isosx:
cmdline = cmdline.encode(sys.getfilesystemencoding()) cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate() out1, out2 = p.communicate()
reslst = out1.split('\n') reslst = out1.split(b'\n')
cnt = len(reslst) cnt = len(reslst)
for j in xrange(cnt): for j in range(cnt):
resline = reslst[j] resline = reslst[j]
pp = resline.find('Ethernet Address: ') pp = resline.find(b'Ethernet Address: ')
if pp >= 0: if pp >= 0:
#print resline #print resline
macnum = resline[pp+18:] macnum = resline[pp+18:]
macnum = macnum.strip() macnum = macnum.strip()
maclst = macnum.split(':') maclst = macnum.split(b':')
n = len(maclst) n = len(maclst)
if n != 6: if n != 6:
continue continue
@ -1373,7 +1373,7 @@ elif isosx:
# now munge it up the way Kindle app does # now munge it up the way Kindle app does
# by xoring it with 0xa5 and swapping elements 3 and 4 # by xoring it with 0xa5 and swapping elements 3 and 4
for i in range(6): 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 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
mlst[5] = maclst[5] ^ 0xa5 mlst[5] = maclst[5] ^ 0xa5
mlst[4] = maclst[3] ^ 0xa5 mlst[4] = maclst[3] ^ 0xa5
@ -1381,7 +1381,7 @@ elif isosx:
mlst[2] = maclst[2] ^ 0xa5 mlst[2] = maclst[2] ^ 0xa5
mlst[1] = maclst[1] ^ 0xa5 mlst[1] = maclst[1] ^ 0xa5
mlst[0] = maclst[0] ^ 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 #print 'munged mac', macnum
macnums.append(macnum) macnums.append(macnum)
return macnums return macnums
@ -1391,7 +1391,7 @@ elif isosx:
def GetUserName(): def GetUserName():
username = os.getenv('USER') username = os.getenv('USER')
#print "Username:",username #print "Username:",username
return username return username.encode('utf-8')
def GetIDStrings(): def GetIDStrings():
# Return all possible ID Strings # Return all possible ID Strings
@ -1400,7 +1400,7 @@ elif isosx:
strings.extend(GetVolumesSerialNumbers()) strings.extend(GetVolumesSerialNumbers())
strings.extend(GetDiskPartitionNames()) strings.extend(GetDiskPartitionNames())
strings.extend(GetDiskPartitionUUIDs()) strings.extend(GetDiskPartitionUUIDs())
strings.append('9999999999') strings.append(b'9999999999')
#print "ID Strings:\n",strings #print "ID Strings:\n",strings
return strings return strings
@ -1408,8 +1408,8 @@ elif isosx:
# unprotect the new header blob in .kinf2011 # unprotect the new header blob in .kinf2011
# used in Kindle for Mac Version >= 1.9.0 # used in Kindle for Mac Version >= 1.9.0
def UnprotectHeaderData(encryptedData): def UnprotectHeaderData(encryptedData):
passwdData = 'header_key_data' passwdData = b'header_key_data'
salt = 'HEADER.2011' salt = b'HEADER.2011'
iter = 0x80 iter = 0x80
keylen = 0x100 keylen = 0x100
crp = LibCrypto() crp = LibCrypto()
@ -1424,7 +1424,7 @@ elif isosx:
# implements an Pseudo Mac Version of Windows built-in Crypto routine # implements an Pseudo Mac Version of Windows built-in Crypto routine
class CryptUnprotectData(object): class CryptUnprotectData(object):
def __init__(self, entropy, IDString): def __init__(self, entropy, IDString):
sp = GetUserName() + '+@#$%+' + IDString sp = GetUserName() + b'+@#$%+' + IDString
passwdData = encode(SHA256(sp),charMap2) passwdData = encode(SHA256(sp),charMap2)
salt = entropy salt = entropy
self.crp = LibCrypto() self.crp = LibCrypto()
@ -1503,59 +1503,79 @@ elif isosx:
# database of keynames and values # database of keynames and values
def getDBfromFile(kInfoFile): def getDBfromFile(kInfoFile):
names = [\ names = [\
'kindle.account.tokens',\ b'kindle.account.tokens',\
'kindle.cookie.item',\ b'kindle.cookie.item',\
'eulaVersionAccepted',\ b'eulaVersionAccepted',\
'login_date',\ b'login_date',\
'kindle.token.item',\ b'kindle.token.item',\
'login',\ b'login',\
'kindle.key.item',\ b'kindle.key.item',\
'kindle.name.info',\ b'kindle.name.info',\
'kindle.device.info',\ b'kindle.device.info',\
'MazamaRandomNumber',\ b'MazamaRandomNumber',\
'max_date',\ b'max_date',\
'SIGVERIF',\ b'SIGVERIF',\
'build_version',\ b'build_version',\
'SerialNumber',\ b'SerialNumber',\
'UsernameHash',\ b'UsernameHash',\
'kindle.directedid.info',\ b'kindle.directedid.info',\
'DSN' 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: with open(kInfoFile, 'rb') as infoReader:
filedata = infoReader.read() filedata = infoReader.read()
data = filedata[:-1] data = filedata[:-1]
items = data.split('/') items = data.split(b'/')
IDStrings = GetIDStrings() IDStrings = GetIDStrings()
print ("trying username ", GetUserName())
for IDString in IDStrings: for IDString in IDStrings:
#print "trying IDString:",IDString print ("trying IDString:",IDString)
try: try:
DB = {} DB = {}
items = data.split('/') items = data.split(b'/')
# the headerblob is the encrypted information needed to build the entropy string # the headerblob is the encrypted information needed to build the entropy string
headerblob = items.pop(0) headerblob = items.pop(0)
#print ("headerblob: ",headerblob)
encryptedValue = decode(headerblob, charMap1) encryptedValue = decode(headerblob, charMap1)
#print ("encryptedvalue: ",encryptedValue)
cleartext = UnprotectHeaderData(encryptedValue) cleartext = UnprotectHeaderData(encryptedValue)
print ("cleartext: ",cleartext)
# now extract the pieces in the same way # 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): for m in re.finditer(pattern, cleartext):
version = int(m.group(1)) version = int(m.group(1))
build = m.group(2) build = m.group(2)
guid = m.group(4) 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 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) cud = CryptUnprotectData(entropy,IDString)
print ("entropy",entropy)
print ("cud",cud)
elif version == 6: # .kinf2018: identical to K4PC elif version == 6: # .kinf2018: identical to K4PC
salt = str(0x6d8 * int(build)) + guid salt = bytes(0x6d8 * int(build)) + guid
sp = GetUserName() + '+@#$%+' + IDString sp = GetUserName() + b'+@#$%+' + IDString
passwd = encode(SHA256(sp), charMap5) passwd = encode(SHA256(sp), charMap5)
key = LibCrypto().keyivgen(passwd, salt, 10000, 0x400)[:32] 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: while len(items) > 0:
# get the first item record # get the first item record
@ -1564,7 +1584,7 @@ elif isosx:
# the first 32 chars of the first record of a group # the first 32 chars of the first record of a group
# is the MD5 hash of the key name encoded by charMap5 # is the MD5 hash of the key name encoded by charMap5
keyhash = item[0:32] keyhash = item[0:32]
keyname = 'unknown' keyname = b'unknown'
# unlike K4PC the keyhash is not used in generating entropy # unlike K4PC the keyhash is not used in generating entropy
# entropy = SHA1(keyhash) + added_entropy # entropy = SHA1(keyhash) + added_entropy
@ -1580,16 +1600,16 @@ elif isosx:
# read and store in rcnt records of data # read and store in rcnt records of data
# that make up the contents value # that make up the contents value
edlst = [] edlst = []
for i in xrange(rcnt): for i in range(rcnt):
item = items.pop(0) item = items.pop(0)
edlst.append(item) edlst.append(item)
keyname = 'unknown' keyname = b'unknown'
for name in names: for name in names:
if encodeHash(name,testMap8) == keyhash: if encodeHash(name,testMap8) == keyhash:
keyname = name keyname = name
break break
if keyname == 'unknown': if keyname == b'unknown':
keyname = keyhash keyname = keyhash
# the testMap8 encoded contents data has had a length # 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) # (in other words split 'about' 2/3rds of the way through)
# move first offsets chars to end to align for decode by testMap8 # move first offsets chars to end to align for decode by testMap8
encdata = ''.join(edlst) encdata = b''.join(edlst)
contlen = len(encdata) contlen = len(encdata)
# now properly split and recombine # now properly split and recombine
@ -1643,7 +1663,9 @@ elif isosx:
if len(DB)>6: if len(DB)>6:
break break
except:
except Exception:
print (traceback.format_exc())
pass pass
if len(DB)>6: if len(DB)>6:
# store values used in decryption # store values used in decryption
@ -1709,7 +1731,7 @@ def cli_main():
sys.stderr=SafeUnbuffered(sys.stderr) sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv() argv=unicode_argv()
progname = os.path.basename(argv[0]) 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: try:
opts, args = getopt.getopt(argv[1:], "hk:") opts, args = getopt.getopt(argv[1:], "hk:")
@ -1800,6 +1822,7 @@ def gui_main():
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
print ("here")
if len(sys.argv) > 1: if len(sys.argv) > 1:
sys.exit(cli_main()) sys.exit(cli_main())
sys.exit(gui_main()) sys.exit(gui_main())

@ -137,10 +137,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["mobidedrm.py"] return ["mobidedrm.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = 'utf-8'
return sys.argv
class DrmException(Exception): class DrmException(Exception):
@ -246,7 +244,7 @@ class MobiBook:
pass pass
def __init__(self, infile): 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: try:
from alfcrypto import Pukall_Cipher from alfcrypto import Pukall_Cipher
@ -522,7 +520,7 @@ def cli_main():
argv=unicode_argv() argv=unicode_argv()
progname = os.path.basename(argv[0]) progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4: 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("Removes protection from Kindle/Mobipocket, Kindle/KF8 and Kindle/Print Replica ebooks")
print("Usage:") print("Usage:")
print(" {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname)) print(" {0} <infile> <outfile> [<Comma separated list of PIDs to try>]".format(progname))
@ -531,7 +529,8 @@ def cli_main():
infile = argv[1] infile = argv[1]
outfile = argv[2] outfile = argv[2]
if len(argv) == 4: if len(argv) == 4:
pidlist = argv[3].split(',') # convert from unicode to bytearray before splitting.
pidlist = argv[3].encode('utf-8').split(b',')
else: else:
pidlist = [] pidlist = []
try: try:

@ -18,7 +18,10 @@ import zlib, zipfile, tempfile, shutil
import traceback import traceback
from struct import pack from struct import pack
from struct import unpack 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: class SafeUnbuffered:
def __init__(self, stream): def __init__(self, stream):
@ -70,10 +73,8 @@ def unicode_argv():
# this should never happen # this should never happen
return ["mobidedrm.py"] return ["mobidedrm.py"]
else: else:
argvencoding = sys.stdin.encoding argvencoding = sys.stdin.encoding or "utf-8"
if argvencoding == None: return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
argvencoding = 'utf-8'
return argv
#global switch #global switch
debug = False debug = False

@ -22,7 +22,10 @@ __version__ = "1.1"
import sys import sys
import zlib import zlib
import calibre_plugins.dedrm.zipfilerugged as zipfilerugged try:
import zipfilerugged
except:
import calibre_plugins.dedrm.zipfilerugged as zipfilerugged
import os import os
import os.path import os.path
import getopt import getopt

Loading…
Cancel
Save