tools v6.0.1

pull/6/head v6.0.1
Apprentice Alf 11 years ago
parent 20bc936e99
commit a2f044e672

@ -24,17 +24,17 @@
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>DeDRM AppleScript 6.0.0. Written 20102013 by Apprentice Alf and others.</string>
<string>DeDRM AppleScript 6.0.1. Written 20102013 by Apprentice Alf and others.</string>
<key>CFBundleIconFile</key>
<string>DeDRM</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DeDRM AppleScript</string>
<string>DeDRM</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>6.0.0</string>
<string>6.0.1</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSRequiresCarbon</key>

@ -26,13 +26,14 @@ __docformat__ = 'restructuredtext en'
#
# Revision history:
# 6.0.0 - Initial release
# 6.0.1 - Bug Fixes for Windows App
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 0)
PLUGIN_VERSION_TUPLE = (6, 0, 1)
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'

@ -46,13 +46,14 @@ from __future__ import with_statement
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
# 5.7 - Unicode support added, renamed adobekey from ineptkey
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '5.8'
__version__ = '5.9'
import sys, os, struct, getopt
@ -483,7 +484,8 @@ def usage(progname):
print u"Usage:"
print u" {0:s} [-h] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2009-2013 i♥cabbages and Apprentice Alf".format(progname,__version__)
@ -538,7 +540,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -556,6 +558,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -9,6 +9,7 @@
#
# Changelog epubtest
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
# 1.01 - Added routine for use by Windows DeDRM
#
# Written in 2011 by Paul Durrant
# Released with unlicense. See http://unlicense.org/
@ -45,7 +46,7 @@
from __future__ import with_statement
__version__ = '1.00'
__version__ = '1.01'
import sys, struct, os
import zlib
@ -72,11 +73,49 @@ class SafeUnbuffered:
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():
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]
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"epubtest.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]
_FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28
@ -129,38 +168,38 @@ def getfiledata(file, zi):
return data
def main(argv=unicode_argv()):
infile = argv[1]
kind = "Unknown"
def encryption(infile):
# returns encryption: one of Unencrypted, Adobe, B&N and Unknown
encryption = "Unknown"
with file(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Mobipocket/Kindle
if bookdata[0:0+2] == "PK":
if bookdata[30:30+28] == 'mimetypeapplication/epub+zip':
kind = "ePub"
else:
kind = "ZIP"
encryption = "Unencrypted"
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
try:
with open(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Zip
if bookdata[0:0+2] == "PK":
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
encryption = "Unknown"
print u"{0} {1}".format(encryption, kind)
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
else:
encryption = "Unknown"
except:
traceback.print_exc()
return encryption
def main():
argv=unicode_argv()
print encryption(argv[1])
return 0
if __name__ == "__main__":

@ -66,8 +66,9 @@
# - Don't reject dictionary format.
# - Ignore sidebars for dictionaries (different format?)
# 0.22 - Unicode and plugin support, different image folders for PMLZ and source
# 0.23 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__='0.22'
__version__='0.23'
import sys, re
import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile, traceback
@ -551,9 +552,10 @@ def getuser_key(name,cc):
cc = cc.replace(" ","")
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
def cli_main(argv=unicode_argv()):
def cli_main():
print u"eRdr2Pml v{0}. Copyright © 20092012 The Dark Reverser et al.".format(__version__)
argv=unicode_argv()
try:
opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
except getopt.GetoptError, err:

@ -33,13 +33,14 @@ from __future__ import with_statement
# 3.6 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 3.7 - Tweaked to match ineptepub more closely
# 3.8 - Fixed to retain zip file metadata (e.g. file modification date)
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Barnes & Noble encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "3.8"
__version__ = "3.9"
import sys
import os
@ -316,7 +317,8 @@ def decryptBook(keyb64, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.b64> <inbook.epub> <outbook.epub>".format(progname)

@ -31,13 +31,14 @@ from __future__ import with_statement
# 2.3 - Modify interface to allow use of import
# 2.4 - Improvements to UI and now works in plugins
# 2.5 - Additional improvement for unicode and plugin support
# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
"""
__license__ = 'GPL v3'
__version__ = "2.5"
__version__ = "2.6"
import sys
import os
@ -214,7 +215,8 @@ def generate_key(name, ccn):
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if AES is None:
print "%s: This script requires OpenSSL or PyCrypto, which must be installed " \

@ -35,13 +35,14 @@ from __future__ import with_statement
# 5.7 - Fix for potential problem with PyCrypto
# 5.8 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 5.9 - Fixed to retain zip file metadata (e.g. file modification date)
# 5.10 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Adobe Digital Editions encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "5.9"
__version__ = "5.10"
import sys
import os
@ -458,7 +459,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname)

@ -50,13 +50,14 @@ from __future__ import with_statement
# 7.11 - More tweaks to fix minor problems.
# 7.12 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 7.13 - Fixed erroneous mentions of ineptepub
# 7.14 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypts Adobe ADEPT-encrypted PDF files.
"""
__license__ = 'GPL v3'
__version__ = "7.13"
__version__ = "7.14"
import sys
import os
@ -2185,7 +2186,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)

@ -53,8 +53,9 @@ from __future__ import with_statement
# 4.9 - Missed some invalid characters in cleanup_name
# 5.0 - Extraction of info from Kindle for PC/Mac moved into kindlekey.py
# - tweaked GetDecryptedBook interface to leave passed parameters unchanged
# 5.1 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '5.0'
__version__ = '5.1'
import sys, os, re
@ -276,7 +277,8 @@ def usage(progname):
#
# Main
#
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"K4MobiDeDrm v{0}.\nCopyright © 2008-2013 The Dark Reverser et al.".format(__version__)

@ -15,13 +15,16 @@ from __future__ import with_statement
# 1.3 - Added getkey interface for Windows DeDRM application
# Simplified some of the Kindle for Mac code.
# 1.4 - Remove dependency on alfcrypto
# 1.5 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 1.6 - Fixed a problem getting the disk serial numbers
"""
Retrieve Kindle for PC/Mac user key.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.6'
import sys, os, re
from struct import pack, unpack, unpack_from
@ -1266,10 +1269,10 @@ elif isosx:
# uses a sub process to get the Hard Drive Serial Number using ioreg
# returns serial numbers of all internal hard drive drives
def GetVolumesSerialNumbers():
sernums = []
sernum = os.getenv('MYSERIALNUMBER')
if sernum != None:
return [sernum]
sernums = []
sernums.append(sernum.strip())
cmdline = '/usr/sbin/ioreg -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1285,7 +1288,7 @@ elif isosx:
if pp >= 0:
sernum = resline[pp+19:-1]
sernums.append(sernum.strip())
return [sernum]
return sernums
def GetUserHomeAppSupKindleDirParitionName():
home = os.getenv('HOME')
@ -1311,10 +1314,11 @@ elif isosx:
return disk
# uses a sub process to get the UUID of the specified disk partition using ioreg
def GetDiskPartitionUUID(diskpart):
def GetDiskPartitionUUIDs(diskpart):
uuids = []
uuidnum = os.getenv('MYUUIDNUMBER')
if uuidnum != None:
return uuidnum
uuids.append(strip(uuidnum))
cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1357,14 +1361,15 @@ elif isosx:
uuidnest = -1
uuidnum = None
bsdname = None
if not foundIt:
uuidnum = ''
return uuidnum
if foundIt:
uuids.append(uuidnum)
return uuids
def GetMACAddressMunged():
def GetMACAddressesMunged():
macnums = []
macnum = os.getenv('MYMACNUM')
if macnum != None:
return macnum
macnums.append(macnum)
cmdline = '/sbin/ifconfig en0'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1399,9 +1404,9 @@ elif isosx:
macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
foundIt = True
break
if not foundIt:
macnum = ''
return macnum
if foundIt:
macnums.append(macnum)
return macnums
# uses unix env to get username instead of using sysctlbyname
@ -1412,11 +1417,12 @@ elif isosx:
def GetIDStrings():
# Return all possible ID Strings
strings = []
strings.append(GetMACAddressMunged())
strings.extend(GetMACAddressesMunged())
strings.extend(GetVolumesSerialNumbers())
diskpart = GetUserHomeAppSupKindleDirParitionName()
strings.append(GetDiskPartitionUUID(diskpart))
strings.extend(GetDiskPartitionUUIDs(diskpart))
strings.append('9999999999')
#print strings
return strings
@ -1797,7 +1803,8 @@ def usage(progname):
print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2010-2013 some_updates and Apprentice Alf".format(progname,__version__)
@ -1837,7 +1844,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -1855,6 +1862,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -9,6 +9,7 @@
# 0.3 changed to autoflush stdout, fixed return code usage
# 0.3 updated for unicode
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
import sys
import binascii
@ -111,8 +112,9 @@ def pidFromSerial(s, l):
return pid
def cli_main(argv=unicode_argv()):
def cli_main():
print u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky"
argv=unicode_argv()
if len(argv)==2:
serial = argv[1]
else:

@ -67,9 +67,10 @@
# 0.37 - Fixed double announcement for stand-alone operation
# 0.38 - Unicode used wherever possible, cope with absent alfcrypto
# 0.39 - Fixed problem with TEXtREAd and getBookType interface
# 0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = u"0.39"
__version__ = u"0.40"
import sys
import os
@ -506,7 +507,8 @@ def getUnencryptedBook(infile,pidlist):
return book.mobi_data
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4:
print u"MobiDeDrm v{0}.\nCopyright © 2008-2012 The Dark Reverser et al.".format(__version__)

@ -0,0 +1,176 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
import sys
import os
import re
import ineptepub
import ignobleepub
import epubtest
import zipfix
import ineptpdf
import erdr2pml
import k4mobidedrm
import traceback
def decryptepub(infile, outdir, rscpath):
errlog = ''
# first fix the epub to make sure we do not get errors
name, ext = os.path.splitext(os.path.basename(infile))
bpath = os.path.dirname(infile)
zippath = os.path.join(bpath,name + '_temp.zip')
rv = zipfix.repairBook(infile, zippath)
if rv != 0:
print "Error while trying to fix epub"
return rv
# determine a good name for the output file
outfile = os.path.join(outdir, name + '_nodrm.epub')
rv = 1
# first try with the Adobe adept epub
if ineptepub.adeptBook(zippath):
# try with any keyfiles (*.der) in the rscpath
files = os.listdir(rscpath)
filefilter = re.compile("\.der$", re.IGNORECASE)
files = filter(filefilter.search, files)
if files:
for filename in files:
keypath = os.path.join(rscpath, filename)
userkey = open(keypath,'rb').read()
try:
rv = ineptepub.decryptBook(userkey, zippath, outfile)
if rv == 0:
print "Decrypted Adobe ePub with key file {0}".format(filename)
break
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
# now try with ignoble epub
elif ignobleepub.ignobleBook(zippath):
# try with any keyfiles (*.b64) in the rscpath
files = os.listdir(rscpath)
filefilter = re.compile("\.b64$", re.IGNORECASE)
files = filter(filefilter.search, files)
if files:
for filename in files:
keypath = os.path.join(rscpath, filename)
userkey = open(keypath,'r').read()
#print userkey
try:
rv = ignobleepub.decryptBook(userkey, zippath, outfile)
if rv == 0:
print "Decrypted B&N ePub with key file {0}".format(filename)
break
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
else:
encryption = epubtest.encryption(zippath)
if encryption == "Unencrypted":
print "{0} is not DRMed.".format(name)
rv = 0
else:
print "{0} has an unknown encryption.".format(name)
os.remove(zippath)
if rv != 0:
print errlog
return rv
def decryptpdf(infile, outdir, rscpath):
errlog = ''
rv = 1
# determine a good name for the output file
name, ext = os.path.splitext(os.path.basename(infile))
outfile = os.path.join(outdir, name + '_nodrm.pdf')
# try with any keyfiles (*.der) in the rscpath
files = os.listdir(rscpath)
filefilter = re.compile("\.der$", re.IGNORECASE)
files = filter(filefilter.search, files)
if files:
for filename in files:
keypath = os.path.join(rscpath, filename)
userkey = open(keypath,'rb').read()
try:
rv = ineptpdf.decryptBook(userkey, infile, outfile)
if rv == 0:
break
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
if rv != 0:
print errlog
return rv
def decryptpdb(infile, outdir, rscpath):
outname = os.path.splitext(os.path.basename(infile))[0] + ".pmlz"
outpath = os.path.join(outdir, outname)
rv = 1
socialpath = os.path.join(rscpath,'sdrmlist.txt')
if os.path.exists(socialpath):
keydata = file(socialpath,'r').read()
keydata = keydata.rstrip(os.linesep)
ar = keydata.split(',')
for i in ar:
try:
name, cc8 = i.split(':')
except ValueError:
print ' Error parsing user supplied social drm data.'
return 1
try:
rv = erdr2pml.decryptBook(infile, outpath, True, erdr2pml.getuser_key(name, cc8))
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
if rv == 0:
break
return rv
def decryptk4mobi(infile, outdir, rscpath):
rv = 1
pidnums = []
pidspath = os.path.join(rscpath,'pidlist.txt')
if os.path.exists(pidspath):
pidstr = file(pidspath,'r').read()
pidstr = pidstr.rstrip(os.linesep)
pidstr = pidstr.strip()
if pidstr != '':
pidnums = pidstr.split(',')
serialnums = []
serialnumspath = os.path.join(rscpath,'seriallist.txt')
if os.path.exists(serialnumspath):
serialstr = file(serialnumspath,'r').read()
serialstr = serialstr.rstrip(os.linesep)
serialstr = serialstr.strip()
if serialstr != '':
serialnums = serialstr.split(',')
kDatabaseFiles = []
files = os.listdir(rscpath)
filefilter = re.compile("\.k4i$", re.IGNORECASE)
files = filter(filefilter.search, files)
if files:
for filename in files:
dpath = os.path.join(rscpath,filename)
kDatabaseFiles.append(dpath)
try:
rv = k4mobidedrm.decryptBook(infile, outdir, kDatabaseFiles, serialnums, pidnums)
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
return rv

@ -1,10 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# topazextract.py, version ?
# topazextract.py
# Mostly written by some_updates based on code from many others
__version__ = '4.8'
# Changelog
# 4.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '4.9'
import sys
import os, csv, getopt
@ -442,7 +445,8 @@ def usage(progname):
print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname)
# Main
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"TopazExtract v{0}.".format(__version__)

@ -1 +1 @@
chcp 65001 > nul && set PWD=%~dp0 && cd /d %~dp0DeDRM_lib && start /min python DeDRM_app.pyw %*
chcp 65001 > nul && set PWD=%~dp0 && cd /d %~dp0DeDRM_lib && start /min python DeDRM_App.pyw %*

@ -1,8 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# DeDRM.pyw, version 6.0.0
# By some_updates and Apprentice Alf
# DeDRM.pyw, version 6.0.1
# Copyright 2010-2013 some_updates and Apprentice Alf
# Revision history:
# 6.0.0 - Release along with unified plugin
# 6.0.1 - Bug Fixes for Windows App
__version__ = '6.0.1'
import sys
import os, os.path
@ -49,8 +55,6 @@ class QueuedUTF8Stream:
def __getattr__(self, attr):
return getattr(self.stream, attr)
__version__ = '6.0.0'
class DrmException(Exception):
pass
@ -372,7 +376,6 @@ class ConvDialog(Toplevel):
self.running = 'inactive'
self.numgood = 0
self.numbad = 0
self.log = ''
self.status = Tkinter.Label(self, text='DeDRM processing...')
self.status.pack(fill=Tkconstants.X, expand=1)
body = Tkinter.Frame(self)
@ -395,6 +398,8 @@ class ConvDialog(Toplevel):
self.qbutton.pack(side=Tkconstants.BOTTOM)
self.status['text'] = ''
self.logfile = open(os.path.join(os.path.expanduser('~'),'DeDRM.log'),'w')
def show(self):
self.deiconify()
self.tkraise()
@ -420,20 +425,19 @@ class ConvDialog(Toplevel):
if len(self.filenames) > 0:
filename = self.filenames.pop(0)
if filename == None:
msg = '\nComplete: '
msg = 'Complete: '
msg += 'Successes: %d, ' % self.numgood
msg += 'Failures: %d\n' % self.numbad
self.showCmdOutput(msg)
if self.numbad == 0:
self.after(2000,self.conversion_done())
logfile = os.path.join(os.path.expanduser('~'),'DeDRM.log')
file(logfile,'w').write(self.log)
self.log=''
self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
self.logfile.close()
return
infile = filename
bname = os.path.basename(infile)
msg = 'Processing: ' + bname + ' ... '
self.log += msg
msg = 'Processing: ' + bname + '...'
self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
self.showCmdOutput(msg)
outdir = os.path.dirname(filename)
if 'outdir' in self.prefs_array:
@ -447,7 +451,7 @@ class ConvDialog(Toplevel):
self.processQueue()
else:
msg = 'Unknown File: ' + bname + '\n'
self.log += msg
self.logfile.write("DeDRM v{0}: {1}".format(__version__,msg))
self.showCmdOutput(msg)
self.numbad += 1
@ -476,39 +480,32 @@ class ConvDialog(Toplevel):
# nothing to wait for so just return
return
poll = self.p2.exitcode
print "processing", poll
#print "processing", poll
done = False
text = ''
while not done:
try:
data = self.q.get_nowait()
text += data
except Empty:
done = True
if text != '':
self.logfile.write(text)
if poll != None:
self.bar.stop()
done = False
text = ''
while not done:
try:
data = self.q.get_nowait()
text += data
except Empty:
done = True
self.log += text
if poll == 0:
msg = 'Success\n'
self.numgood += 1
self.log += msg
else:
msg = 'Failed\n'
self.numbad += 1
self.log += msg
self.p2.join()
self.logfile.write("DeDRM v{0}: {1}\n".format(__version__,msg))
self.showCmdOutput(msg)
self.p2 = None
self.running = 'inactive'
self.after(50,self.processBooks)
return
try:
text = self.q.get_nowait()
except Empty:
text = ''
pass
if text != '':
self.log += text
# make sure we get invoked again by event loop after interval
self.stext.after(self.interval,self.processQueue)
return
@ -574,7 +571,8 @@ def processPDB(q, infile, outdir, rscpath):
sys.exit(rv)
def main(argv=unicode_argv()):
def main():
argv=unicode_argv()
apphome = os.path.dirname(argv[0])
apphome = os.path.abspath(apphome)

@ -26,13 +26,14 @@ __docformat__ = 'restructuredtext en'
#
# Revision history:
# 6.0.0 - Initial release
# 6.0.1 - Bug Fixes for Windows App
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 0)
PLUGIN_VERSION_TUPLE = (6, 0, 1)
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'

@ -46,13 +46,14 @@ from __future__ import with_statement
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
# 5.7 - Unicode support added, renamed adobekey from ineptkey
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '5.8'
__version__ = '5.9'
import sys, os, struct, getopt
@ -483,7 +484,8 @@ def usage(progname):
print u"Usage:"
print u" {0:s} [-h] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2009-2013 i♥cabbages and Apprentice Alf".format(progname,__version__)
@ -538,7 +540,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -556,6 +558,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -9,6 +9,7 @@
#
# Changelog epubtest
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
# 1.01 - Added routine for use by Windows DeDRM
#
# Written in 2011 by Paul Durrant
# Released with unlicense. See http://unlicense.org/
@ -45,7 +46,7 @@
from __future__ import with_statement
__version__ = '1.00'
__version__ = '1.01'
import sys, struct, os
import zlib
@ -72,11 +73,49 @@ class SafeUnbuffered:
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():
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]
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"epubtest.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]
_FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28
@ -129,38 +168,38 @@ def getfiledata(file, zi):
return data
def main(argv=unicode_argv()):
infile = argv[1]
kind = "Unknown"
def encryption(infile):
# returns encryption: one of Unencrypted, Adobe, B&N and Unknown
encryption = "Unknown"
with file(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Mobipocket/Kindle
if bookdata[0:0+2] == "PK":
if bookdata[30:30+28] == 'mimetypeapplication/epub+zip':
kind = "ePub"
else:
kind = "ZIP"
encryption = "Unencrypted"
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
try:
with open(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Zip
if bookdata[0:0+2] == "PK":
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
encryption = "Unknown"
print u"{0} {1}".format(encryption, kind)
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
else:
encryption = "Unknown"
except:
traceback.print_exc()
return encryption
def main():
argv=unicode_argv()
print encryption(argv[1])
return 0
if __name__ == "__main__":

@ -66,8 +66,9 @@
# - Don't reject dictionary format.
# - Ignore sidebars for dictionaries (different format?)
# 0.22 - Unicode and plugin support, different image folders for PMLZ and source
# 0.23 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__='0.22'
__version__='0.23'
import sys, re
import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile, traceback
@ -551,9 +552,10 @@ def getuser_key(name,cc):
cc = cc.replace(" ","")
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
def cli_main(argv=unicode_argv()):
def cli_main():
print u"eRdr2Pml v{0}. Copyright © 20092012 The Dark Reverser et al.".format(__version__)
argv=unicode_argv()
try:
opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
except getopt.GetoptError, err:

@ -1,84 +0,0 @@
#!/usr/bin/python
#
# This is a python script. You need a Python interpreter to run it.
# For example, ActiveState Python, which exists for windows.
#
# Changelog
# 1.00 - Initial version
# 1.01 - getPidList interface change
__version__ = '1.01'
import sys
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)
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
import os
import struct
import binascii
import kgenpids
import topazextract
import mobidedrm
from alfcrypto import Pukall_Cipher
class DrmException(Exception):
pass
def getK4PCpids(path_to_ebook):
# Return Kindle4PC PIDs. Assumes that the caller checked that we are not on Linux, which will raise an exception
mobi = True
magic3 = file(path_to_ebook,'rb').read(3)
if magic3 == 'TPZ':
mobi = False
if mobi:
mb = mobidedrm.MobiBook(path_to_ebook)
else:
mb = topazextract.TopazBook(path_to_ebook)
md1, md2 = mb.getPIDMetaInfo()
return kgenpids.getPidList(md1, md2)
def main(argv=sys.argv):
print ('getk4pcpids.py v%(__version__)s. '
'Copyright 2012 Apprentice Alf' % globals())
if len(argv)<2 or len(argv)>3:
print "Gets the possible book-specific PIDs from K4PC for a particular book"
print "Usage:"
print " %s <bookfile> [<outfile>]" % sys.argv[0]
return 1
else:
infile = argv[1]
try:
pidlist = getK4PCpids(infile)
except DrmException, e:
print "Error: %s" % e
return 1
pidstring = ','.join(pidlist)
print "Possible PIDs are: ", pidstring
if len(argv) is 3:
outfile = argv[2]
file(outfile, 'w').write(pidstring)
return 0
if __name__ == "__main__":
sys.exit(main())

@ -33,13 +33,14 @@ from __future__ import with_statement
# 3.6 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 3.7 - Tweaked to match ineptepub more closely
# 3.8 - Fixed to retain zip file metadata (e.g. file modification date)
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Barnes & Noble encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "3.8"
__version__ = "3.9"
import sys
import os
@ -316,7 +317,8 @@ def decryptBook(keyb64, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.b64> <inbook.epub> <outbook.epub>".format(progname)

@ -31,13 +31,14 @@ from __future__ import with_statement
# 2.3 - Modify interface to allow use of import
# 2.4 - Improvements to UI and now works in plugins
# 2.5 - Additional improvement for unicode and plugin support
# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
"""
__license__ = 'GPL v3'
__version__ = "2.5"
__version__ = "2.6"
import sys
import os
@ -214,7 +215,8 @@ def generate_key(name, ccn):
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if AES is None:
print "%s: This script requires OpenSSL or PyCrypto, which must be installed " \

@ -35,13 +35,14 @@ from __future__ import with_statement
# 5.7 - Fix for potential problem with PyCrypto
# 5.8 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 5.9 - Fixed to retain zip file metadata (e.g. file modification date)
# 5.10 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Adobe Digital Editions encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "5.9"
__version__ = "5.10"
import sys
import os
@ -458,7 +459,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname)

@ -1,512 +0,0 @@
#! /usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import with_statement
# ineptkey.pyw, version 5.6
# Copyright © 2009-2010 i♥cabbages
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Windows users: Before running this program, you must first install Python 2.6
# from <http://www.python.org/download/> and PyCrypto from
# <http://www.voidspace.org.uk/python/modules.shtml#pycrypto> (make certain
# to install the version for Python 2.6). Then save this script file as
# ineptkey.pyw and double-click on it to run it. It will create a file named
# adeptkey.der in the same directory. This is your ADEPT user key.
#
# Mac OS X users: Save this script file as ineptkey.pyw. You can run this
# program from the command line (pythonw ineptkey.pyw) or by double-clicking
# it when it has been associated with PythonLauncher. It will create a file
# named adeptkey.der in the same directory. This is your ADEPT user key.
# Revision history:
# 1 - Initial release, for Adobe Digital Editions 1.7
# 2 - Better algorithm for finding pLK; improved error handling
# 3 - Rename to INEPT
# 4 - Series of changes by joblack (and others?) --
# 4.1 - quick beta fix for ADE 1.7.2 (anon)
# 4.2 - added old 1.7.1 processing
# 4.3 - better key search
# 4.4 - Make it working on 64-bit Python
# 5 - Clean up and improve 4.x changes;
# Clean up and merge OS X support by unknown
# 5.1 - add support for using OpenSSL on Windows in place of PyCrypto
# 5.2 - added support for output of key to a particular file
# 5.3 - On Windows try PyCrypto first, OpenSSL next
# 5.4 - Modify interface to allow use of import
# 5.5 - Fix for potential problem with PyCrypto
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
import sys
import os
import struct
# 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 '?'.
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)]
return [u"ineptkey.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 ADEPTError(Exception):
pass
if iswindows:
from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
string_at, Structure, c_void_p, cast, c_size_t, memmove, CDLL, c_int, \
c_long, c_ulong
from ctypes.wintypes import LPVOID, DWORD, BOOL
import _winreg as winreg
def _load_crypto_libcrypto():
from ctypes.util import find_library
libcrypto = find_library('libeay32')
if libcrypto is None:
raise ADEPTError('libcrypto not found')
libcrypto = CDLL(libcrypto)
AES_MAXNR = 14
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
def F(restype, name, argtypes):
func = getattr(libcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
[c_char_p, c_int, AES_KEY_p])
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
c_int])
class AES(object):
def __init__(self, userkey):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise ADEPTError('AES improper key used')
key = self._key = AES_KEY()
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
if rv < 0:
raise ADEPTError('Failed to initialize AES key')
def decrypt(self, data):
out = create_string_buffer(len(data))
iv = ("\x00" * self._blocksize)
rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
if rv == 0:
raise ADEPTError('AES decryption failed')
return out.raw
return AES
def _load_crypto_pycrypto():
from Crypto.Cipher import AES as _AES
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)
return AES
def _load_crypto():
AES = None
for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto):
try:
AES = loader()
break
except (ImportError, ADEPTError):
pass
return AES
AES = _load_crypto()
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
def GetSystemDirectory():
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(
path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
def VirtualAlloc():
_VirtualAlloc = kernel32.VirtualAlloc
_VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
_VirtualAlloc.restype = LPVOID
def VirtualAlloc(addr, size, alloctype=(MEM_COMMIT | MEM_RESERVE),
protect=PAGE_EXECUTE_READWRITE):
return _VirtualAlloc(addr, size, alloctype, protect)
return VirtualAlloc
VirtualAlloc = VirtualAlloc()
MEM_RELEASE = 0x8000
def VirtualFree():
_VirtualFree = kernel32.VirtualFree
_VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
_VirtualFree.restype = BOOL
def VirtualFree(addr, size=0, freetype=MEM_RELEASE):
return _VirtualFree(addr, size, freetype)
return VirtualFree
VirtualFree = VirtualFree()
class NativeFunction(object):
def __init__(self, restype, argtypes, insns):
self._buf = buf = VirtualAlloc(None, len(insns))
memmove(buf, insns, len(insns))
ftype = CFUNCTYPE(restype, *argtypes)
self._native = ftype(buf)
def __call__(self, *args):
return self._native(*args)
def __del__(self):
if self._buf is not None:
VirtualFree(self._buf)
self._buf = None
if struct.calcsize("P") == 4:
CPUID0_INSNS = (
"\x53" # push %ebx
"\x31\xc0" # xor %eax,%eax
"\x0f\xa2" # cpuid
"\x8b\x44\x24\x08" # mov 0x8(%esp),%eax
"\x89\x18" # mov %ebx,0x0(%eax)
"\x89\x50\x04" # mov %edx,0x4(%eax)
"\x89\x48\x08" # mov %ecx,0x8(%eax)
"\x5b" # pop %ebx
"\xc3" # ret
)
CPUID1_INSNS = (
"\x53" # push %ebx
"\x31\xc0" # xor %eax,%eax
"\x40" # inc %eax
"\x0f\xa2" # cpuid
"\x5b" # pop %ebx
"\xc3" # ret
)
else:
CPUID0_INSNS = (
"\x49\x89\xd8" # mov %rbx,%r8
"\x49\x89\xc9" # mov %rcx,%r9
"\x48\x31\xc0" # xor %rax,%rax
"\x0f\xa2" # cpuid
"\x4c\x89\xc8" # mov %r9,%rax
"\x89\x18" # mov %ebx,0x0(%rax)
"\x89\x50\x04" # mov %edx,0x4(%rax)
"\x89\x48\x08" # mov %ecx,0x8(%rax)
"\x4c\x89\xc3" # mov %r8,%rbx
"\xc3" # retq
)
CPUID1_INSNS = (
"\x53" # push %rbx
"\x48\x31\xc0" # xor %rax,%rax
"\x48\xff\xc0" # inc %rax
"\x0f\xa2" # cpuid
"\x5b" # pop %rbx
"\xc3" # retq
)
def cpuid0():
_cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
buf = create_string_buffer(12)
def cpuid0():
_cpuid0(buf)
return buf.raw
return cpuid0
cpuid0 = cpuid0()
cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
raise ADEPTError("Failed to decrypt user key key (sic)")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def retrieve_keys():
if AES is None:
raise ADEPTError("PyCrypto or OpenSSL must be installed")
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
signature = struct.pack('>I', cpuid1())[1:]
user = GetUserName()
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
cuser = winreg.HKEY_CURRENT_USER
try:
regkey = winreg.OpenKey(cuser, DEVICE_KEY_PATH)
device = winreg.QueryValueEx(regkey, 'key')[0]
except WindowsError:
raise ADEPTError("Adobe Digital Editions not activated")
keykey = CryptUnprotectData(device, entropy)
userkey = None
keys = []
try:
plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
except WindowsError:
raise ADEPTError("Could not locate ADE activation")
for i in xrange(0, 16):
try:
plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
except WindowsError:
break
ktype = winreg.QueryValueEx(plkparent, None)[0]
if ktype != 'credentials':
continue
for j in xrange(0, 16):
try:
plkkey = winreg.OpenKey(plkparent, "%04d" % (j,))
except WindowsError:
break
ktype = winreg.QueryValueEx(plkkey, None)[0]
if ktype != 'privateLicenseKey':
continue
userkey = winreg.QueryValueEx(plkkey, 'value')[0]
userkey = userkey.decode('base64')
aes = AES(keykey)
userkey = aes.decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
keys.append(userkey)
if len(keys) == 0:
raise ADEPTError('Could not locate privateLicenseKey')
return keys
elif isosx:
import xml.etree.ElementTree as etree
import subprocess
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
def findActivationDat():
home = os.getenv('HOME')
cmdline = 'find "' + home + '/Library/Application Support/Adobe/Digital Editions" -name "activation.dat"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p2.communicate()
reslst = out1.split('\n')
cnt = len(reslst)
for j in xrange(cnt):
resline = reslst[j]
pp = resline.find('activation.dat')
if pp >= 0:
ActDatPath = resline
break
if os.path.exists(ActDatPath):
return ActDatPath
return None
def retrieve_keys():
actpath = findActivationDat()
if actpath is None:
raise ADEPTError("Could not locate ADE activation")
tree = etree.parse(actpath)
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = '//%s/%s' % (adept('credentials'), adept('privateLicenseKey'))
userkey = tree.findtext(expr)
userkey = userkey.decode('base64')
userkey = userkey[26:]
return [userkey]
else:
def retrieve_keys(keypath):
raise ADEPTError("This script only supports Windows and Mac OS X.")
return []
def retrieve_key(keypath):
keys = retrieve_keys()
with open(keypath, 'wb') as f:
f.write(keys[0])
return True
def extractKeyfile(keypath):
try:
success = retrieve_key(keypath)
except ADEPTError, e:
print u"Key generation Error: {0}".format(e.args[0])
return 1
except Exception, e:
print "General Error: {0}".format(e.args[0])
return 1
if not success:
return 1
return 0
def cli_main(argv=unicode_argv()):
keypath = argv[1]
return extractKeyfile(keypath)
def gui_main(argv=unicode_argv()):
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
Tkinter.Frame.__init__(self, root, border=5)
label = Tkinter.Label(self, text=u"Unexpected error:",
anchor=Tkconstants.W, justify=Tkconstants.LEFT)
label.pack(fill=Tkconstants.X, expand=0)
self.text = Tkinter.Text(self)
self.text.pack(fill=Tkconstants.BOTH, expand=1)
self.text.insert(Tkconstants.END, text)
root = Tkinter.Tk()
root.withdraw()
keypath, progname = os.path.split(argv[0])
keypath = os.path.join(keypath, u"adeptkey.der")
success = False
try:
success = retrieve_key(keypath)
except ADEPTError, e:
tkMessageBox.showerror(u"ADEPT Key", "Error: {0}".format(e.args[0]))
except Exception:
root.wm_state('normal')
root.title('ADEPT Key')
text = traceback.format_exc()
ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
root.mainloop()
if not success:
return 1
tkMessageBox.showinfo(
u"ADEPT Key", u"Key successfully retrieved to {0}".format(keypath))
return 0
if __name__ == '__main__':
if len(sys.argv) > 1:
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
sys.exit(cli_main())
sys.exit(gui_main())

@ -50,13 +50,14 @@ from __future__ import with_statement
# 7.11 - More tweaks to fix minor problems.
# 7.12 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 7.13 - Fixed erroneous mentions of ineptepub
# 7.14 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypts Adobe ADEPT-encrypted PDF files.
"""
__license__ = 'GPL v3'
__version__ = "7.13"
__version__ = "7.14"
import sys
import os
@ -2185,7 +2186,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)

@ -53,8 +53,9 @@ from __future__ import with_statement
# 4.9 - Missed some invalid characters in cleanup_name
# 5.0 - Extraction of info from Kindle for PC/Mac moved into kindlekey.py
# - tweaked GetDecryptedBook interface to leave passed parameters unchanged
# 5.1 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '5.0'
__version__ = '5.1'
import sys, os, re
@ -276,7 +277,8 @@ def usage(progname):
#
# Main
#
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"K4MobiDeDrm v{0}.\nCopyright © 2008-2013 The Dark Reverser et al.".format(__version__)

@ -1,747 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# standlone set of Mac OSX specific routines needed for KindleBooks
from __future__ import with_statement
import sys
import os
import os.path
import re
import copy
import subprocess
from struct import pack, unpack, unpack_from
class DrmException(Exception):
pass
# interface to needed routines in openssl's libcrypto
def _load_crypto_libcrypto():
from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
Structure, c_ulong, create_string_buffer, addressof, string_at, cast
from ctypes.util import find_library
libcrypto = find_library('crypto')
if libcrypto is None:
raise DrmException(u"libcrypto not found")
libcrypto = CDLL(libcrypto)
# From OpenSSL's crypto aes header
#
# AES_ENCRYPT 1
# AES_DECRYPT 0
# AES_MAXNR 14 (in bytes)
# AES_BLOCK_SIZE 16 (in bytes)
#
# struct aes_key_st {
# unsigned long rd_key[4 *(AES_MAXNR + 1)];
# int rounds;
# };
# typedef struct aes_key_st AES_KEY;
#
# int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
#
# note: the ivec string, and output buffer are both mutable
# void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
# const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc);
AES_MAXNR = 14
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
def F(restype, name, argtypes):
func = getattr(libcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int])
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
# From OpenSSL's Crypto evp/p5_crpt2.c
#
# int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
# const unsigned char *salt, int saltlen, int iter,
# int keylen, unsigned char *out);
PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1',
[c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
class LibCrypto(object):
def __init__(self):
self._blocksize = 0
self._keyctx = None
self._iv = 0
def set_decrypt_key(self, userkey, iv):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise DrmException(u"AES improper key used")
return
keyctx = self._keyctx = AES_KEY()
self._iv = iv
self._userkey = userkey
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
if rv < 0:
raise DrmException(u"Failed to initialize AES key")
def decrypt(self, data):
out = create_string_buffer(len(data))
mutable_iv = create_string_buffer(self._iv, len(self._iv))
keyctx = self._keyctx
rv = AES_cbc_encrypt(data, out, len(data), keyctx, mutable_iv, 0)
if rv == 0:
raise DrmException(u"AES decryption failed")
return out.raw
def keyivgen(self, passwd, salt, iter, keylen):
saltlen = len(salt)
passlen = len(passwd)
out = create_string_buffer(keylen)
rv = PKCS5_PBKDF2_HMAC_SHA1(passwd, passlen, salt, saltlen, iter, keylen, out)
return out.raw
return LibCrypto
def _load_crypto():
LibCrypto = None
try:
LibCrypto = _load_crypto_libcrypto()
except (ImportError, DrmException):
pass
return LibCrypto
LibCrypto = _load_crypto()
#
# Utility Routines
#
# crypto digestroutines
import hashlib
def MD5(message):
ctx = hashlib.md5()
ctx.update(message)
return ctx.digest()
def SHA1(message):
ctx = hashlib.sha1()
ctx.update(message)
return ctx.digest()
def SHA256(message):
ctx = hashlib.sha256()
ctx.update(message)
return ctx.digest()
# Various character maps used to decrypt books. Probably supposed to act as obfuscation
charMap1 = 'n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M'
charMap2 = 'ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM'
# For kinf approach of K4Mac 1.6.X or later
# On K4PC charMap5 = 'AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE'
# For Mac they seem to re-use charMap2 here
charMap5 = charMap2
# new in K4M 1.9.X
testMap8 = 'YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD'
def encode(data, map):
result = ''
for char in data:
value = ord(char)
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += map[Q]
result += map[R]
return result
# Hash the bytes in data and then encode the digest with the characters in map
def encodeHash(data,map):
return encode(MD5(data),map)
# Decode the string in data with the characters in map. Returns the decoded bytes
def decode(data,map):
result = ''
for i in range (0,len(data)-1,2):
high = map.find(data[i])
low = map.find(data[i+1])
if (high == -1) or (low == -1) :
break
value = (((high * len(map)) ^ 0x80) & 0xFF) + low
result += pack('B',value)
return result
# For K4M 1.6.X and later
# generate table of prime number less than or equal to int n
def primes(n):
if n==2: return [2]
elif n<2: return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
# uses a sub process to get the Hard Drive Serial Number using ioreg
# returns with the serial number of drive whose BSD Name is 'disk0'
def GetVolumeSerialNumber():
sernum = os.getenv('MYSERIALNUMBER')
if sernum != None:
return sernum
cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate()
reslst = out1.split('\n')
cnt = len(reslst)
bsdname = None
sernum = None
foundIt = False
for j in xrange(cnt):
resline = reslst[j]
pp = resline.find('\"Serial Number\" = \"')
if pp >= 0:
sernum = resline[pp+19:-1]
sernum = sernum.strip()
bb = resline.find('\"BSD Name\" = \"')
if bb >= 0:
bsdname = resline[bb+14:-1]
bsdname = bsdname.strip()
if (bsdname == 'disk0') and (sernum != None):
foundIt = True
break
if not foundIt:
sernum = ''
return sernum
def GetUserHomeAppSupKindleDirParitionName():
home = os.getenv('HOME')
dpath = home + '/Library'
cmdline = '/sbin/mount'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate()
reslst = out1.split('\n')
cnt = len(reslst)
disk = ''
foundIt = False
for j in xrange(cnt):
resline = reslst[j]
if resline.startswith('/dev'):
(devpart, mpath) = resline.split(' on ')
dpart = devpart[5:]
pp = mpath.find('(')
if pp >= 0:
mpath = mpath[:pp-1]
if dpath.startswith(mpath):
disk = dpart
return disk
# uses a sub process to get the UUID of the specified disk partition using ioreg
def GetDiskPartitionUUID(diskpart):
uuidnum = os.getenv('MYUUIDNUMBER')
if uuidnum != None:
return uuidnum
cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate()
reslst = out1.split('\n')
cnt = len(reslst)
bsdname = None
uuidnum = None
foundIt = False
nest = 0
uuidnest = -1
partnest = -2
for j in xrange(cnt):
resline = reslst[j]
if resline.find('{') >= 0:
nest += 1
if resline.find('}') >= 0:
nest -= 1
pp = resline.find('\"UUID\" = \"')
if pp >= 0:
uuidnum = resline[pp+10:-1]
uuidnum = uuidnum.strip()
uuidnest = nest
if partnest == uuidnest and uuidnest > 0:
foundIt = True
break
bb = resline.find('\"BSD Name\" = \"')
if bb >= 0:
bsdname = resline[bb+14:-1]
bsdname = bsdname.strip()
if (bsdname == diskpart):
partnest = nest
else :
partnest = -2
if partnest == uuidnest and partnest > 0:
foundIt = True
break
if nest == 0:
partnest = -2
uuidnest = -1
uuidnum = None
bsdname = None
if not foundIt:
uuidnum = ''
return uuidnum
def GetMACAddressMunged():
macnum = os.getenv('MYMACNUM')
if macnum != None:
return macnum
cmdline = '/sbin/ifconfig en0'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p.communicate()
reslst = out1.split('\n')
cnt = len(reslst)
macnum = None
foundIt = False
for j in xrange(cnt):
resline = reslst[j]
pp = resline.find('ether ')
if pp >= 0:
macnum = resline[pp+6:-1]
macnum = macnum.strip()
# print 'original mac', macnum
# now munge it up the way Kindle app does
# by xoring it with 0xa5 and swapping elements 3 and 4
maclst = macnum.split(':')
n = len(maclst)
if n != 6:
fountIt = False
break
for i in range(6):
maclst[i] = int('0x' + maclst[i], 0)
mlst = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
mlst[5] = maclst[5] ^ 0xa5
mlst[4] = maclst[3] ^ 0xa5
mlst[3] = maclst[4] ^ 0xa5
mlst[2] = maclst[2] ^ 0xa5
mlst[1] = maclst[1] ^ 0xa5
mlst[0] = maclst[0] ^ 0xa5
macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
foundIt = True
break
if not foundIt:
macnum = ''
return macnum
# uses unix env to get username instead of using sysctlbyname
def GetUserName():
username = os.getenv('USER')
return username
def isNewInstall():
home = os.getenv('HOME')
# soccer game fan anyone
dpath = home + '/Library/Application Support/Kindle/storage/.pes2011'
# print dpath, os.path.exists(dpath)
if os.path.exists(dpath):
return True
dpath = home + '/Library/Containers/com.amazon.Kindle/Data/Library/Application Support/Kindle/storage/.pes2011'
# print dpath, os.path.exists(dpath)
if os.path.exists(dpath):
return True
return False
class Memoize:
"""Memoize(fn) - an instance which acts like fn but memoizes its arguments
Will only work on functions with non-mutable arguments
"""
def __init__(self, fn):
self.fn = fn
self.memo = {}
def __call__(self, *args):
if not self.memo.has_key(args):
self.memo[args] = self.fn(*args)
return self.memo[args]
@Memoize
def GetIDString():
# K4Mac now has an extensive set of ids strings it uses
# in encoding pids and in creating unique passwords
# for use in its own version of CryptUnprotectDataV2
# BUT Amazon has now become nasty enough to detect when its app
# is being run under a debugger and actually changes code paths
# including which one of these strings is chosen, all to try
# to prevent reverse engineering
# Sad really ... they will only hurt their own sales ...
# true book lovers really want to keep their books forever
# and move them to their devices and DRM prevents that so they
# will just buy from someplace else that they can remove
# the DRM from
# Amazon should know by now that true book lover's are not like
# penniless kids that pirate music, we do not pirate books
if isNewInstall():
mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac
sernum = GetVolumeSerialNumber()
if len(sernum) > 7:
print('Using Volume Serial Number for ID: '+sernum)
return sernum
diskpart = GetUserHomeAppSupKindleDirParitionName()
uuidnum = GetDiskPartitionUUID(diskpart)
if len(uuidnum) > 7:
print('Using Disk Partition UUID for ID: '+uuidnum)
return uuidnum
mungedmac = GetMACAddressMunged()
if len(mungedmac) > 7:
print('Using Munged MAC Address for ID: '+mungedmac)
return mungedmac
print('Using Fixed constant 9999999999 for ID.')
return '9999999999'
# implements an Pseudo Mac Version of Windows built-in Crypto routine
# used by Kindle for Mac versions < 1.6.0
class CryptUnprotectData(object):
def __init__(self):
sernum = GetVolumeSerialNumber()
if sernum == '':
sernum = '9999999999'
sp = sernum + '!@#' + GetUserName()
passwdData = encode(SHA256(sp),charMap1)
salt = '16743'
self.crp = LibCrypto()
iter = 0x3e8
keylen = 0x80
key_iv = self.crp.keyivgen(passwdData, salt, iter, keylen)
self.key = key_iv[0:32]
self.iv = key_iv[32:48]
self.crp.set_decrypt_key(self.key, self.iv)
def decrypt(self, encryptedData):
cleartext = self.crp.decrypt(encryptedData)
cleartext = decode(cleartext,charMap1)
return cleartext
# implements an Pseudo Mac Version of Windows built-in Crypto routine
# used for Kindle for Mac Versions >= 1.6.0
class CryptUnprotectDataV2(object):
def __init__(self):
sp = GetUserName() + ':&%:' + GetIDString()
passwdData = encode(SHA256(sp),charMap5)
# salt generation as per the code
salt = 0x0512981d * 2 * 1 * 1
salt = str(salt) + GetUserName()
salt = encode(salt,charMap5)
self.crp = LibCrypto()
iter = 0x800
keylen = 0x400
key_iv = self.crp.keyivgen(passwdData, salt, iter, keylen)
self.key = key_iv[0:32]
self.iv = key_iv[32:48]
self.crp.set_decrypt_key(self.key, self.iv)
def decrypt(self, encryptedData):
cleartext = self.crp.decrypt(encryptedData)
cleartext = decode(cleartext, charMap5)
return cleartext
# unprotect the new header blob in .kinf2011
# used in Kindle for Mac Version >= 1.9.0
def UnprotectHeaderData(encryptedData):
passwdData = 'header_key_data'
salt = 'HEADER.2011'
iter = 0x80
keylen = 0x100
crp = LibCrypto()
key_iv = crp.keyivgen(passwdData, salt, iter, keylen)
key = key_iv[0:32]
iv = key_iv[32:48]
crp.set_decrypt_key(key,iv)
cleartext = crp.decrypt(encryptedData)
return cleartext
# implements an Pseudo Mac Version of Windows built-in Crypto routine
# used for Kindle for Mac Versions >= 1.9.0
class CryptUnprotectDataV3(object):
def __init__(self, entropy):
sp = GetUserName() + '+@#$%+' + GetIDString()
passwdData = encode(SHA256(sp),charMap2)
salt = entropy
self.crp = LibCrypto()
iter = 0x800
keylen = 0x400
key_iv = self.crp.keyivgen(passwdData, salt, iter, keylen)
self.key = key_iv[0:32]
self.iv = key_iv[32:48]
self.crp.set_decrypt_key(self.key, self.iv)
def decrypt(self, encryptedData):
cleartext = self.crp.decrypt(encryptedData)
cleartext = decode(cleartext, charMap2)
return cleartext
# Locate the .kindle-info files
def getKindleInfoFiles():
# file searches can take a long time on some systems, so just look in known specific places.
kInfoFiles=[]
found = False
home = os.getenv('HOME')
# check for .kinf2011 file in new location (App Store Kindle for Mac)
testpath = home + '/Library/Containers/com.amazon.Kindle/Data/Library/Application Support/Kindle/storage/.kinf2011'
if os.path.isfile(testpath):
kInfoFiles.append(testpath)
print('Found k4Mac kinf2011 file: ' + testpath)
found = True
# check for .kinf2011 files
testpath = home + '/Library/Application Support/Kindle/storage/.kinf2011'
if os.path.isfile(testpath):
kInfoFiles.append(testpath)
print('Found k4Mac kinf2011 file: ' + testpath)
found = True
# check for .rainier-2.1.1-kinf files
testpath = home + '/Library/Application Support/Kindle/storage/.rainier-2.1.1-kinf'
if os.path.isfile(testpath):
kInfoFiles.append(testpath)
print('Found k4Mac rainier file: ' + testpath)
found = True
# check for .rainier-2.1.1-kinf files
testpath = home + '/Library/Application Support/Kindle/storage/.kindle-info'
if os.path.isfile(testpath):
kInfoFiles.append(testpath)
print('Found k4Mac kindle-info file: ' + testpath)
found = True
if not found:
print('No k4Mac kindle-info/rainier/kinf2011 files have been found.')
return kInfoFiles
# determine type of kindle info provided and return a
# database of keynames and values
def getDBfromFile(kInfoFile):
names = ['kindle.account.tokens','kindle.cookie.item','eulaVersionAccepted','login_date','kindle.token.item','login','kindle.key.item','kindle.name.info','kindle.device.info', 'MazamaRandomNumber', 'max_date', 'SIGVERIF']
DB = {}
cnt = 0
infoReader = open(kInfoFile, 'r')
hdr = infoReader.read(1)
data = infoReader.read()
if data.find('[') != -1 :
# older style kindle-info file
cud = CryptUnprotectData()
items = data.split('[')
for item in items:
if item != '':
keyhash, rawdata = item.split(':')
keyname = 'unknown'
for name in names:
if encodeHash(name,charMap2) == keyhash:
keyname = name
break
if keyname == 'unknown':
keyname = keyhash
encryptedValue = decode(rawdata,charMap2)
cleartext = cud.decrypt(encryptedValue)
DB[keyname] = cleartext
cnt = cnt + 1
if cnt == 0:
DB = None
return DB
if hdr == '/':
# else newer style .kinf file used by K4Mac >= 1.6.0
# the .kinf file uses '/' to separate it into records
# so remove the trailing '/' to make it easy to use split
data = data[:-1]
items = data.split('/')
cud = CryptUnprotectDataV2()
# loop through the item records until all are processed
while len(items) > 0:
# get the first item record
item = items.pop(0)
# the first 32 chars of the first record of a group
# is the MD5 hash of the key name encoded by charMap5
keyhash = item[0:32]
keyname = 'unknown'
# the raw keyhash string is also used to create entropy for the actual
# CryptProtectData Blob that represents that keys contents
# 'entropy' not used for K4Mac only K4PC
# entropy = SHA1(keyhash)
# the remainder of the first record when decoded with charMap5
# has the ':' split char followed by the string representation
# of the number of records that follow
# and make up the contents
srcnt = decode(item[34:],charMap5)
rcnt = int(srcnt)
# read and store in rcnt records of data
# that make up the contents value
edlst = []
for i in xrange(rcnt):
item = items.pop(0)
edlst.append(item)
keyname = 'unknown'
for name in names:
if encodeHash(name,charMap5) == keyhash:
keyname = name
break
if keyname == 'unknown':
keyname = keyhash
# the charMap5 encoded contents data has had a length
# of chars (always odd) cut off of the front and moved
# to the end to prevent decoding using charMap5 from
# working properly, and thereby preventing the ensuing
# CryptUnprotectData call from succeeding.
# The offset into the charMap5 encoded contents seems to be:
# len(contents) - largest prime number less than or equal to int(len(content)/3)
# (in other words split 'about' 2/3rds of the way through)
# move first offsets chars to end to align for decode by charMap5
encdata = ''.join(edlst)
contlen = len(encdata)
# now properly split and recombine
# by moving noffset chars from the start of the
# string to the end of the string
noffset = contlen - primes(int(contlen/3))[-1]
pfx = encdata[0:noffset]
encdata = encdata[noffset:]
encdata = encdata + pfx
# decode using charMap5 to get the CryptProtect Data
encryptedValue = decode(encdata,charMap5)
cleartext = cud.decrypt(encryptedValue)
DB[keyname] = cleartext
cnt = cnt + 1
if cnt == 0:
DB = None
return DB
# the latest .kinf2011 version for K4M 1.9.1
# put back the hdr char, it is needed
data = hdr + data
data = data[:-1]
items = data.split('/')
# the headerblob is the encrypted information needed to build the entropy string
headerblob = items.pop(0)
encryptedValue = decode(headerblob, charMap1)
cleartext = UnprotectHeaderData(encryptedValue)
# now extract the pieces in the same way
# this version is different from K4PC it scales the build number by multipying by 735
pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
for m in re.finditer(pattern, cleartext):
entropy = str(int(m.group(2)) * 0x2df) + m.group(4)
cud = CryptUnprotectDataV3(entropy)
# loop through the item records until all are processed
while len(items) > 0:
# get the first item record
item = items.pop(0)
# the first 32 chars of the first record of a group
# is the MD5 hash of the key name encoded by charMap5
keyhash = item[0:32]
keyname = 'unknown'
# unlike K4PC the keyhash is not used in generating entropy
# entropy = SHA1(keyhash) + added_entropy
# entropy = added_entropy
# the remainder of the first record when decoded with charMap5
# has the ':' split char followed by the string representation
# of the number of records that follow
# and make up the contents
srcnt = decode(item[34:],charMap5)
rcnt = int(srcnt)
# read and store in rcnt records of data
# that make up the contents value
edlst = []
for i in xrange(rcnt):
item = items.pop(0)
edlst.append(item)
keyname = 'unknown'
for name in names:
if encodeHash(name,testMap8) == keyhash:
keyname = name
break
if keyname == 'unknown':
keyname = keyhash
# the testMap8 encoded contents data has had a length
# of chars (always odd) cut off of the front and moved
# to the end to prevent decoding using testMap8 from
# working properly, and thereby preventing the ensuing
# CryptUnprotectData call from succeeding.
# The offset into the testMap8 encoded contents seems to be:
# len(contents) - largest prime number less than or equal to int(len(content)/3)
# (in other words split 'about' 2/3rds of the way through)
# move first offsets chars to end to align for decode by testMap8
encdata = ''.join(edlst)
contlen = len(encdata)
# now properly split and recombine
# by moving noffset chars from the start of the
# string to the end of the string
noffset = contlen - primes(int(contlen/3))[-1]
pfx = encdata[0:noffset]
encdata = encdata[noffset:]
encdata = encdata + pfx
# decode using testMap8 to get the CryptProtect Data
encryptedValue = decode(encdata,testMap8)
cleartext = cud.decrypt(encryptedValue)
# print keyname
# print cleartext
DB[keyname] = cleartext
cnt = cnt + 1
if cnt == 0:
DB = None
return DB

@ -1,457 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# K4PC Windows specific routines
from __future__ import with_statement
import sys, os, re
from struct import pack, unpack, unpack_from
from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
string_at, Structure, c_void_p, cast
import _winreg as winreg
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
import traceback
# crypto digestroutines
import hashlib
def MD5(message):
ctx = hashlib.md5()
ctx.update(message)
return ctx.digest()
def SHA1(message):
ctx = hashlib.sha1()
ctx.update(message)
return ctx.digest()
def SHA256(message):
ctx = hashlib.sha256()
ctx.update(message)
return ctx.digest()
# For K4PC 1.9.X
# use routines in alfcrypto:
# AES_cbc_encrypt
# AES_set_decrypt_key
# PKCS5_PBKDF2_HMAC_SHA1
from alfcrypto import AES_CBC, KeyIVGen
def UnprotectHeaderData(encryptedData):
passwdData = 'header_key_data'
salt = 'HEADER.2011'
iter = 0x80
keylen = 0x100
key_iv = KeyIVGen().pbkdf2(passwdData, salt, iter, keylen)
key = key_iv[0:32]
iv = key_iv[32:48]
aes=AES_CBC()
aes.set_decrypt_key(key, iv)
cleartext = aes.decrypt(encryptedData)
return cleartext
# simple primes table (<= n) calculator
def primes(n):
if n==2: return [2]
elif n<2: return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
# Various character maps used to decrypt kindle info values.
# Probably supposed to act as obfuscation
charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
charMap5 = "AzB0bYyCeVvaZ3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_c1XxDdW2wE"
# New maps in K4PC 1.9.0
testMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
testMap6 = "9YzAb0Cd1Ef2n5Pr6St7Uvh3Jk4M8WxG"
testMap8 = "YvaZ3FfUm9Nn_c1XuG4yCAzB0beVg-TtHh5SsIiR6rJjQdW2wEq7KkPpL8lOoMxD"
class DrmException(Exception):
pass
# Encode the bytes in data with the characters in map
def encode(data, map):
result = ""
for char in data:
value = ord(char)
Q = (value ^ 0x80) // len(map)
R = value % len(map)
result += map[Q]
result += map[R]
return result
# Hash the bytes in data and then encode the digest with the characters in map
def encodeHash(data,map):
return encode(MD5(data),map)
# Decode the string in data with the characters in map. Returns the decoded bytes
def decode(data,map):
result = ""
for i in range (0,len(data)-1,2):
high = map.find(data[i])
low = map.find(data[i+1])
if (high == -1) or (low == -1) :
break
value = (((high * len(map)) ^ 0x80) & 0xFF) + low
result += pack("B",value)
return result
# interface with Windows OS Routines
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def GetSystemDirectory():
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path = GetSystemDirectory().split('\\')[0] + '\\'):
vsn = c_uint(0)
GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
return str(vsn.value)
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetIDString():
vsn = GetVolumeSerialNumber()
print('Using Volume Serial Number for ID: '+vsn)
return vsn
def getLastError():
GetLastError = kernel32.GetLastError
GetLastError.argtypes = None
GetLastError.restype = c_uint
def getLastError():
return GetLastError()
return getLastError
getLastError = getLastError()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(2)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
errcd = getLastError()
if errcd == 234:
# bad wine implementation up through wine 1.3.21
return "AlternateUserName"
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy, flags):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, flags, byref(outdata)):
# raise DrmException("Failed to Unprotect Data")
return 'failed'
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
# 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 python interface to the 32 vs 64 bit registry is broken
path = ""
if 'LOCALAPPDATA' in os.environ.keys():
path = os.environ['LOCALAPPDATA']
else:
# User Shell Folders show take precedent over Shell Folders if present
try:
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
except WindowsError:
pass
if not os.path.isdir(path):
path = ""
try:
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
except WindowsError:
pass
if not os.path.isdir(path):
path = ""
found = False
if path == "":
print ('Could not find the folder in which to look for kinfoFiles.')
else:
print('searching for kinfoFiles in ' + path)
# first look for older kindle-info files
kinfopath = path +'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info'
if os.path.isfile(kinfopath):
found = True
print('Found K4PC kindle.info file: ' + kinfopath)
kInfoFiles.append(kinfopath)
# now look for newer (K4PC 1.5.0 and later rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle For PC\\storage\\rainier.2.1.1.kinf'
if os.path.isfile(kinfopath):
found = True
print('Found K4PC 1.5.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.6.0 and later) rainier.2.1.1.kinf file
kinfopath = path +'\\Amazon\\Kindle\\storage\\rainier.2.1.1.kinf'
if os.path.isfile(kinfopath):
found = True
print('Found K4PC 1.6.X kinf file: ' + kinfopath)
kInfoFiles.append(kinfopath)
# now look for even newer (K4PC 1.9.0 and later) .kinf2011 file
kinfopath = path +'\\Amazon\\Kindle\\storage\\.kinf2011'
if os.path.isfile(kinfopath):
found = True
print('Found K4PC kinf2011 file: ' + kinfopath)
kInfoFiles.append(kinfopath)
if not found:
print('No K4PC kindle.info/kinf/kinf2011 files have been found.')
return kInfoFiles
# determine type of kindle info provided and return a
# database of keynames and values
def getDBfromFile(kInfoFile):
names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber", "max_date", "SIGVERIF"]
DB = {}
cnt = 0
infoReader = open(kInfoFile, 'r')
hdr = infoReader.read(1)
data = infoReader.read()
if data.find('{') != -1 :
# older style kindle-info file
items = data.split('{')
for item in items:
if item != '':
keyhash, rawdata = item.split(':')
keyname = "unknown"
for name in names:
if encodeHash(name,charMap2) == keyhash:
keyname = name
break
if keyname == "unknown":
keyname = keyhash
encryptedValue = decode(rawdata,charMap2)
DB[keyname] = CryptUnprotectData(encryptedValue, "", 0)
cnt = cnt + 1
if cnt == 0:
DB = None
return DB
if hdr == '/':
# else rainier-2-1-1 .kinf file
# the .kinf file uses "/" to separate it into records
# so remove the trailing "/" to make it easy to use split
data = data[:-1]
items = data.split('/')
# loop through the item records until all are processed
while len(items) > 0:
# get the first item record
item = items.pop(0)
# the first 32 chars of the first record of a group
# is the MD5 hash of the key name encoded by charMap5
keyhash = item[0:32]
# the raw keyhash string is used to create entropy for the actual
# CryptProtectData Blob that represents that keys contents
entropy = SHA1(keyhash)
# the remainder of the first record when decoded with charMap5
# has the ':' split char followed by the string representation
# of the number of records that follow
# and make up the contents
srcnt = decode(item[34:],charMap5)
rcnt = int(srcnt)
# read and store in rcnt records of data
# that make up the contents value
edlst = []
for i in xrange(rcnt):
item = items.pop(0)
edlst.append(item)
keyname = "unknown"
for name in names:
if encodeHash(name,charMap5) == keyhash:
keyname = name
break
if keyname == "unknown":
keyname = keyhash
# the charMap5 encoded contents data has had a length
# of chars (always odd) cut off of the front and moved
# to the end to prevent decoding using charMap5 from
# working properly, and thereby preventing the ensuing
# CryptUnprotectData call from succeeding.
# The offset into the charMap5 encoded contents seems to be:
# len(contents)-largest prime number <= int(len(content)/3)
# (in other words split "about" 2/3rds of the way through)
# move first offsets chars to end to align for decode by charMap5
encdata = "".join(edlst)
contlen = len(encdata)
noffset = contlen - primes(int(contlen/3))[-1]
# now properly split and recombine
# by moving noffset chars from the start of the
# string to the end of the string
pfx = encdata[0:noffset]
encdata = encdata[noffset:]
encdata = encdata + pfx
# decode using Map5 to get the CryptProtect Data
encryptedValue = decode(encdata,charMap5)
DB[keyname] = CryptUnprotectData(encryptedValue, entropy, 1)
cnt = cnt + 1
if cnt == 0:
DB = None
return DB
# else newest .kinf2011 style .kinf file
# the .kinf file uses "/" to separate it into records
# so remove the trailing "/" to make it easy to use split
# need to put back the first char read because it it part
# of the added entropy blob
data = hdr + data[:-1]
items = data.split('/')
# starts with and encoded and encrypted header blob
headerblob = items.pop(0)
encryptedValue = decode(headerblob, testMap1)
cleartext = UnprotectHeaderData(encryptedValue)
# now extract the pieces that form the added entropy
pattern = re.compile(r'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE)
for m in re.finditer(pattern, cleartext):
added_entropy = m.group(2) + m.group(4)
# loop through the item records until all are processed
while len(items) > 0:
# get the first item record
item = items.pop(0)
# the first 32 chars of the first record of a group
# is the MD5 hash of the key name encoded by charMap5
keyhash = item[0:32]
# the sha1 of raw keyhash string is used to create entropy along
# with the added entropy provided above from the headerblob
entropy = SHA1(keyhash) + added_entropy
# the remainder of the first record when decoded with charMap5
# has the ':' split char followed by the string representation
# of the number of records that follow
# and make up the contents
srcnt = decode(item[34:],charMap5)
rcnt = int(srcnt)
# read and store in rcnt records of data
# that make up the contents value
edlst = []
for i in xrange(rcnt):
item = items.pop(0)
edlst.append(item)
# key names now use the new testMap8 encoding
keyname = "unknown"
for name in names:
if encodeHash(name,testMap8) == keyhash:
keyname = name
break
# the testMap8 encoded contents data has had a length
# of chars (always odd) cut off of the front and moved
# to the end to prevent decoding using testMap8 from
# working properly, and thereby preventing the ensuing
# CryptUnprotectData call from succeeding.
# The offset into the testMap8 encoded contents seems to be:
# len(contents)-largest prime number <= int(len(content)/3)
# (in other words split "about" 2/3rds of the way through)
# move first offsets chars to end to align for decode by testMap8
# by moving noffset chars from the start of the
# string to the end of the string
encdata = "".join(edlst)
contlen = len(encdata)
noffset = contlen - primes(int(contlen/3))[-1]
pfx = encdata[0:noffset]
encdata = encdata[noffset:]
encdata = encdata + pfx
# decode using new testMap8 to get the original CryptProtect Data
encryptedValue = decode(encdata,testMap8)
cleartext = CryptUnprotectData(encryptedValue, entropy, 1)
DB[keyname] = cleartext
cnt = cnt + 1
if cnt == 0:
DB = None
return DB

@ -15,13 +15,16 @@ from __future__ import with_statement
# 1.3 - Added getkey interface for Windows DeDRM application
# Simplified some of the Kindle for Mac code.
# 1.4 - Remove dependency on alfcrypto
# 1.5 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 1.6 - Fixed a problem getting the disk serial numbers
"""
Retrieve Kindle for PC/Mac user key.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.6'
import sys, os, re
from struct import pack, unpack, unpack_from
@ -1266,10 +1269,10 @@ elif isosx:
# uses a sub process to get the Hard Drive Serial Number using ioreg
# returns serial numbers of all internal hard drive drives
def GetVolumesSerialNumbers():
sernums = []
sernum = os.getenv('MYSERIALNUMBER')
if sernum != None:
return [sernum]
sernums = []
sernums.append(sernum.strip())
cmdline = '/usr/sbin/ioreg -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1285,7 +1288,7 @@ elif isosx:
if pp >= 0:
sernum = resline[pp+19:-1]
sernums.append(sernum.strip())
return [sernum]
return sernums
def GetUserHomeAppSupKindleDirParitionName():
home = os.getenv('HOME')
@ -1311,10 +1314,11 @@ elif isosx:
return disk
# uses a sub process to get the UUID of the specified disk partition using ioreg
def GetDiskPartitionUUID(diskpart):
def GetDiskPartitionUUIDs(diskpart):
uuids = []
uuidnum = os.getenv('MYUUIDNUMBER')
if uuidnum != None:
return uuidnum
uuids.append(strip(uuidnum))
cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1357,14 +1361,15 @@ elif isosx:
uuidnest = -1
uuidnum = None
bsdname = None
if not foundIt:
uuidnum = ''
return uuidnum
if foundIt:
uuids.append(uuidnum)
return uuids
def GetMACAddressMunged():
def GetMACAddressesMunged():
macnums = []
macnum = os.getenv('MYMACNUM')
if macnum != None:
return macnum
macnums.append(macnum)
cmdline = '/sbin/ifconfig en0'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1399,9 +1404,9 @@ elif isosx:
macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
foundIt = True
break
if not foundIt:
macnum = ''
return macnum
if foundIt:
macnums.append(macnum)
return macnums
# uses unix env to get username instead of using sysctlbyname
@ -1412,11 +1417,12 @@ elif isosx:
def GetIDStrings():
# Return all possible ID Strings
strings = []
strings.append(GetMACAddressMunged())
strings.extend(GetMACAddressesMunged())
strings.extend(GetVolumesSerialNumbers())
diskpart = GetUserHomeAppSupKindleDirParitionName()
strings.append(GetDiskPartitionUUID(diskpart))
strings.extend(GetDiskPartitionUUIDs(diskpart))
strings.append('9999999999')
#print strings
return strings
@ -1797,7 +1803,8 @@ def usage(progname):
print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2010-2013 some_updates and Apprentice Alf".format(progname,__version__)
@ -1837,7 +1844,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -1855,6 +1862,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -9,6 +9,7 @@
# 0.3 changed to autoflush stdout, fixed return code usage
# 0.3 updated for unicode
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
import sys
import binascii
@ -111,8 +112,9 @@ def pidFromSerial(s, l):
return pid
def cli_main(argv=unicode_argv()):
def cli_main():
print u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky"
argv=unicode_argv()
if len(argv)==2:
serial = argv[1]
else:

@ -67,9 +67,10 @@
# 0.37 - Fixed double announcement for stand-alone operation
# 0.38 - Unicode used wherever possible, cope with absent alfcrypto
# 0.39 - Fixed problem with TEXtREAd and getBookType interface
# 0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = u"0.39"
__version__ = u"0.40"
import sys
import os
@ -506,7 +507,8 @@ def getUnencryptedBook(infile,pidlist):
return book.mobi_data
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4:
print u"MobiDeDrm v{0}.\nCopyright © 2008-2012 The Dark Reverser et al.".format(__version__)

@ -7,6 +7,7 @@ import os
import re
import ineptepub
import ignobleepub
import epubtest
import zipfix
import ineptpdf
import erdr2pml
@ -42,18 +43,14 @@ def decryptepub(infile, outdir, rscpath):
try:
rv = ineptepub.decryptBook(userkey, zippath, outfile)
if rv == 0:
print "Decrypted Adobe ePub with key file {0}".format(filename)
break
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
if rv == 0:
os.remove(zippath)
return 0
# now try with ignoble epub
if ignobleepub.ignobleBook(zippath):
elif ignobleepub.ignobleBook(zippath):
# try with any keyfiles (*.b64) in the rscpath
files = os.listdir(rscpath)
filefilter = re.compile("\.b64$", re.IGNORECASE)
@ -62,15 +59,23 @@ def decryptepub(infile, outdir, rscpath):
for filename in files:
keypath = os.path.join(rscpath, filename)
userkey = open(keypath,'r').read()
print userkey
#print userkey
try:
rv = ignobleepub.decryptBook(userkey, zippath, outfile)
if rv == 0:
print "Decrypted B&N ePub with key file {0}".format(filename)
break
except Exception, e:
errlog += traceback.format_exc()
errlog += str(e)
rv = 1
else:
encryption = epubtest.encryption(zippath)
if encryption == "Unencrypted":
print "{0} is not DRMed.".format(name)
rv = 0
else:
print "{0} has an unknown encryption.".format(name)
os.remove(zippath)
if rv != 0:

@ -1,148 +0,0 @@
#!/usr/bin/env python
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
import os, sys
import signal
import threading
import subprocess
from subprocess import Popen, PIPE, STDOUT
# **heavily** chopped up and modfied version of asyncproc.py
# to make it actually work on Windows as well as Mac/Linux
# For the original see:
# "http://www.lysator.liu.se/~bellman/download/"
# author is "Thomas Bellman <bellman@lysator.liu.se>"
# available under GPL version 3 or Later
# create an asynchronous subprocess whose output can be collected in
# a non-blocking manner
# What a mess! Have to use threads just to get non-blocking io
# in a cross-platform manner
# luckily all thread use is hidden within this class
class Process(object):
def __init__(self, *params, **kwparams):
if len(params) <= 3:
kwparams.setdefault('stdin', subprocess.PIPE)
if len(params) <= 4:
kwparams.setdefault('stdout', subprocess.PIPE)
if len(params) <= 5:
kwparams.setdefault('stderr', subprocess.PIPE)
self.__pending_input = []
self.__collected_outdata = []
self.__collected_errdata = []
self.__exitstatus = None
self.__lock = threading.Lock()
self.__inputsem = threading.Semaphore(0)
self.__quit = False
self.__process = subprocess.Popen(*params, **kwparams)
if self.__process.stdin:
self.__stdin_thread = threading.Thread(
name="stdin-thread",
target=self.__feeder, args=(self.__pending_input,
self.__process.stdin))
self.__stdin_thread.setDaemon(True)
self.__stdin_thread.start()
if self.__process.stdout:
self.__stdout_thread = threading.Thread(
name="stdout-thread",
target=self.__reader, args=(self.__collected_outdata,
self.__process.stdout))
self.__stdout_thread.setDaemon(True)
self.__stdout_thread.start()
if self.__process.stderr:
self.__stderr_thread = threading.Thread(
name="stderr-thread",
target=self.__reader, args=(self.__collected_errdata,
self.__process.stderr))
self.__stderr_thread.setDaemon(True)
self.__stderr_thread.start()
def pid(self):
return self.__process.pid
def kill(self, signal):
self.__process.send_signal(signal)
# check on subprocess (pass in 'nowait') to act like poll
def wait(self, flag):
if flag.lower() == 'nowait':
rc = self.__process.poll()
else:
rc = self.__process.wait()
if rc != None:
if self.__process.stdin:
self.closeinput()
if self.__process.stdout:
self.__stdout_thread.join()
if self.__process.stderr:
self.__stderr_thread.join()
return self.__process.returncode
def terminate(self):
if self.__process.stdin:
self.closeinput()
self.__process.terminate()
# thread gets data from subprocess stdout
def __reader(self, collector, source):
while True:
data = os.read(source.fileno(), 65536)
self.__lock.acquire()
collector.append(data)
self.__lock.release()
if data == "":
source.close()
break
return
# thread feeds data to subprocess stdin
def __feeder(self, pending, drain):
while True:
self.__inputsem.acquire()
self.__lock.acquire()
if not pending and self.__quit:
drain.close()
self.__lock.release()
break
data = pending.pop(0)
self.__lock.release()
drain.write(data)
# non-blocking read of data from subprocess stdout
def read(self):
self.__lock.acquire()
outdata = "".join(self.__collected_outdata)
del self.__collected_outdata[:]
self.__lock.release()
return outdata
# non-blocking read of data from subprocess stderr
def readerr(self):
self.__lock.acquire()
errdata = "".join(self.__collected_errdata)
del self.__collected_errdata[:]
self.__lock.release()
return errdata
# non-blocking write to stdin of subprocess
def write(self, data):
if self.__process.stdin is None:
raise ValueError("Writing to process with stdin not a pipe")
self.__lock.acquire()
self.__pending_input.append(data)
self.__inputsem.release()
self.__lock.release()
# close stdinput of subprocess
def closeinput(self):
self.__lock.acquire()
self.__quit = True
self.__inputsem.release()
self.__lock.release()

@ -1,10 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# topazextract.py, version ?
# topazextract.py
# Mostly written by some_updates based on code from many others
__version__ = '4.8'
# Changelog
# 4.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '4.9'
import sys
import os, csv, getopt
@ -442,7 +445,8 @@ def usage(progname):
print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname)
# Main
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"TopazExtract v{0}.".format(__version__)

