# 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
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:")
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
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
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
# 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
#
def main(argv):
+ sys.stdout=SafeUnbuffered(sys.stdout)
+ sys.stderr=SafeUnbuffered(sys.stderr)
dictFile = ""
pageFile = ""
debug = False
# 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:
# 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:
# 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
def main(argv):
+ sys.stdout=SafeUnbuffered(sys.stdout)
+ sys.stderr=SafeUnbuffered(sys.stderr)
bookDir = ''
if len(argv) == 0:
argv = sys.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):
# 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
# 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):
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)
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)
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
# 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):
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():
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):
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?
except ValueError:
pass
return (self.parse_main, j)
-
+
def parse_decimal(self, s, i):
m = END_NUMBER.search(s, i)
if not m:
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):
# 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
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")
except ImportError:
from io import StringIO
-try:
- from calibre_plugins.dedrm import ion
-except ImportError:
- import ion
-
__license__ = 'GPL v3'
__version__ = '2.0'
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:
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=""
# 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
from struct import pack, unpack, unpack_from
import json
import getopt
+import traceback
try:
RegError
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)
# 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
# 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
# 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])
# 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):
# 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"
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):
# 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:
# 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)
# 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)
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'
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
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())
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
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()
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
# 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
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
def GetUserName():
username = os.getenv('USER')
#print "Username:",username
- return username
+ return username.encode('utf-8')
def GetIDStrings():
# Return all possible ID Strings
strings.extend(GetVolumesSerialNumbers())
strings.extend(GetDiskPartitionNames())
strings.extend(GetDiskPartitionUUIDs())
- strings.append('9999999999')
+ strings.append(b'9999999999')
#print "ID Strings:\n",strings
return strings
# unprotect the new header blob in .kinf2011
# used in Kindle for Mac Version >= 1.9.0
def UnprotectHeaderData(encryptedData):
- passwdData = 'header_key_data'
- salt = 'HEADER.2011'
+ passwdData = b'header_key_data'
+ salt = b'HEADER.2011'
iter = 0x80
keylen = 0x100
crp = LibCrypto()
# 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()
# 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
# 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
# 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
# (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
if len(DB)>6:
break
- except:
+
+ except Exception:
+ print (traceback.format_exc())
pass
if len(DB)>6:
# store values used in decryption
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:")
return 0
if __name__ == '__main__':
+ print ("here")
if len(sys.argv) > 1:
sys.exit(cli_main())
sys.exit(gui_main())
# 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):
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
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))
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:
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):
# 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
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