Update androidkindlekey.py to work with tools

This commit is contained in:
Apprentice Alf 2015-03-17 07:07:12 +00:00
parent 27d8f08b54
commit b146e4b864
3 changed files with 570 additions and 90 deletions

View File

@ -1,16 +1,107 @@
#!/usr/bin/env python #!/usr/bin/env python
#fileencoding: utf-8 # -*- coding: utf-8 -*-
from __future__ import with_statement
# androidkindlekey.py
# Copyright © 2013-15 by Thom
# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper
#
# Revision history:
# 1.0 - Android serial number extracted from AmazonSecureStorage.xml
# 1.1 - Fixes and enhancements of some kind
# 1.2 - Changed to be callable from AppleScript by returning only serial number
# - and changed name to androidkindlekey.py
# - and added in unicode command line support
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.2'
import os import os
import sys import sys
import getopt
import tempfile
import zlib import zlib
import tarfile import tarfile
from hashlib import md5 from hashlib import md5
from cStringIO import StringIO from cStringIO import StringIO
from binascii import a2b_hex, b2a_hex from binascii import a2b_hex, b2a_hex
STORAGE = 'AmazonSecureStorage.xml' # Routines common to Mac and PC
STORAGE2 = 'map_data_storage.db'
# 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,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"kindlekey.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]
class DrmException(Exception):
pass
STORAGE = u"backup.ab"
STORAGE1 = u"AmazonSecureStorage.xml"
STORAGE2 = u"map_data_storage.db"
class AndroidObfuscation(object): class AndroidObfuscation(object):
'''AndroidObfuscation '''AndroidObfuscation
@ -75,10 +166,8 @@ def parse_preference(path):
read.close() read.close()
return storage return storage
def get_serials(path=None): def get_serials1(path=STORAGE1):
''' get serials from android's shared preference xml ''' ''' get serials from android's shared preference xml '''
if path is None and os.path.isfile("backup.ab"):
return get_storage()
if not os.path.isfile(path): if not os.path.isfile(path):
return [] return []
@ -86,10 +175,8 @@ def get_serials(path=None):
storage = parse_preference(path) storage = parse_preference(path)
salt = storage.get('AmazonSaltKey') salt = storage.get('AmazonSaltKey')
if salt and len(salt) == 16: if salt and len(salt) == 16:
sys.stdout.write('Using AndroidObfuscationV2\n')
obfuscation = AndroidObfuscationV2(a2b_hex(salt)) obfuscation = AndroidObfuscationV2(a2b_hex(salt))
else: else:
sys.stdout.write('Using AndroidObfuscation\n')
obfuscation = AndroidObfuscation() obfuscation = AndroidObfuscation()
def get_value(key): def get_value(key):
@ -118,6 +205,10 @@ def get_serials(path=None):
return serials return serials
def get_serials2(path=STORAGE2): def get_serials2(path=STORAGE2):
''' get serials from android's shared preference xml '''
if not os.path.isfile(path):
return []
import sqlite3 import sqlite3
connection = sqlite3.connect(path) connection = sqlite3.connect(path)
cursor = connection.cursor() cursor = connection.cursor()
@ -132,24 +223,31 @@ def get_serials2(path=STORAGE2):
serials.append('%s%s' % (x, y)) serials.append('%s%s' % (x, y))
return serials return serials
def get_storage(path='backup.ab'): def get_serials(path=STORAGE):
'''get AmazonSecureStorage.xml from android backup.ab '''get serials from files in from android backup.ab
backup.ab can be get using adb command: backup.ab can be get using adb command:
shell> adb backup com.amazon.kindle shell> adb backup com.amazon.kindle
or from individual files if they're passed.
''' '''
if not os.path.isfile(path): if not os.path.isfile(path):
serials = [] return []
if os.path.isfile(STORAGE2):
serials.extend(get_serials2(STORAGE2)) basename = os.path.basename(path)
if os.path.isfile(STORAGE): if basename == STORAGE1:
serials.extend(get_serials(STORAGE)) return get_serials1(path)
return serials elif basename == STORAGE2:
return get_serials2(path)
output = None output = None
read = open(path, 'rb') try :
head = read.read(24) read = open(path, 'rb')
if head[:14] == 'ANDROID BACKUP': head = read.read(24)
output = StringIO(zlib.decompress(read.read())) if head[:14] == 'ANDROID BACKUP':
read.close() output = StringIO(zlib.decompress(read.read()))
except Exception:
pass
finally:
read.close()
if not output: if not output:
return [] return []
@ -157,21 +255,83 @@ def get_storage(path='backup.ab'):
serials = [] serials = []
tar = tarfile.open(fileobj=output) tar = tarfile.open(fileobj=output)
for member in tar.getmembers(): for member in tar.getmembers():
if member.name.strip().endswith(STORAGE2): if member.name.strip().endswith(STORAGE1):
write = open(STORAGE2, 'w') write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials2(STORAGE2)) write_path = os.path.abspath(write.name)
elif member.name.strip().endswith(STORAGE): serials.extend(get_serials1(write_path))
write = open(STORAGE, 'w') os.remove(write_path)
elif member.name.strip().endswith(STORAGE2):
write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials(STORAGE)) write_path = os.path.abspath(write.name)
serials.extend(get_serials2(write_path))
os.remove(write_path)
return serials return serials
__all__ = [ 'get_storage', 'get_serials', 'parse_preference', __all__ = [ 'get_serials' ]
'AndroidObfuscation', 'AndroidObfuscationV2', 'STORAGE']
def usage(progname):
print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__)
print u"Decrypts the serial number of Kindle For Android from Android backup or file"
print u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+."
print u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml"
print u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db"
print u""
print u"Serial number is written to standard output."
print u"Usage:"
print u" {0:s} [-h] <inputfile>".format(progname)
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
try:
opts, args = getopt.getopt(argv[1:], "h")
except getopt.GetoptError, err:
usage(progname)
print u"\nError in options or arguments: {0}".format(err.args[0])
return 2
files = []
for o, a in opts:
if o == "-h":
usage(progname)
return 0
if len(args) > 1:
usage(progname)
return 2
inpath = args[0]
if not os.path.isabs(inpath):
inpath = os.path.abspath(inpath)
inpath = os.path.realpath(os.path.normpath(inpath))
if not os.path.isfile(inpath):
usage(progname)
print u"\n{0:s} file not found".format(inpath)
return 2
serials = get_serials(inpath)
if len(serials) == 0:
print u"No keys found in {0:s}".format(inpath)
return 2
for serial in serials:
print serial
return 0
if __name__ == '__main__': if __name__ == '__main__':
print get_serials() if len(sys.argv) > 1:
sys.exit(cli_main())
usage(os.path.basename(unicode_argv()[0]))
sys.exit(0);