@ -26,13 +26,14 @@ __docformat__ = 'restructuredtext en'
#
# Revision history:
# 6.0.0 - Initial release
# 6.0.1 - Bug Fixes for Windows App, Kindle for Mac and Windows Adobe Digital Editions
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = u"DeDRM"
PLUGIN_VERSION_TUPLE = (6, 0, 0)
PLUGIN_VERSION_TUPLE = (6, 0, 1)
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'

@ -46,13 +46,14 @@ from __future__ import with_statement
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
# 5.7 - Unicode support added, renamed adobekey from ineptkey
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '5.8'
__version__ = '5.9'
import sys, os, struct, getopt
@ -401,9 +402,11 @@ if iswindows:
aes = AES(keykey)
userkey = aes.decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
#print "found key:",userkey.encode('hex')
keys.append(userkey)
if len(keys) == 0:
raise ADEPTError('Could not locate privateLicenseKey')
print u"Found {0:d} keys".format(len(keys))
return keys
@ -483,7 +486,8 @@ def usage(progname):
print u"Usage:"
print u" {0:s} [-h] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2009-2013 i♥cabbages and Apprentice Alf".format(progname,__version__)
@ -538,7 +542,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -556,6 +560,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])
@ -574,7 +579,7 @@ def gui_main(argv=unicode_argv()):
keyfileout.write(key)
success = True
tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile))
except DrmException, e:
except ADEPTError, e:
tkMessageBox.showerror(progname, u"Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')

@ -29,10 +29,6 @@ from calibre_plugins.dedrm.__init__ import RESOURCE_NAME as help_file_name
from calibre_plugins.dedrm.utilities import (uStrCmp, DETAILED_MESSAGE)
import calibre_plugins.dedrm.dialogs as dialogs
import calibre_plugins.dedrm.ignoblekeygen as bandn
import calibre_plugins.dedrm.erdr2pml as ereader
import calibre_plugins.dedrm.adobekey as adobe
import calibre_plugins.dedrm.kindlekey as amazon
JSON_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
JSON_PATH = os.path.join(u"plugins", JSON_NAME + '.json')
@ -212,6 +208,7 @@ def addvaluetoprefs(prefkind, prefsvalue):
def convertprefs(always = False):
def parseIgnobleString(keystuff):
import calibre_plugins.dedrm.ignoblekeygen as bandn
userkeys = {}
ar = keystuff.split(':')
for i, keystring in enumerate(ar):
@ -230,6 +227,7 @@ def convertprefs(always = False):
return userkeys
def parseeReaderString(keystuff):
import calibre_plugins.dedrm.erdr2pml as ereader
userkeys = {}
ar = keystuff.split(':')
for i, keystring in enumerate(ar):
@ -302,10 +300,13 @@ def convertprefs(always = False):
dedrmprefs['serials'] = []
# get default adobe adept key(s)
import calibre_plugins.dedrm.adobekey as adobe
priorkeycount = len(dedrmprefs['adeptkeys'])
try:
defaultkeys = adobe.adeptkeys()
except:
import traceback
traceback.print_exc()
defaultkeys = []
defaultcount = 1
for keyvalue in defaultkeys:
@ -323,7 +324,8 @@ def convertprefs(always = False):
writeprefs(False)
# get default kindle key(s)
# get default kindle key(s)
import calibre_plugins.dedrm.kindlekey as amazon
priorkeycount = len(dedrmprefs['kindlekeys'])
try:
defaultkeys = amazon.kindlekeys()

@ -7,6 +7,7 @@ __license__ = 'GPL v3'
# Standard Python modules.
import os, sys, re, hashlib
import json
import traceback
from PyQt4.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QListWidget, QListWidgetItem, QAbstractItemView, QLineEdit, QPushButton, QIcon, QGroupBox, QDialog, QDialogButtonBox, QUrl, QString)
from PyQt4 import QtGui
@ -18,10 +19,6 @@ from calibre.utils.config import dynamic, config_dir, JSONConfig
from calibre_plugins.dedrm.__init__ import PLUGIN_NAME, PLUGIN_VERSION
from calibre_plugins.dedrm.utilities import (uStrCmp, DETAILED_MESSAGE, parseCustString)
from calibre_plugins.dedrm.ignoblekeygen import generate_key as generate_bandn_key
from calibre_plugins.dedrm.erdr2pml import getuser_key as generate_ereader_key
from calibre_plugins.dedrm.adobekey import adeptkeys as retrieve_adept_keys
from calibre_plugins.dedrm.kindlekey import kindlekeys as retrieve_kindle_keys
class ManageKeysDialog(QDialog):
def __init__(self, parent, key_type_name, plugin_keys, create_key, keyfile_ext = u""):
@ -393,6 +390,7 @@ class AddBandNKeyDialog(QDialog):
@property
def key_value(self):
from calibre_plugins.dedrm.ignoblekeygen import generate_key as generate_bandn_key
return generate_bandn_key(self.user_name,self.cc_number)
@property
@ -473,6 +471,7 @@ class AddEReaderDialog(QDialog):
@property
def key_value(self):
from calibre_plugins.dedrm.erdr2pml import getuser_key as generate_ereader_key
return generate_ereader_key(self.user_name,self.cc_number).encode('hex')
@property
@ -505,9 +504,12 @@ class AddAdeptDialog(QDialog):
layout = QVBoxLayout(self)
self.setLayout(layout)
from calibre_plugins.dedrm.adobekey import adeptkeys as retrieve_adept_keys
try:
self.default_key = retrieve_adept_keys()[0]
default_keys = retrieve_adept_keys()
self.default_key = default_keys[0]
except:
traceback.print_exc()
self.default_key = u""
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
@ -567,6 +569,7 @@ class AddKindleDialog(QDialog):
layout = QVBoxLayout(self)
self.setLayout(layout)
from calibre_plugins.dedrm.kindlekey import kindlekeys as retrieve_kindle_keys
try:
self.default_key = retrieve_kindle_keys()[0]
except:

