tools v6.0.8

This commit is contained in:
Apprentice Alf
2013-10-02 19:59:40 +01:00
parent 74a4c894cb
commit b1feca321d
54 changed files with 11353 additions and 8746 deletions

View File

@@ -31,14 +31,17 @@ __docformat__ = 'restructuredtext en'
# 6.0.3 - Fixes for Kindle for Mac and Windows non-ascii user names
# 6.0.4 - Fixes for stand-alone scripts and applications
# and pdb files in plugin and initial conversion of prefs.
# 6.0.5 - Fix a key issue
# 6.0.6 - Fix up an incorrect function call
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 7)
PLUGIN_VERSION_TUPLE = (6, 0, 8)
PLUGIN_VERSION = u".".join([unicode(str(x)) for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
@@ -313,7 +316,7 @@ class DeDRM(FileTypePlugin):
from wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,u"adobekey.py")
defaultkeys = self.WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
defaultkeys = WineGetKeys(scriptpath, u".der",dedrmprefs['adobewineprefix'])
except:
pass
@@ -391,7 +394,7 @@ class DeDRM(FileTypePlugin):
from wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,u"kindlekey.py")
defaultkeys = self.WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
defaultkeys = WineGetKeys(scriptpath, u".k4i",dedrmprefs['kindlewineprefix'])
except:
pass

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env python
#fileencoding: utf-8
import os
import sys
import zlib
import tarfile
from hashlib import md5
from cStringIO import StringIO
from binascii import a2b_hex, b2a_hex
STORAGE = 'AmazonSecureStorage.xml'
class AndroidObfuscation(object):
'''AndroidObfuscation
For the key, it's written in java, and run in android dalvikvm
'''
key = a2b_hex('0176e04c9408b1702d90be333fd53523')
def encrypt(self, plaintext):
cipher = self._get_cipher()
padding = len(self.key) - len(plaintext) % len(self.key)
plaintext += chr(padding) * padding
return b2a_hex(cipher.encrypt(plaintext))
def decrypt(self, ciphertext):
cipher = self._get_cipher()
plaintext = cipher.decrypt(a2b_hex(ciphertext))
return plaintext[:-ord(plaintext[-1])]
def _get_cipher(self):
try:
from Crypto.Cipher import AES
return AES.new(self.key)
except ImportError:
from aescbc import AES, noPadding
return AES(self.key, padding=noPadding())
class AndroidObfuscationV2(AndroidObfuscation):
'''AndroidObfuscationV2
'''
count = 503
password = 'Thomsun was here!'
def __init__(self, salt):
key = self.password + salt
for _ in range(self.count):
key = md5(key).digest()
self.key = key[:8]
self.iv = key[8:16]
def _get_cipher(self):
try :
from Crypto.Cipher import DES
return DES.new(self.key, DES.MODE_CBC, self.iv)
except ImportError:
from python_des import Des, CBC
return Des(self.key, CBC, self.iv)
def parse_preference(path):
''' parse android's shared preference xml '''
storage = {}
read = open(path)
for line in read:
line = line.strip()
# <string name="key">value</string>
if line.startswith('<string name="'):
index = line.find('"', 14)
key = line[14:index]
value = line[index+2:-9]
storage[key] = value
read.close()
return storage
def get_serials(path=None):
''' get serials from android's shared preference xml '''
if path is None:
if not os.path.isfile(STORAGE):
if os.path.isfile("backup.ab"):
get_storage()
else:
return []
path = STORAGE
if not os.path.isfile(path):
return []
storage = parse_preference(path)
salt = storage.get('AmazonSaltKey')
if salt and len(salt) == 16:
sys.stdout.write('Using AndroidObfuscationV2\n')
obfuscation = AndroidObfuscationV2(a2b_hex(salt))
else:
sys.stdout.write('Using AndroidObfuscation\n')
obfuscation = AndroidObfuscation()
def get_value(key):
encrypted_key = obfuscation.encrypt(key)
encrypted_value = storage.get(encrypted_key)
if encrypted_value:
return obfuscation.decrypt(encrypted_value)
return ''
# also see getK4Pids in kgenpids.py
try:
dsnid = get_value('DsnId')
except:
sys.stderr.write('cannot get DsnId\n')
return []
try:
tokens = set(get_value('kindle.account.tokens').split(','))
except:
return [dsnid]
serials = []
for token in tokens:
if token:
serials.append('%s%s' % (dsnid, token))
serials.append(dsnid)
for token in tokens:
if token:
serials.append(token)
return serials
def get_storage(path='backup.ab'):
'''get AmazonSecureStorage.xml from android backup.ab
backup.ab can be get using adb command:
shell> adb backup com.amazon.kindle
'''
output = None
read = open(path, 'rb')
head = read.read(24)
if head == 'ANDROID BACKUP\n1\n1\nnone\n':
output = StringIO(zlib.decompress(read.read()))
read.close()
if not output:
return False
tar = tarfile.open(fileobj=output)
for member in tar.getmembers():
if member.name.strip().endswith(STORAGE):
write = open(STORAGE, 'w')
write.write(tar.extractfile(member).read())
write.close()
break
return True
__all__ = [ 'get_storage', 'get_serials', 'parse_preference',
'AndroidObfuscation', 'AndroidObfuscationV2', 'STORAGE']
if __name__ == '__main__':
print get_serials()

