Skip to content

Commit

Permalink
tools v6.0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
apprenticealf committed Mar 7, 2015
1 parent 74a4c89 commit b1feca3
Show file tree
Hide file tree
Showing 54 changed files with 11,419 additions and 8,812 deletions.
6 changes: 3 additions & 3 deletions DeDRM_Macintosh_Application/DeDRM.app/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM AppleScript 6.0.7. Written 2010–2013 by Apprentice Alf and others.</string>
<string>DeDRM AppleScript 6.0.8. Written 2010–2013 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key>
<string>DeDRM</string>
<key>CFBundleIdentifier</key>
<string>com.apple.ScriptEditor.id.707CCCD5-0C6C-4BEB-B67C-B6E866ADE85A</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0.7</string>
<string>6.0.8</string>
<key>CFBundleName</key>
<string>DeDRM</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>6.0.7</string>
<string>6.0.8</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSRequiresCarbon</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@
# 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'
Expand Down Expand Up @@ -313,7 +316,7 @@ def PDFDecrypt(self,path_to_ebook):
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

Expand Down Expand Up @@ -391,7 +394,7 @@ def KindleMobiDecrypt(self,path_to_ebook):
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

Expand Down
157 changes: 157 additions & 0 deletions DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/android.py
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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>'
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,11 @@ def buildParagraph(self, pclass, pdesc, type, regtype) :
(wtype, num) = pdesc[j]

if wtype == 'ocr' :
word = self.ocrtext[num]
try:
word = self.ocrtext[num]
except:
word = ""

sep = ' '

if handle_links:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ class DrmException(Exception):
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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
# 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


"""
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
Expand Down Expand Up @@ -907,18 +908,34 @@ def CryptUnprotectData(indata, entropy, flags):
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):
Expand All @@ -937,13 +954,14 @@ def getKindleInfoFiles():
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
Expand Down Expand Up @@ -1142,7 +1160,7 @@ def getDBfromFile(kInfoFile):
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()
Expand Down Expand Up @@ -1758,7 +1776,7 @@ def getDBfromFile(kInfoFile):
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,12 @@ def process(self):
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
Expand Down
Loading

0 comments on commit b1feca3

Please sign in to comment.