@ -9,6 +9,7 @@
#
# Changelog epubtest
# 1.00 - Cut to epubtest.py, testing ePub files only by Apprentice Alf
# 1.01 - Added routine for use by Windows DeDRM
#
# Written in 2011 by Paul Durrant
# Released with unlicense. See http://unlicense.org/
@ -45,7 +46,7 @@
from __future__ import with_statement
__version__ = '1.00'
__version__ = '1.01'
import sys, struct, os
import zlib
@ -72,11 +73,49 @@ class SafeUnbuffered:
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():
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]
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"epubtest.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]
_FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28
@ -129,38 +168,38 @@ def getfiledata(file, zi):
return data
def main(argv=unicode_argv()):
infile = argv[1]
kind = "Unknown"
def encryption(infile):
# returns encryption: one of Unencrypted, Adobe, B&N and Unknown
encryption = "Unknown"
with file(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Mobipocket/Kindle
if bookdata[0:0+2] == "PK":
if bookdata[30:30+28] == 'mimetypeapplication/epub+zip':
kind = "ePub"
else:
kind = "ZIP"
encryption = "Unencrypted"
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
try:
with open(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Zip
if bookdata[0:0+2] == "PK":
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
namelist = set(inzip.namelist())
if 'META-INF/rights.xml' not in namelist or 'META-INF/encryption.xml' not in namelist:
encryption = "Unencrypted"
else:
encryption = "Unknown"
print u"{0} {1}".format(encryption, kind)
rights = etree.fromstring(inzip.read('META-INF/rights.xml'))
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = './/%s' % (adept('encryptedKey'),)
bookkey = ''.join(rights.findtext(expr))
if len(bookkey) == 172:
encryption = "Adobe"
elif len(bookkey) == 64:
encryption = "B&N"
else:
encryption = "Unknown"
except:
traceback.print_exc()
return encryption
def main():
argv=unicode_argv()
print encryption(argv[1])
return 0
if __name__ == "__main__":

@ -66,8 +66,9 @@
# - Don't reject dictionary format.
# - Ignore sidebars for dictionaries (different format?)
# 0.22 - Unicode and plugin support, different image folders for PMLZ and source
# 0.23 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__='0.22'
__version__='0.23'
import sys, re
import struct, binascii, getopt, zlib, os, os.path, urllib, tempfile, traceback
@ -551,9 +552,10 @@ def getuser_key(name,cc):
cc = cc.replace(" ","")
return struct.pack('>LL', binascii.crc32(newname) & 0xffffffff,binascii.crc32(cc[-8:])& 0xffffffff)
def cli_main(argv=unicode_argv()):
def cli_main():
print u"eRdr2Pml v{0}. Copyright © 20092012 The Dark Reverser et al.".format(__version__)
argv=unicode_argv()
try:
opts, args = getopt.getopt(argv[1:], "hp", ["make-pmlz"])
except getopt.GetoptError, err:

@ -33,13 +33,14 @@ from __future__ import with_statement
# 3.6 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 3.7 - Tweaked to match ineptepub more closely
# 3.8 - Fixed to retain zip file metadata (e.g. file modification date)
# 3.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Barnes & Noble encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "3.8"
__version__ = "3.9"
import sys
import os
@ -316,7 +317,8 @@ def decryptBook(keyb64, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.b64> <inbook.epub> <outbook.epub>".format(progname)

@ -31,13 +31,14 @@ from __future__ import with_statement
# 2.3 - Modify interface to allow use of import
# 2.4 - Improvements to UI and now works in plugins
# 2.5 - Additional improvement for unicode and plugin support
# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
"""
__license__ = 'GPL v3'
__version__ = "2.5"
__version__ = "2.6"
import sys
import os
@ -214,7 +215,8 @@ def generate_key(name, ccn):
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if AES is None:
print "%s: This script requires OpenSSL or PyCrypto, which must be installed " \

@ -35,13 +35,14 @@ from __future__ import with_statement
# 5.7 - Fix for potential problem with PyCrypto
# 5.8 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 5.9 - Fixed to retain zip file metadata (e.g. file modification date)
# 5.10 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypt Adobe Digital Editions encrypted ePub books.
"""
__license__ = 'GPL v3'
__version__ = "5.9"
__version__ = "5.10"
import sys
import os
@ -458,7 +459,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.epub> <outbook.epub>".format(progname)

@ -50,13 +50,14 @@ from __future__ import with_statement
# 7.11 - More tweaks to fix minor problems.
# 7.12 - Revised to allow use in calibre plugins to eliminate need for duplicate code
# 7.13 - Fixed erroneous mentions of ineptepub
# 7.14 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Decrypts Adobe ADEPT-encrypted PDF files.
"""
__license__ = 'GPL v3'
__version__ = "7.13"
__version__ = "7.14"
import sys
import os
@ -2185,7 +2186,8 @@ def decryptBook(userkey, inpath, outpath):
return 0
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv) != 4:
print u"usage: {0} <keyfile.der> <inbook.pdf> <outbook.pdf>".format(progname)

@ -53,8 +53,9 @@ from __future__ import with_statement
# 4.9 - Missed some invalid characters in cleanup_name
# 5.0 - Extraction of info from Kindle for PC/Mac moved into kindlekey.py
# - tweaked GetDecryptedBook interface to leave passed parameters unchanged
# 5.1 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '5.0'
__version__ = '5.1'
import sys, os, re
@ -276,7 +277,8 @@ def usage(progname):
#
# Main
#
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"K4MobiDeDrm v{0}.\nCopyright © 2008-2013 The Dark Reverser et al.".format(__version__)

@ -15,13 +15,16 @@ from __future__ import with_statement
# 1.3 - Added getkey interface for Windows DeDRM application
# Simplified some of the Kindle for Mac code.
# 1.4 - Remove dependency on alfcrypto
# 1.5 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 1.6 - Fixed a problem getting the disk serial numbers
"""
Retrieve Kindle for PC/Mac user key.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.6'
import sys, os, re
from struct import pack, unpack, unpack_from
@ -1266,10 +1269,10 @@ elif isosx:
# uses a sub process to get the Hard Drive Serial Number using ioreg
# returns serial numbers of all internal hard drive drives
def GetVolumesSerialNumbers():
sernums = []
sernum = os.getenv('MYSERIALNUMBER')
if sernum != None:
return [sernum]
sernums = []
sernums.append(sernum.strip())
cmdline = '/usr/sbin/ioreg -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1285,7 +1288,7 @@ elif isosx:
if pp >= 0:
sernum = resline[pp+19:-1]
sernums.append(sernum.strip())
return [sernum]
return sernums
def GetUserHomeAppSupKindleDirParitionName():
home = os.getenv('HOME')
@ -1311,10 +1314,11 @@ elif isosx:
return disk
# uses a sub process to get the UUID of the specified disk partition using ioreg
def GetDiskPartitionUUID(diskpart):
def GetDiskPartitionUUIDs(diskpart):
uuids = []
uuidnum = os.getenv('MYUUIDNUMBER')
if uuidnum != None:
return uuidnum
uuids.append(strip(uuidnum))
cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1357,14 +1361,15 @@ elif isosx:
uuidnest = -1
uuidnum = None
bsdname = None
if not foundIt:
uuidnum = ''
return uuidnum
if foundIt:
uuids.append(uuidnum)
return uuids
def GetMACAddressMunged():
def GetMACAddressesMunged():
macnums = []
macnum = os.getenv('MYMACNUM')
if macnum != None:
return macnum
macnums.append(macnum)
cmdline = '/sbin/ifconfig en0'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@ -1399,9 +1404,9 @@ elif isosx:
macnum = '%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x' % (mlst[0], mlst[1], mlst[2], mlst[3], mlst[4], mlst[5])
foundIt = True
break
if not foundIt:
macnum = ''
return macnum
if foundIt:
macnums.append(macnum)
return macnums
# uses unix env to get username instead of using sysctlbyname
@ -1412,11 +1417,12 @@ elif isosx:
def GetIDStrings():
# Return all possible ID Strings
strings = []
strings.append(GetMACAddressMunged())
strings.extend(GetMACAddressesMunged())
strings.extend(GetVolumesSerialNumbers())
diskpart = GetUserHomeAppSupKindleDirParitionName()
strings.append(GetDiskPartitionUUID(diskpart))
strings.extend(GetDiskPartitionUUIDs(diskpart))
strings.append('9999999999')
#print strings
return strings
@ -1797,7 +1803,8 @@ def usage(progname):
print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2010-2013 some_updates and Apprentice Alf".format(progname,__version__)
@ -1837,7 +1844,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -1855,6 +1862,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -9,6 +9,7 @@
# 0.3 changed to autoflush stdout, fixed return code usage
# 0.3 updated for unicode
# 0.4 Added support for serial numbers starting with '9', fixed unicode bugs.
# 0.5 moved unicode_argv call inside main for Windows DeDRM compatibility
import sys
import binascii
@ -111,8 +112,9 @@ def pidFromSerial(s, l):
return pid
def cli_main(argv=unicode_argv()):
def cli_main():
print u"Mobipocket PID calculator for Amazon Kindle. Copyright © 2007, 2009 Igor Skochinsky"
argv=unicode_argv()
if len(argv)==2:
serial = argv[1]
else:

@ -67,9 +67,10 @@
# 0.37 - Fixed double announcement for stand-alone operation
# 0.38 - Unicode used wherever possible, cope with absent alfcrypto
# 0.39 - Fixed problem with TEXtREAd and getBookType interface
# 0.40 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = u"0.39"
__version__ = u"0.40"
import sys
import os
@ -506,7 +507,8 @@ def getUnencryptedBook(infile,pidlist):
return book.mobi_data
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if len(argv)<3 or len(argv)>4:
print u"MobiDeDrm v{0}.\nCopyright © 2008-2012 The Dark Reverser et al.".format(__version__)

@ -1,10 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# topazextract.py, version ?
# topazextract.py
# Mostly written by some_updates based on code from many others
__version__ = '4.8'
# Changelog
# 4.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
__version__ = '4.9'
import sys
import os, csv, getopt
@ -442,7 +445,8 @@ def usage(progname):
print u" {0} [-k <kindle.k4i>] [-p <comma separated PIDs>] [-s <comma separated Kindle serial numbers>] <infile> <outdir>".format(progname)
# Main
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"TopazExtract v{0}.".format(__version__)

@ -13,7 +13,7 @@ Do **NOT** select "Get plugins to enhance calibre" as this is reserved for "offi
Customization
-------------
On Windows and Mac, the keys for ebooks downloaded for Kindle for Mac/PC and Adobe Digital Editions are automatically generated. If all your DRMed ebooks can be opened and read in Kindle for Mac/PC and/or Adobe Digital Editions on the same computer on which you are running calibre, you do not need to do any configuration of this plugin. On Linux, keys for Kindle for PC and Adobe Figital Editions need to be generated separately (see Linux systems section)
On Windows and Mac, the keys for ebooks downloaded for Kindle for Mac/PC and Adobe Digital Editions are automatically generated. If all your DRMed ebooks can be opened and read in Kindle for Mac/PC and/or Adobe Digital Editions on the same computer on which you are running calibre, you do not need to do any configuration of this plugin. On Linux, keys for Kindle for PC and Adobe Digital Editions need to be generated separately (see Linux systems section)
Otherwise, highlight the plugin (DeDRM under the "File type plugins" category) and click the "Customize Plugin" button.

@ -46,13 +46,14 @@ from __future__ import with_statement
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
# 5.7 - Unicode support added, renamed adobekey from ineptkey
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '5.8'
__version__ = '5.9'
import sys, os, struct, getopt
@ -483,7 +484,8 @@ def usage(progname):
print u"Usage:"
print u" {0:s} [-h] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2009-2013 i♥cabbages and Apprentice Alf".format(progname,__version__)
@ -538,7 +540,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -556,6 +558,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -31,13 +31,14 @@ from __future__ import with_statement
# 2.3 - Modify interface to allow use of import
# 2.4 - Improvements to UI and now works in plugins
# 2.5 - Additional improvement for unicode and plugin support
# 2.6 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
"""
__license__ = 'GPL v3'
__version__ = "2.5"
__version__ = "2.6"
import sys
import os
@ -214,7 +215,8 @@ def generate_key(name, ccn):
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
if AES is None:
print "%s: This script requires OpenSSL or PyCrypto, which must be installed " \

@ -15,13 +15,15 @@ from __future__ import with_statement
# 1.3 - Added getkey interface for Windows DeDRM application
# Simplified some of the Kindle for Mac code.
# 1.4 - Remove dependency on alfcrypto
# 1.5 - moved unicode_argv call inside main for Windows DeDRM compatibility
"""
Retrieve Kindle for PC/Mac user key.
"""
__license__ = 'GPL v3'
__version__ = '1.4'
__version__ = '1.5'
import sys, os, re
from struct import pack, unpack, unpack_from
@ -1797,7 +1799,8 @@ def usage(progname):
print u" {0:s} [-h] [-k <kindle.info>] [<outpath>]".format(progname)
def cli_main(argv=unicode_argv()):
def cli_main():
argv=unicode_argv()
progname = os.path.basename(argv[0])
print u"{0} v{1}\nCopyright © 2010-2013 some_updates and Apprentice Alf".format(progname,__version__)
@ -1837,7 +1840,7 @@ def cli_main(argv=unicode_argv()):
return 0
def gui_main(argv=unicode_argv()):
def gui_main():
import Tkinter
import Tkconstants
import tkMessageBox
@ -1855,6 +1858,7 @@ def gui_main(argv=unicode_argv()):
self.text.insert(Tkconstants.END, text)
argv=unicode_argv()
root = Tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])

@ -1,7 +1,7 @@
Welcome to the tools!
=====================
This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started. This document is part of the Tools v6.0.0 archive from Apprentice Alf's Blog: http://apprenticealf.wordpress.com/
This ReadMe_First.txt is meant to give users a quick overview of what is available and how to get started. This document is part of the Tools v6.0.1 archive from Apprentice Alf's Blog: http://apprenticealf.wordpress.com/
The is archive includes tools to remove DRM from:
@ -36,9 +36,9 @@ The DeDRM python GUI is by some_updates and Apprentice Alf
Many fixes, updates and enhancements to the scripts and applicatons have been by Apprentice Alf, some_updates and DiapDealer and others.
Calibre Users (Mac OS X, Windows, and Linux)
DeDRM plugin for calibre (Mac OS X, Windows, and Linux)
--------------------------------------------
If you are a calibre user, the quickest and easiest way, especially on Windows, to remove DRM from your ebooks is to install the DeDRM plugin from the DeDRM_plugin folder, following the instructions and configuration directions provided in the ReadMe and the help links.
If you already use calibre, the quickest and easiest way, especially on Windows, to remove DRM from your ebooks is to install the DeDRM plugin from the DeDRM_plugin folder, following the instructions and configuration directions provided in the ReadMe and the help links.
Once installed and configured, you can simply add a DRM book to calibre and the DeDRMed version will be imported into the calibre database. Note that DRM removal ONLY occurs on import. If you have already imported DRM books you'll need to remove them from calibre and re-import them.
@ -49,7 +49,7 @@ DeDRM application for Mac OS X users: (Mac OS X 10.4 and above)
----------------------------------------------------------------------
This application combines all the tools into one easy-to-use tool for Mac OS X users.
Drag the "DeDRM 6.0.0.app" application from the DeDRM_Application_Macintosh folder to your Desktop (or your Applications Folder, or anywhere else you find convenient). Double-click on the application to run it and it will guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
Drag the "DeDRM.app" application from the DeDRM_Application_Macintosh folder to your Desktop (or your Applications Folder, or anywhere else you find convenient). Double-click on the application to run it and it will guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
To use the DeDRM application, simply drag ebooks, or folders containing ebooks, onto the DeDRM application and it will remove the DRM of the kinds listed above.
@ -63,7 +63,7 @@ DeDRM application for Windows users: (Windows XP through Windows 8)
This application combines all the tools into one easy-to-use tool for Windows users.
Drag the DeDRM_6.0.0 folder that's in the DeDRM_Application_Windows folder, to your "My Documents" folder (or anywhere else you find convenient). Make a short-cut on your Desktop of the DeDRM_Drop_Target.bat file that's in the DeDRM_6.0.0 folder. Double-click on the shortcut and the DeDRM application will run and guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
Drag the DeDRM_App folder that's in the DeDRM_Application_Windows folder, to your "My Documents" folder (or anywhere else you find convenient). Make a short-cut on your Desktop of the DeDRM_Drop_Target.bat file that's in the DeDRM_App folder. Double-click on the shortcut and the DeDRM application will run and guide you through collecting the data it needs to remove the DRM from any of the kinds of DRMed ebook listed in the first section of this ReadMe.
To use the DeDRM application, simply drag ebooks, or folders containing ebooks, onto the DeDRM_Drop_Target.bat shortcut and it will remove the DRM of the kinds listed above.
@ -72,13 +72,10 @@ For more detailed instructions, see the DeDRM_Application_ReadMe.txt file in the
Other_Tools
-----------
This folder includes other useful tools:
This folder includes other tools that may be useful for DRMed ebooks from certain sources or for Linux users. Most users won't need any of these tools.
Key_Generation_Script
This folder contains a python script that creates a hashed keyfile for Barnes and Noble ePubs, and will be useful to Windows and Linux users who don't want to use the plugin.
Key_Retrieval_Scripts
This folder contains python script that retrieve keyfiles for Kindle for Mac and Adobe Digital Editions, and will be useful to all Linux Users.
Key_Generation_Scripts
This folder contains python scripts that creates a keyfiles for Barnes and Noble ePubs, Adobe Digital Editions ePubs and Kindle for Mac/PC ebooks. The will mostly be useful for Linux users using the calibre plugin.
Kindle_for_Android_Patches
Definitely only for the adventurous, this folder contains information on how to modify the Kindel for Android app to b able to get a PID for use with the other Kindle tools (DeDRM apps and calibre plugin).

Loading…
Cancel
Save