View File

@ -1,16 +1,107 @@
#!/usr/bin/env python #!/usr/bin/env python
#fileencoding: utf-8 # -*- coding: utf-8 -*-
from __future__ import with_statement
# androidkindlekey.py
# Copyright © 2013-15 by Thom
# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper
#
# Revision history:
# 1.0 - Android serial number extracted from AmazonSecureStorage.xml
# 1.1 - Fixes and enhancements of some kind
# 1.2 - Changed to be callable from AppleScript by returning only serial number
# - and changed name to androidkindlekey.py
# - and added in unicode command line support
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.2'
import os import os
import sys import sys
import getopt
import tempfile
import zlib import zlib
import tarfile import tarfile
from hashlib import md5 from hashlib import md5
from cStringIO import StringIO from cStringIO import StringIO
from binascii import a2b_hex, b2a_hex from binascii import a2b_hex, b2a_hex
STORAGE = 'AmazonSecureStorage.xml' # Routines common to Mac and PC
STORAGE2 = 'map_data_storage.db'
# 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,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"kindlekey.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]
class DrmException(Exception):
pass
STORAGE = u"backup.ab"
STORAGE1 = u"AmazonSecureStorage.xml"
STORAGE2 = u"map_data_storage.db"
class AndroidObfuscation(object): class AndroidObfuscation(object):
'''AndroidObfuscation '''AndroidObfuscation
@ -75,10 +166,8 @@ def parse_preference(path):
read.close() read.close()
return storage return storage
def get_serials(path=None): def get_serials1(path=STORAGE1):
''' get serials from android's shared preference xml ''' ''' get serials from android's shared preference xml '''
if path is None and os.path.isfile("backup.ab"):
return get_storage()
if not os.path.isfile(path): if not os.path.isfile(path):
return [] return []
@ -86,10 +175,8 @@ def get_serials(path=None):
storage = parse_preference(path) storage = parse_preference(path)
salt = storage.get('AmazonSaltKey') salt = storage.get('AmazonSaltKey')
if salt and len(salt) == 16: if salt and len(salt) == 16:
sys.stdout.write('Using AndroidObfuscationV2\n')
obfuscation = AndroidObfuscationV2(a2b_hex(salt)) obfuscation = AndroidObfuscationV2(a2b_hex(salt))
else: else:
sys.stdout.write('Using AndroidObfuscation\n')
obfuscation = AndroidObfuscation() obfuscation = AndroidObfuscation()
def get_value(key): def get_value(key):
@ -118,6 +205,10 @@ def get_serials(path=None):
return serials return serials
def get_serials2(path=STORAGE2): def get_serials2(path=STORAGE2):
''' get serials from android's shared preference xml '''
if not os.path.isfile(path):
return []
import sqlite3 import sqlite3
connection = sqlite3.connect(path) connection = sqlite3.connect(path)
cursor = connection.cursor() cursor = connection.cursor()
@ -132,24 +223,31 @@ def get_serials2(path=STORAGE2):
serials.append('%s%s' % (x, y)) serials.append('%s%s' % (x, y))
return serials return serials
def get_storage(path='backup.ab'): def get_serials(path=STORAGE):
'''get AmazonSecureStorage.xml from android backup.ab '''get serials from files in from android backup.ab
backup.ab can be get using adb command: backup.ab can be get using adb command:
shell> adb backup com.amazon.kindle shell> adb backup com.amazon.kindle
or from individual files if they're passed.
''' '''
if not os.path.isfile(path): if not os.path.isfile(path):
serials = [] return []
if os.path.isfile(STORAGE2):
serials.extend(get_serials2(STORAGE2)) basename = os.path.basename(path)
if os.path.isfile(STORAGE): if basename == STORAGE1:
serials.extend(get_serials(STORAGE)) return get_serials1(path)
return serials elif basename == STORAGE2:
return get_serials2(path)
output = None output = None
read = open(path, 'rb') try :
head = read.read(24) read = open(path, 'rb')
if head[:14] == 'ANDROID BACKUP': head = read.read(24)
output = StringIO(zlib.decompress(read.read())) if head[:14] == 'ANDROID BACKUP':
read.close() output = StringIO(zlib.decompress(read.read()))
except Exception:
pass
finally:
read.close()
if not output: if not output:
return [] return []
@ -157,21 +255,83 @@ def get_storage(path='backup.ab'):
serials = [] serials = []
tar = tarfile.open(fileobj=output) tar = tarfile.open(fileobj=output)
for member in tar.getmembers(): for member in tar.getmembers():
if member.name.strip().endswith(STORAGE2): if member.name.strip().endswith(STORAGE1):
write = open(STORAGE2, 'w') write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials2(STORAGE2)) write_path = os.path.abspath(write.name)
elif member.name.strip().endswith(STORAGE): serials.extend(get_serials1(write_path))
write = open(STORAGE, 'w') os.remove(write_path)
elif member.name.strip().endswith(STORAGE2):
write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials(STORAGE)) write_path = os.path.abspath(write.name)
serials.extend(get_serials2(write_path))
os.remove(write_path)
return serials return serials
__all__ = [ 'get_storage', 'get_serials', 'parse_preference', __all__ = [ 'get_serials' ]
'AndroidObfuscation', 'AndroidObfuscationV2', 'STORAGE']
def usage(progname):
print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__)
print u"Decrypts the serial number of Kindle For Android from Android backup or file"
print u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+."
print u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml"
print u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db"
print u""
print u"Serial number is written to standard output."
print u"Usage:"
print u" {0:s} [-h] <inputfile>".format(progname)
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
try:
opts, args = getopt.getopt(argv[1:], "h")
except getopt.GetoptError, err:
usage(progname)
print u"\nError in options or arguments: {0}".format(err.args[0])
return 2
files = []
for o, a in opts:
if o == "-h":
usage(progname)
return 0
if len(args) > 1:
usage(progname)
return 2
inpath = args[0]
if not os.path.isabs(inpath):
inpath = os.path.abspath(inpath)
inpath = os.path.realpath(os.path.normpath(inpath))
if not os.path.isfile(inpath):
usage(progname)
print u"\n{0:s} file not found".format(inpath)
return 2
serials = get_serials(inpath)
if len(serials) == 0:
print u"No keys found in {0:s}".format(inpath)
return 2
for serial in serials:
print serial
return 0
if __name__ == '__main__': if __name__ == '__main__':
print get_serials() if len(sys.argv) > 1:
sys.exit(cli_main())
usage(os.path.basename(unicode_argv()[0]))
sys.exit(0);

