Merge pull request #1318 from task-hazy/kindle_fetch
Get working kindlekey.py on Python 3.8.6
This commit is contained in:
commit
c4c20eb07e
|
@ -36,6 +36,7 @@ Retrieve Kindle for PC/Mac user key.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, os, re
|
import sys, os, re
|
||||||
|
import codecs
|
||||||
from struct import pack, unpack, unpack_from
|
from struct import pack, unpack, unpack_from
|
||||||
import json
|
import json
|
||||||
import getopt
|
import getopt
|
||||||
|
@ -156,25 +157,27 @@ def primes(n):
|
||||||
# Encode the bytes in data with the characters in map
|
# Encode the bytes in data with the characters in map
|
||||||
# data and map should be byte arrays
|
# data and map should be byte arrays
|
||||||
def encode(data, map):
|
def encode(data, map):
|
||||||
result = b''
|
result = ''
|
||||||
for char in data:
|
for char in data:
|
||||||
value = char
|
value = char
|
||||||
Q = (value ^ 0x80) // len(map)
|
Q = (value ^ 0x80) // len(map)
|
||||||
R = value % len(map)
|
R = value % len(map)
|
||||||
result += bytes([map[Q]])
|
result += map[Q]
|
||||||
result += bytes([map[R]])
|
result += map[R]
|
||||||
return result
|
return result.encode('utf-8')
|
||||||
|
|
||||||
# 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
|
||||||
def encodeHash(data,map):
|
def encodeHash(data,map):
|
||||||
return encode(MD5(data),map)
|
h = MD5(data)
|
||||||
|
return encode(h,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):
|
||||||
|
str_data = data.decode()
|
||||||
result = b''
|
result = b''
|
||||||
for i in range (0,len(data)-1,2):
|
for i in range (0,len(str_data)-1,2):
|
||||||
high = map.find(data[i])
|
high = map.find(str_data[i])
|
||||||
low = map.find(data[i+1])
|
low = map.find(str_data[i+1])
|
||||||
if (high == -1) or (low == -1) :
|
if (high == -1) or (low == -1) :
|
||||||
break
|
break
|
||||||
value = (((high * len(map)) ^ 0x80) & 0xFF) + low
|
value = (((high * len(map)) ^ 0x80) & 0xFF) + low
|
||||||
|
@ -187,7 +190,8 @@ if iswindows:
|
||||||
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
|
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
|
||||||
string_at, Structure, c_void_p, cast
|
string_at, Structure, c_void_p, cast
|
||||||
|
|
||||||
import _winreg as winreg
|
# import _winreg as winreg
|
||||||
|
import winreg
|
||||||
MAX_PATH = 255
|
MAX_PATH = 255
|
||||||
kernel32 = windll.kernel32
|
kernel32 = windll.kernel32
|
||||||
advapi32 = windll.advapi32
|
advapi32 = windll.advapi32
|
||||||
|
@ -243,8 +247,8 @@ if iswindows:
|
||||||
""" XOR two strings """
|
""" XOR two strings """
|
||||||
x = []
|
x = []
|
||||||
for i in range(min(len(a),len(b))):
|
for i in range(min(len(a),len(b))):
|
||||||
x.append( chr(ord(a[i])^ord(b[i])))
|
x.append( a[i] ^ b[i])
|
||||||
return ''.join(x)
|
return bytes(x)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Base 'BlockCipher' and Pad classes for cipher instances.
|
Base 'BlockCipher' and Pad classes for cipher instances.
|
||||||
|
@ -263,10 +267,10 @@ if iswindows:
|
||||||
self.resetDecrypt()
|
self.resetDecrypt()
|
||||||
def resetEncrypt(self):
|
def resetEncrypt(self):
|
||||||
self.encryptBlockCount = 0
|
self.encryptBlockCount = 0
|
||||||
self.bytesToEncrypt = ''
|
self.bytesToEncrypt = b''
|
||||||
def resetDecrypt(self):
|
def resetDecrypt(self):
|
||||||
self.decryptBlockCount = 0
|
self.decryptBlockCount = 0
|
||||||
self.bytesToDecrypt = ''
|
self.bytesToDecrypt = b''
|
||||||
|
|
||||||
def encrypt(self, plainText, more = None):
|
def encrypt(self, plainText, more = None):
|
||||||
""" Encrypt a string and return a binary string """
|
""" Encrypt a string and return a binary string """
|
||||||
|
@ -306,7 +310,7 @@ if iswindows:
|
||||||
numBlocks -= 1
|
numBlocks -= 1
|
||||||
numExtraBytes = self.blockSize
|
numExtraBytes = self.blockSize
|
||||||
|
|
||||||
plainText = ''
|
plainText = b''
|
||||||
for i in range(numBlocks):
|
for i in range(numBlocks):
|
||||||
bStart = i*self.blockSize
|
bStart = i*self.blockSize
|
||||||
ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize])
|
ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize])
|
||||||
|
@ -371,11 +375,11 @@ if iswindows:
|
||||||
self.blockSize = blockSize # blockSize is in bytes
|
self.blockSize = blockSize # blockSize is in bytes
|
||||||
self.padding = padding # change default to noPadding() to get normal ECB behavior
|
self.padding = padding # change default to noPadding() to get normal ECB behavior
|
||||||
|
|
||||||
assert( keySize%4==0 and NrTable[4].has_key(keySize/4)),'key size must be 16,20,24,29 or 32 bytes'
|
assert( keySize%4==0 and (keySize//4) in NrTable[4]),'key size must be 16,20,24,29 or 32 bytes'
|
||||||
assert( blockSize%4==0 and NrTable.has_key(blockSize/4)), 'block size must be 16,20,24,29 or 32 bytes'
|
assert( blockSize%4==0 and (blockSize//4) in NrTable), 'block size must be 16,20,24,29 or 32 bytes'
|
||||||
|
|
||||||
self.Nb = self.blockSize/4 # Nb is number of columns of 32 bit words
|
self.Nb = self.blockSize//4 # Nb is number of columns of 32 bit words
|
||||||
self.Nk = keySize/4 # Nk is the key length in 32-bit words
|
self.Nk = keySize//4 # Nk is the key length in 32-bit words
|
||||||
self.Nr = NrTable[self.Nb][self.Nk] # The number of rounds (Nr) is a function of
|
self.Nr = NrTable[self.Nb][self.Nk] # The number of rounds (Nr) is a function of
|
||||||
# the block (Nb) and key (Nk) sizes.
|
# the block (Nb) and key (Nk) sizes.
|
||||||
if key != None:
|
if key != None:
|
||||||
|
@ -419,15 +423,15 @@ if iswindows:
|
||||||
def _toBlock(self, bs):
|
def _toBlock(self, bs):
|
||||||
""" Convert binary string to array of bytes, state[col][row]"""
|
""" Convert binary string to array of bytes, state[col][row]"""
|
||||||
assert ( len(bs) == 4*self.Nb ), 'Rijndarl blocks must be of size blockSize'
|
assert ( len(bs) == 4*self.Nb ), 'Rijndarl blocks must be of size blockSize'
|
||||||
return [[ord(bs[4*i]),ord(bs[4*i+1]),ord(bs[4*i+2]),ord(bs[4*i+3])] for i in range(self.Nb)]
|
return [[bs[4*i],bs[4*i+1],bs[4*i+2],bs[4*i+3]] for i in range(self.Nb)]
|
||||||
|
|
||||||
def _toBString(self, block):
|
def _toBString(self, block):
|
||||||
""" Convert block (array of bytes) to binary string """
|
""" Convert block (array of bytes) to binary string """
|
||||||
l = []
|
l = []
|
||||||
for col in block:
|
for col in block:
|
||||||
for rowElement in col:
|
for rowElement in col:
|
||||||
l.append(chr(rowElement))
|
l.append(rowElement)
|
||||||
return ''.join(l)
|
return bytes(l)
|
||||||
#-------------------------------------
|
#-------------------------------------
|
||||||
""" Number of rounds Nr = NrTable[Nb][Nk]
|
""" Number of rounds Nr = NrTable[Nb][Nk]
|
||||||
|
|
||||||
|
@ -442,14 +446,14 @@ if iswindows:
|
||||||
def keyExpansion(algInstance, keyString):
|
def keyExpansion(algInstance, keyString):
|
||||||
""" Expand a string of size keySize into a larger array """
|
""" Expand a string of size keySize into a larger array """
|
||||||
Nk, Nb, Nr = algInstance.Nk, algInstance.Nb, algInstance.Nr # for readability
|
Nk, Nb, Nr = algInstance.Nk, algInstance.Nb, algInstance.Nr # for readability
|
||||||
key = [ord(byte) for byte in keyString] # convert string to list
|
key = [byte for byte in keyString] # convert string to list
|
||||||
w = [[key[4*i],key[4*i+1],key[4*i+2],key[4*i+3]] for i in range(Nk)]
|
w = [[key[4*i],key[4*i+1],key[4*i+2],key[4*i+3]] for i in range(Nk)]
|
||||||
for i in range(Nk,Nb*(Nr+1)):
|
for i in range(Nk,Nb*(Nr+1)):
|
||||||
temp = w[i-1] # a four byte column
|
temp = w[i-1] # a four byte column
|
||||||
if (i%Nk) == 0 :
|
if (i%Nk) == 0 :
|
||||||
temp = temp[1:]+[temp[0]] # RotWord(temp)
|
temp = temp[1:]+[temp[0]] # RotWord(temp)
|
||||||
temp = [ Sbox[byte] for byte in temp ]
|
temp = [ Sbox[byte] for byte in temp ]
|
||||||
temp[0] ^= Rcon[i/Nk]
|
temp[0] ^= Rcon[i//Nk]
|
||||||
elif Nk > 6 and i%Nk == 4 :
|
elif Nk > 6 and i%Nk == 4 :
|
||||||
temp = [ Sbox[byte] for byte in temp ] # SubWord(temp)
|
temp = [ Sbox[byte] for byte in temp ] # SubWord(temp)
|
||||||
w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] )
|
w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] )
|
||||||
|
@ -741,7 +745,7 @@ if iswindows:
|
||||||
if self.decryptBlockCount == 0: # first call, process IV
|
if self.decryptBlockCount == 0: # first call, process IV
|
||||||
if self.iv == None: # auto decrypt IV?
|
if self.iv == None: # auto decrypt IV?
|
||||||
self.prior_CT_block = encryptedBlock
|
self.prior_CT_block = encryptedBlock
|
||||||
return ''
|
return b''
|
||||||
else:
|
else:
|
||||||
assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption"
|
assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption"
|
||||||
self.prior_CT_block = self.iv
|
self.prior_CT_block = self.iv
|
||||||
|
@ -794,6 +798,11 @@ if iswindows:
|
||||||
raise Exception("xorstr(): lengths differ")
|
raise Exception("xorstr(): lengths differ")
|
||||||
return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b)))
|
return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b)))
|
||||||
|
|
||||||
|
def xorbytes( a, b ):
|
||||||
|
if len(a) != len(b):
|
||||||
|
raise Exception("xorstr(): lengths differ")
|
||||||
|
return bytes([x ^ y for x, y in zip(a, b)])
|
||||||
|
|
||||||
def prf( h, data ):
|
def prf( h, data ):
|
||||||
hm = h.copy()
|
hm = h.copy()
|
||||||
hm.update( data )
|
hm.update( data )
|
||||||
|
@ -804,24 +813,24 @@ if iswindows:
|
||||||
T = U
|
T = U
|
||||||
for i in range(2, itercount+1):
|
for i in range(2, itercount+1):
|
||||||
U = prf( h, U )
|
U = prf( h, U )
|
||||||
T = xorstr( T, U )
|
T = xorbytes( T, U )
|
||||||
return T
|
return T
|
||||||
|
|
||||||
sha = hashlib.sha1
|
sha = hashlib.sha1
|
||||||
digest_size = sha().digest_size
|
digest_size = sha().digest_size
|
||||||
# l - number of output blocks to produce
|
# l - number of output blocks to produce
|
||||||
l = keylen / digest_size
|
l = keylen // digest_size
|
||||||
if keylen % digest_size != 0:
|
if keylen % digest_size != 0:
|
||||||
l += 1
|
l += 1
|
||||||
h = hmac.new( passwd, None, sha )
|
h = hmac.new( passwd, None, sha )
|
||||||
T = ""
|
T = b""
|
||||||
for i in range(1, l+1):
|
for i in range(1, l+1):
|
||||||
T += pbkdf2_F( h, salt, iter, i )
|
T += pbkdf2_F( h, salt, iter, i )
|
||||||
return T[0: keylen]
|
return T[0: keylen]
|
||||||
|
|
||||||
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
|
||||||
key_iv = KeyIVGen().pbkdf2(passwdData, salt, iter, keylen)
|
key_iv = KeyIVGen().pbkdf2(passwdData, salt, iter, keylen)
|
||||||
|
@ -834,12 +843,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 = b"AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
|
charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
|
||||||
charMap5 = b"AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
|
charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
|
||||||
# New maps in K4PC 1.9.0
|
# New maps in K4PC 1.9.0
|
||||||
testMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
|
testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
|
||||||
testMap6 = b"9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
|
testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
|
||||||
testMap8 = b"YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
|
testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
|
||||||
|
|
||||||
# interface with Windows OS Routines
|
# interface with Windows OS Routines
|
||||||
class DataBlob(Structure):
|
class DataBlob(Structure):
|
||||||
|
@ -927,7 +936,7 @@ if iswindows:
|
||||||
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
|
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
|
||||||
None, None, flags, byref(outdata)):
|
None, None, flags, byref(outdata)):
|
||||||
# raise DrmException("Failed to Unprotect Data")
|
# raise DrmException("Failed to Unprotect Data")
|
||||||
return 'failed'
|
return b'failed'
|
||||||
return string_at(outdata.pbData, outdata.cbData)
|
return string_at(outdata.pbData, outdata.cbData)
|
||||||
return CryptUnprotectData
|
return CryptUnprotectData
|
||||||
CryptUnprotectData = CryptUnprotectData()
|
CryptUnprotectData = CryptUnprotectData()
|
||||||
|
@ -979,20 +988,21 @@ if iswindows:
|
||||||
print ('Could not find the folder in which to look for kinfoFiles.')
|
print ('Could not find the folder in which to look for kinfoFiles.')
|
||||||
else:
|
else:
|
||||||
# Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
|
# Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
|
||||||
print("searching for kinfoFiles in " + path.encode('ascii', 'ignore'))
|
# print("searching for kinfoFiles in " + path.encode('ascii', 'ignore'))
|
||||||
|
print("searching for kinfoFiles in " + path)
|
||||||
|
|
||||||
# look for (K4PC 1.25.1 and later) .kinf2018 file
|
# look for (K4PC 1.25.1 and later) .kinf2018 file
|
||||||
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2018'
|
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2018'
|
||||||
if os.path.isfile(kinfopath):
|
if os.path.isfile(kinfopath):
|
||||||
found = True
|
found = True
|
||||||
print('Found K4PC 1.25+ kinf2018 file: ' + kinfopath.encode('ascii','ignore'))
|
print('Found K4PC 1.25+ kinf2018 file: ' + kinfopath)
|
||||||
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):
|
||||||
found = True
|
found = True
|
||||||
print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath.encode('ascii','ignore'))
|
print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath)
|
||||||
kInfoFiles.append(kinfopath)
|
kInfoFiles.append(kinfopath)
|
||||||
|
|
||||||
# look for (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
|
# look for (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
|
||||||
|
@ -1048,6 +1058,8 @@ if iswindows:
|
||||||
b'proxy.http.password',\
|
b'proxy.http.password',\
|
||||||
b'proxy.http.username'
|
b'proxy.http.username'
|
||||||
]
|
]
|
||||||
|
namehashmap = {encodeHash(n,testMap8):n for n in names}
|
||||||
|
# print(namehashmap)
|
||||||
DB = {}
|
DB = {}
|
||||||
with open(kInfoFile, 'rb') as infoReader:
|
with open(kInfoFile, 'rb') as infoReader:
|
||||||
data = infoReader.read()
|
data = infoReader.read()
|
||||||
|
@ -1063,7 +1075,7 @@ if iswindows:
|
||||||
cleartext = UnprotectHeaderData(encryptedValue)
|
cleartext = UnprotectHeaderData(encryptedValue)
|
||||||
#print "header cleartext:",cleartext
|
#print "header cleartext:",cleartext
|
||||||
# now extract the pieces that form the added entropy
|
# now extract the pieces that form the added entropy
|
||||||
pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
|
pattern = re.compile(br'''\[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)
|
||||||
|
@ -1102,13 +1114,10 @@ if iswindows:
|
||||||
edlst.append(item)
|
edlst.append(item)
|
||||||
|
|
||||||
# key names now use the new testMap8 encoding
|
# key names now use the new testMap8 encoding
|
||||||
keyname = "unknown"
|
if keyhash in namehashmap:
|
||||||
for name in names:
|
keyname=namehashmap[keyhash]
|
||||||
if encodeHash(name,testMap8) == keyhash:
|
#print "keyname found from hash:",keyname
|
||||||
keyname = name
|
else:
|
||||||
#print "keyname found from hash:",keyname
|
|
||||||
break
|
|
||||||
if keyname == "unknown":
|
|
||||||
keyname = keyhash
|
keyname = keyhash
|
||||||
#print "keyname not found, hash is:",keyname
|
#print "keyname not found, hash is:",keyname
|
||||||
|
|
||||||
|
@ -1125,7 +1134,7 @@ if iswindows:
|
||||||
# move first offsets chars to end to align for decode by testMap8
|
# move first offsets chars to end to align for decode by testMap8
|
||||||
# by moving noffset chars from the start of the
|
# by moving noffset chars from the start of the
|
||||||
# string to the end of the string
|
# string to the end of the string
|
||||||
encdata = "".join(edlst)
|
encdata = b"".join(edlst)
|
||||||
#print "encrypted data:",encdata
|
#print "encrypted data:",encdata
|
||||||
contlen = len(encdata)
|
contlen = len(encdata)
|
||||||
noffset = contlen - primes(int(contlen/3))[-1]
|
noffset = contlen - primes(int(contlen/3))[-1]
|
||||||
|
@ -1164,9 +1173,9 @@ if iswindows:
|
||||||
|
|
||||||
if len(DB)>6:
|
if len(DB)>6:
|
||||||
# store values used in decryption
|
# store values used in decryption
|
||||||
DB[b'IDString'] = GetIDString()
|
DB[b'IDString'] = GetIDString().encode('utf-8')
|
||||||
DB[b'UserName'] = GetUserName()
|
DB[b'UserName'] = GetUserName()
|
||||||
print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().encode('hex')))
|
print("Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().decode('utf-8')))
|
||||||
else:
|
else:
|
||||||
print("Couldn't decrypt file.")
|
print("Couldn't decrypt file.")
|
||||||
DB = {}
|
DB = {}
|
||||||
|
@ -1550,7 +1559,7 @@ elif isosx:
|
||||||
#print ("cleartext: ",cleartext)
|
#print ("cleartext: ",cleartext)
|
||||||
|
|
||||||
# now extract the pieces in the same way
|
# now extract the pieces in the same way
|
||||||
pattern = re.compile(rb'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
|
pattern = re.compile(br'''\[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)
|
||||||
|
@ -1691,9 +1700,11 @@ def kindlekeys(files = []):
|
||||||
key = getDBfromFile(file)
|
key = getDBfromFile(file)
|
||||||
if key:
|
if key:
|
||||||
# convert all values to hex, just in case.
|
# convert all values to hex, just in case.
|
||||||
for keyname in key:
|
n_key = {}
|
||||||
key[keyname]=key[keyname].hex().encode('utf-8')
|
for k,v in key.items():
|
||||||
keys.append(key)
|
n_key[k.decode()]=codecs.encode(v, 'hex_codec').decode()
|
||||||
|
# key = {k.decode():v.decode() for k,v in key.items()}
|
||||||
|
keys.append(n_key)
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
# interface for Python DeDRM
|
# interface for Python DeDRM
|
||||||
|
@ -1714,9 +1725,8 @@ def getkey(outpath, files=[]):
|
||||||
outfile = os.path.join(outpath,"kindlekey{0:d}.k4i".format(keycount))
|
outfile = os.path.join(outpath,"kindlekey{0:d}.k4i".format(keycount))
|
||||||
if not os.path.exists(outfile):
|
if not os.path.exists(outfile):
|
||||||
break
|
break
|
||||||
unikey = {k.decode("utf-8"):v.decode("utf-8") for k,v in key.items()}
|
|
||||||
with open(outfile, 'w') as keyfileout:
|
with open(outfile, 'w') as keyfileout:
|
||||||
keyfileout.write(json.dumps(unikey))
|
keyfileout.write(json.dumps(key))
|
||||||
print("Saved a key to {0}".format(outfile))
|
print("Saved a key to {0}".format(outfile))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
Loading…
Reference in New Issue