mirror of
https://github.com/noDRM/DeDRM_tools.git
synced 2025-01-24 08:14:31 +06:00
Update androidkindlekey.py to work with tools
This commit is contained in:
parent
27d8f08b54
commit
b146e4b864
@ -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,23 +223,30 @@ 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
|
||||||
|
try :
|
||||||
read = open(path, 'rb')
|
read = open(path, 'rb')
|
||||||
head = read.read(24)
|
head = read.read(24)
|
||||||
if head[:14] == 'ANDROID BACKUP':
|
if head[:14] == 'ANDROID BACKUP':
|
||||||
output = StringIO(zlib.decompress(read.read()))
|
output = StringIO(zlib.decompress(read.read()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
read.close()
|
read.close()
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
@ -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);
|
||||||
|
@ -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,23 +223,30 @@ 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
|
||||||
|
try :
|
||||||
read = open(path, 'rb')
|
read = open(path, 'rb')
|
||||||
head = read.read(24)
|
head = read.read(24)
|
||||||
if head[:14] == 'ANDROID BACKUP':
|
if head[:14] == 'ANDROID BACKUP':
|
||||||
output = StringIO(zlib.decompress(read.read()))
|
output = StringIO(zlib.decompress(read.read()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
read.close()
|
read.close()
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
@ -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);
|
||||||
|
@ -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,23 +223,30 @@ 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
|
||||||
|
try :
|
||||||
read = open(path, 'rb')
|
read = open(path, 'rb')
|
||||||
head = read.read(24)
|
head = read.read(24)
|
||||||
if head[:14] == 'ANDROID BACKUP':
|
if head[:14] == 'ANDROID BACKUP':
|
||||||
output = StringIO(zlib.decompress(read.read()))
|
output = StringIO(zlib.decompress(read.read()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
read.close()
|
read.close()
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user