View File

@@ -0,0 +1,6 @@
1.1 get AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml
1.2 on android 4.0+, run `adb backup com.amazon.kindle` from PC will get backup.ab
now android.py can convert backup.ab to AmazonSecureStorage.xml
2. run `k4mobidedrm.py -a AmazonSecureStorage.xml <infile> <outdir>'

View File

@@ -458,7 +458,11 @@ class DocParser(object):
(wtype, num) = pdesc[j]
if wtype == 'ocr' :
word = self.ocrtext[num]
try:
word = self.ocrtext[num]
except:
word = ""
sep = ' '
if handle_links:

View File

@@ -80,10 +80,12 @@ if inCalibre:
from calibre_plugins.dedrm import mobidedrm
from calibre_plugins.dedrm import topazextract
from calibre_plugins.dedrm import kgenpids
from calibre_plugins.dedrm import android
else:
import mobidedrm
import topazextract
import kgenpids
import android
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
@@ -273,7 +275,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, serials, pids):
def usage(progname):
print u"Removes DRM protection from Mobipocket, Amazon KF8, Amazon Print Replica and Amazon Topaz ebooks"
print u"Usage:"
print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname)
print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] [ -a <AmazonSecureStorage.xml> ] <infile> <outdir>".format(progname)
#
# Main
@@ -284,7 +286,7 @@ def cli_main():
print u"K4MobiDeDrm v{0}.\nCopyright © 2008-2013 The Dark Reverser et al.".format(__version__)
try:
opts, args = getopt.getopt(argv[1:], "k:p:s:")
opts, args = getopt.getopt(argv[1:], "k:p:s:a:")
except getopt.GetoptError, err:
print u"Error in options or arguments: {0}".format(err.args[0])
usage(progname)
@@ -312,6 +314,11 @@ def cli_main():
if a == None :
raise DrmException("Invalid parameter for -s")
serials = a.split(',')
if o == '-a':
if a == None:
continue
serials.extend(android.get_serials(a))
serials.extend(android.get_serials())
# try with built in Kindle Info files if not on Linux
k4 = not sys.platform.startswith('linux')

View File

@@ -19,6 +19,7 @@ from __future__ import with_statement
# 1.6 - Fixed a problem getting the disk serial numbers
# 1.7 - Work if TkInter is missing
# 1.8 - Fixes for Kindle for Mac, and non-ascii in Windows user names
# 1.9 - Fixes for Unicode in Windows user names
"""
@@ -26,7 +27,7 @@ Retrieve Kindle for PC/Mac user key.
"""
__license__ = 'GPL v3'
__version__ = '1.8'
__version__ = '1.9'
import sys, os, re
from struct import pack, unpack, unpack_from
@@ -907,18 +908,34 @@ if iswindows:
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
# Returns Environmental Variables that contain unicode
def getEnvironmentVariable(name):
import ctypes
name = unicode(name) # make sure string argument is unicode
n = ctypes.windll.kernel32.GetEnvironmentVariableW(name, None, 0)
if n == 0:
return None
buf = ctypes.create_unicode_buffer(u'\0'*n)
ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
return buf.value
# Locate all of the kindle-info style files and return as list
def getKindleInfoFiles():
kInfoFiles = []
# some 64 bit machines do not have the proper registry key for some reason
# or the pythonn interface to the 32 vs 64 bit registry is broken
# or the python interface to the 32 vs 64 bit registry is broken
path = ""
if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA']
# Python 2.x does not return unicode env. Use Python 3.x
path = winreg.ExpandEnvironmentStrings(u"%LOCALAPPDATA%")
# this is just another alternative.
# path = getEnvironmentVariable('LOCALAPPDATA')
if not os.path.isdir(path):
path = ""
else:
# User Shell Folders show take precedent over Shell Folders if present
try:
# this will still break
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
if not os.path.isdir(path):
@@ -937,13 +954,14 @@ if iswindows:
if path == "":
print ('Could not find the folder in which to look for kinfoFiles.')
else:
print('searching for kinfoFiles in ' + path)
# Probably not the best. To Fix (shouldn't ignore in encoding) or use utf-8
print(u'searching for kinfoFiles in ' + path.encode('ascii', 'ignore'))
# look for (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if os.path.isfile(kinfopath):
found = True
print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath)
print('Found K4PC 1.9+ kinf2011 file: ' + kinfopath.encode('ascii','ignore'))
kInfoFiles.append(kinfopath)
# look for (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
@@ -1142,7 +1160,7 @@ if iswindows:
cleartext = CryptUnprotectData(encryptedValue, entropy, 1)
DB[keyname] = cleartext
if 'MazamaRandomNumber' in DB and 'kindle.account.tokens' in DB:
if 'kindle.account.tokens' in DB:
print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(GetIDString(), GetUserName().decode("latin-1"))
# store values used in decryption
DB['IDString'] = GetIDString()
@@ -1758,7 +1776,7 @@ elif isosx:
break
except:
pass
if 'MazamaRandomNumber' in DB and 'kindle.account.tokens' in DB:
if 'kindle.account.tokens' in DB:
# store values used in decryption
print u"Decrypted key file using IDString '{0:s}' and UserName '{1:s}'".format(IDString, GetUserName())
DB['IDString'] = IDString

View File

@@ -156,6 +156,8 @@ def PC1(key, src, decryption=True):
return Pukall_Cipher().PC1(key,src,decryption)
except NameError:
pass
except TypeError:
pass
# use slow python version, since Pukall_Cipher didn't load
sum1 = 0;

View File

@@ -178,7 +178,12 @@ class DocParser(object):
if val == "":
val = 0
if not ((attr == 'hang') and (int(val) == 0)) :
if not ((attr == 'hang') and (int(val) == 0)):
try:
f = float(val)
except:
print "Warning: unrecognised val, ignoring"
val = 0
pv = float(val)/scale
cssargs[attr] = (self.attr_val_map[attr], pv)
keep = True

View File

@@ -356,7 +356,7 @@ class TopazBook:
self.setBookKey(bookKey)
self.createBookDirectory()
self.extractFiles()
self.extractFiles()
print u"Successfully Extracted Topaz contents"
if inCalibre:
from calibre_plugins.dedrm import genbook