View File

@ -1,16 +1,107 @@
#!/usr/bin/env python #!/usr/bin/env python
#fileencoding: utf-8 # -*- coding: utf-8 -*-
from __future__ import with_statement
# androidkindlekey.py
# Copyright © 2013-15 by Thom
# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper
#
# Revision history:
# 1.0 - Android serial number extracted from AmazonSecureStorage.xml
# 1.1 - Fixes and enhancements of some kind
# 1.2 - Changed to be callable from AppleScript by returning only serial number
# - and changed name to androidkindlekey.py
# - and added in unicode command line support
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '1.2'
import os import os
import sys import sys
import getopt
import tempfile
import zlib import zlib
import tarfile import tarfile
from hashlib import md5 from hashlib import md5
from cStringIO import StringIO from cStringIO import StringIO
from binascii import a2b_hex, b2a_hex from binascii import a2b_hex, b2a_hex
STORAGE = 'AmazonSecureStorage.xml' # Routines common to Mac and PC
STORAGE2 = 'map_data_storage.db'
# 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,unicode):
data = data.encode(self.encoding,"replace")
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
xrange(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return [u"kindlekey.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]
class DrmException(Exception):
pass
STORAGE = u"backup.ab"
STORAGE1 = u"AmazonSecureStorage.xml"
STORAGE2 = u"map_data_storage.db"
class AndroidObfuscation(object): class AndroidObfuscation(object):
'''AndroidObfuscation '''AndroidObfuscation
@ -75,10 +166,8 @@ def parse_preference(path):
read.close() read.close()
return storage return storage
def get_serials(path=None): def get_serials1(path=STORAGE1):
''' get serials from android's shared preference xml ''' ''' get serials from android's shared preference xml '''
if path is None and os.path.isfile("backup.ab"):
return get_storage()
if not os.path.isfile(path): if not os.path.isfile(path):
return [] return []
@ -86,10 +175,8 @@ def get_serials(path=None):
storage = parse_preference(path) storage = parse_preference(path)
salt = storage.get('AmazonSaltKey') salt = storage.get('AmazonSaltKey')
if salt and len(salt) == 16: if salt and len(salt) == 16:
sys.stdout.write('Using AndroidObfuscationV2\n')
obfuscation = AndroidObfuscationV2(a2b_hex(salt)) obfuscation = AndroidObfuscationV2(a2b_hex(salt))
else: else:
sys.stdout.write('Using AndroidObfuscation\n')
obfuscation = AndroidObfuscation() obfuscation = AndroidObfuscation()
def get_value(key): def get_value(key):
@ -118,6 +205,10 @@ def get_serials(path=None):
return serials return serials
def get_serials2(path=STORAGE2): def get_serials2(path=STORAGE2):
''' get serials from android's shared preference xml '''
if not os.path.isfile(path):
return []
import sqlite3 import sqlite3
connection = sqlite3.connect(path) connection = sqlite3.connect(path)
cursor = connection.cursor() cursor = connection.cursor()
@ -132,24 +223,31 @@ def get_serials2(path=STORAGE2):
serials.append('%s%s' % (x, y)) serials.append('%s%s' % (x, y))
return serials return serials
def get_storage(path='backup.ab'): def get_serials(path=STORAGE):
'''get AmazonSecureStorage.xml from android backup.ab '''get serials from files in from android backup.ab
backup.ab can be get using adb command: backup.ab can be get using adb command:
shell> adb backup com.amazon.kindle shell> adb backup com.amazon.kindle
or from individual files if they're passed.
''' '''
if not os.path.isfile(path): if not os.path.isfile(path):
serials = [] return []
if os.path.isfile(STORAGE2):
serials.extend(get_serials2(STORAGE2)) basename = os.path.basename(path)
if os.path.isfile(STORAGE): if basename == STORAGE1:
serials.extend(get_serials(STORAGE)) return get_serials1(path)
return serials elif basename == STORAGE2:
return get_serials2(path)
output = None output = None
read = open(path, 'rb') try :
head = read.read(24) read = open(path, 'rb')
if head[:14] == 'ANDROID BACKUP': head = read.read(24)
output = StringIO(zlib.decompress(read.read())) if head[:14] == 'ANDROID BACKUP':
read.close() output = StringIO(zlib.decompress(read.read()))
except Exception:
pass
finally:
read.close()
if not output: if not output:
return [] return []
@ -157,21 +255,83 @@ def get_storage(path='backup.ab'):
serials = [] serials = []
tar = tarfile.open(fileobj=output) tar = tarfile.open(fileobj=output)
for member in tar.getmembers(): for member in tar.getmembers():
if member.name.strip().endswith(STORAGE2): if member.name.strip().endswith(STORAGE1):
write = open(STORAGE2, 'w') write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials2(STORAGE2)) write_path = os.path.abspath(write.name)
elif member.name.strip().endswith(STORAGE): serials.extend(get_serials1(write_path))
write = open(STORAGE, 'w') os.remove(write_path)
elif member.name.strip().endswith(STORAGE2):
write = tempfile.NamedTemporaryFile(mode='w', delete=False)
write.write(tar.extractfile(member).read()) write.write(tar.extractfile(member).read())
write.close() write.close()
serials.extend(get_serials(STORAGE)) write_path = os.path.abspath(write.name)
serials.extend(get_serials2(write_path))
os.remove(write_path)
return serials return serials
__all__ = [ 'get_storage', 'get_serials', 'parse_preference', __all__ = [ 'get_serials' ]
'AndroidObfuscation', 'AndroidObfuscationV2', 'STORAGE']
def usage(progname):
print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__)
print u"Decrypts the serial number of Kindle For Android from Android backup or file"
print u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+."
print u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml"
print u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db"
print u""
print u"Serial number is written to standard output."
print u"Usage:"
print u" {0:s} [-h] <inputfile>".format(progname)
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
try:
opts, args = getopt.getopt(argv[1:], "h")
except getopt.GetoptError, err:
usage(progname)
print u"\nError in options or arguments: {0}".format(err.args[0])
return 2
files = []
for o, a in opts:
if o == "-h":
usage(progname)
return 0
if len(args) > 1:
usage(progname)
return 2
inpath = args[0]
if not os.path.isabs(inpath):
inpath = os.path.abspath(inpath)
inpath = os.path.realpath(os.path.normpath(inpath))
if not os.path.isfile(inpath):
usage(progname)
print u"\n{0:s} file not found".format(inpath)
return 2
serials = get_serials(inpath)
if len(serials) == 0:
print u"No keys found in {0:s}".format(inpath)
return 2
for serial in serials:
print serial
return 0
if __name__ == '__main__': if __name__ == '__main__':
print get_serials() if len(sys.argv) > 1:
sys.exit(cli_main())
usage(os.path.basename(unicode_argv()[0]))
sys.exit(0);