diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py index fd4c193..6e6aef7 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/androidkindlekey.py @@ -5,7 +5,7 @@ from __future__ import with_statement # androidkindlekey.py # Copyright © 2013-15 by Thom -# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper +# Some portions Copyright © 2010-15 by some_updates, Apprentice Alf and Apprentice Harper # # Revision history: @@ -14,14 +14,14 @@ from __future__ import with_statement # 1.2 - Changed to be callable from AppleScript by returning only serial number # - and changed name to androidkindlekey.py # - and added in unicode command line support - +# 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. """ Retrieve Kindle for Android Serial Number. """ __license__ = 'GPL v3' -__version__ = '1.2' +__version__ = '1.3' import os import sys @@ -272,7 +272,32 @@ def get_serials(path=STORAGE): return serials -__all__ = [ 'get_serials' ] +__all__ = [ 'get_serials', 'getkey'] + +# interface for Python DeDRM +# returns single key or multiple keys, depending on path or file passed in +def getkey(outpath, inpath): + keys = get_serials(inpath) + if len(keys) > 0: + if not os.path.isdir(outpath): + outfile = outpath + with file(outfile, 'w') as keyfileout: + keyfileout.write(keys[0]) + print u"Saved a key to {0}".format(outfile) + else: + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(outpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + print u"Saved a key to {0}".format(outfile) + return True + return False + def usage(progname): print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__) @@ -283,7 +308,7 @@ def usage(progname): print u"" print u"Serial number is written to standard output." print u"Usage:" - print u" {0:s} [-h] ".format(progname) + print u" {0:s} [-h] [-b ] []".format(progname) def cli_main(): @@ -291,47 +316,131 @@ def cli_main(): sys.stderr=SafeUnbuffered(sys.stderr) argv=unicode_argv() progname = os.path.basename(argv[0]) + print u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__) try: - opts, args = getopt.getopt(argv[1:], "h") + opts, args = getopt.getopt(argv[1:], "hb:") except getopt.GetoptError, err: usage(progname) print u"\nError in options or arguments: {0}".format(err.args[0]) return 2 - files = [] + inpath = "" for o, a in opts: if o == "-h": usage(progname) return 0 + if o == "-b": + inpath = a if len(args) > 1: usage(progname) return 2 - inpath = args[0] - if not os.path.isabs(inpath): - inpath = os.path.abspath(inpath) + if len(args) == 1: + # save to the specified file or directory + outpath = args[0] + if not os.path.isabs(outpath): + outpath = os.path.join(os.path.dirname(argv[0]),outpath) + outpath = os.path.abspath(outpath) + else: + # save to the same directory as the script + outpath = os.path.dirname(argv[0]) - inpath = os.path.realpath(os.path.normpath(inpath)) + # make sure the outpath is OK + outpath = os.path.realpath(os.path.normpath(outpath)) if not os.path.isfile(inpath): usage(progname) print u"\n{0:s} file not found".format(inpath) return 2 - serials = get_serials(inpath) + if not getkey(outpath, inpath): + print u"Could not retrieve Kindle for Android key." + return 0 - if len(serials) == 0: - print u"No keys found in {0:s}".format(inpath) - return 2 - for serial in serials: - print serial +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + import tkFileDialog + import traceback + except: + print "Tkinter not installed" + return cli_main() + + class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.status = Tkinter.Label(self, text=u"Select backup.ab file") + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0) + self.keypath = Tkinter.Entry(body, width=40) + self.keypath.grid(row=0, column=1, sticky=sticky) + self.keypath.insert(2, u"backup.ab") + button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + button.grid(row=0, column=2) + buttons = Tkinter.Frame(self) + buttons.pack() + button2 = Tkinter.Button( + buttons, text=u"Extract", width=10, command=self.generate) + button2.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button3 = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button3.pack(side=Tkconstants.RIGHT) + + def get_keypath(self): + keypath = tkFileDialog.askopenfilename( + parent=None, title=u"Select backup.ab file", + defaultextension=u".ab", + filetypes=[('adb backup com.amazon.kindle', '.ab'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + self.keypath.delete(0, Tkconstants.END) + self.keypath.insert(0, keypath) + return + + def generate(self): + inpath = self.keypath.get() + self.status['text'] = u"Getting key..." + try: + keys = get_serials(inpath) + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + success = True + tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + except Exception, e: + self.status['text'] = u"Error: {0}".format(e.args[0]) + return + self.status['text'] = u"Select backup.ab file" + + argv=unicode_argv() + progpath, progname = os.path.split(argv[0]) + root = Tkinter.Tk() + root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__)) + root.resizable(True, False) + root.minsize(300, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() return 0 if __name__ == '__main__': if len(sys.argv) > 1: sys.exit(cli_main()) - usage(os.path.basename(unicode_argv()[0])) - sys.exit(0); + sys.exit(gui_main()) diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw index 7381664..d33fef9 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw @@ -12,8 +12,9 @@ # 6.0.4 - Fix for other potential unicode problems # 6.0.5 - Fix typo # 6.2.0 - Update to match plugin and AppleScript +# 6.3.0 - Add in Android support -__version__ = '6.2.0' +__version__ = '6.3.0' import sys import os, os.path @@ -158,83 +159,107 @@ class PrefsDialog(Toplevel): body.pack(fill=Tkconstants.X, expand=1) sticky = Tkconstants.E + Tkconstants.W body.grid_columnconfigure(1, weight=2) - - Tkinter.Label(body, text='Adobe Key file (adeptkey.der)').grid(row=0, sticky=Tkconstants.E) + + cur_row = 0 + Tkinter.Label(body, text='Adobe Key file (adeptkey.der)').grid(row=cur_row, sticky=Tkconstants.E) self.adkpath = Tkinter.Entry(body, width=50) - self.adkpath.grid(row=0, column=1, sticky=sticky) + self.adkpath.grid(row=cur_row, column=1, sticky=sticky) prefdir = self.prefs_array['dir'] keyfile = os.path.join(prefdir,'adeptkey.der') if os.path.isfile(keyfile): path = keyfile - self.adkpath.insert(0, path) + self.adkpath.insert(cur_row, path) button = Tkinter.Button(body, text="...", command=self.get_adkpath) - button.grid(row=0, column=2) + button.grid(row=cur_row, column=2) - Tkinter.Label(body, text='Kindle Key file (kindlekey.k4i)').grid(row=1, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='Kindle Key file (kindlekey.k4i)').grid(row=cur_row, sticky=Tkconstants.E) self.kkpath = Tkinter.Entry(body, width=50) - self.kkpath.grid(row=1, column=1, sticky=sticky) + self.kkpath.grid(row=cur_row, column=1, sticky=sticky) prefdir = self.prefs_array['dir'] keyfile = os.path.join(prefdir,'kindlekey.k4i') if os.path.isfile(keyfile): path = keyfile - self.kkpath.insert(1, path) + self.kkpath.insert(0, path) button = Tkinter.Button(body, text="...", command=self.get_kkpath) - button.grid(row=1, column=2) + button.grid(row=cur_row, column=2) - Tkinter.Label(body, text='Barnes and Noble Key file (bnepubkey.b64)').grid(row=2, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='Android Kindle Key file (kindlekey.k4a)').grid(row=cur_row, sticky=Tkconstants.E) + self.akkpath = Tkinter.Entry(body, width=50) + self.akkpath.grid(row=cur_row, column=1, sticky=sticky) + prefdir = self.prefs_array['dir'] + keyfile = os.path.join(prefdir,'kindlekey.k4a') + if os.path.isfile(keyfile): + path = keyfile + self.akkpath.insert(0, path) + button = Tkinter.Button(body, text="...", command=self.get_akkpath) + button.grid(row=cur_row, column=2) + + cur_row = cur_row + 1 + Tkinter.Label(body, text='Barnes and Noble Key file (bnepubkey.b64)').grid(row=cur_row, sticky=Tkconstants.E) self.bnkpath = Tkinter.Entry(body, width=50) - self.bnkpath.grid(row=2, column=1, sticky=sticky) + self.bnkpath.grid(row=cur_row, column=1, sticky=sticky) prefdir = self.prefs_array['dir'] keyfile = os.path.join(prefdir,'bnepubkey.b64') if os.path.isfile(keyfile): path = keyfile - self.bnkpath.insert(2, path) + self.bnkpath.insert(0, path) button = Tkinter.Button(body, text="...", command=self.get_bnkpath) - button.grid(row=2, column=2) + button.grid(row=cur_row, column=2) - Tkinter.Label(body, text='Mobipocket PID list\n(8 or 10 characters, comma separated)').grid(row=3, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='Mobipocket PID list\n(8 or 10 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E) self.pidnums = Tkinter.StringVar() self.pidinfo = Tkinter.Entry(body, width=50, textvariable=self.pidnums) if 'pids' in self.prefs_array: self.pidnums.set(self.prefs_array['pids']) - self.pidinfo.grid(row=3, column=1, sticky=sticky) + self.pidinfo.grid(row=cur_row, column=1, sticky=sticky) - Tkinter.Label(body, text='eInk Kindle Serial Number list\n(16 characters, comma separated)').grid(row=4, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='eInk Kindle Serial Number list\n(16 characters, comma separated)').grid(row=cur_row, sticky=Tkconstants.E) self.sernums = Tkinter.StringVar() self.serinfo = Tkinter.Entry(body, width=50, textvariable=self.sernums) if 'serials' in self.prefs_array: self.sernums.set(self.prefs_array['serials']) - self.serinfo.grid(row=4, column=1, sticky=sticky) + self.serinfo.grid(row=cur_row, column=1, sticky=sticky) - Tkinter.Label(body, text='eReader data list\n(name:last 8 digits on credit card, comma separated)').grid(row=5, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='eReader data list\n(name:last 8 digits on credit card, comma separated)').grid(row=cur_row, sticky=Tkconstants.E) self.sdrmnums = Tkinter.StringVar() self.sdrminfo = Tkinter.Entry(body, width=50, textvariable=self.sdrmnums) if 'sdrms' in self.prefs_array: self.sdrmnums.set(self.prefs_array['sdrms']) - self.sdrminfo.grid(row=5, column=1, sticky=sticky) + self.sdrminfo.grid(row=cur_row, column=1, sticky=sticky) - Tkinter.Label(body, text="Output Folder (if blank, use input ebook's folder)").grid(row=6, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text="Output Folder (if blank, use input ebook's folder)").grid(row=cur_row, sticky=Tkconstants.E) self.outpath = Tkinter.Entry(body, width=50) - self.outpath.grid(row=6, column=1, sticky=sticky) + self.outpath.grid(row=cur_row, column=1, sticky=sticky) if 'outdir' in self.prefs_array: dpath = self.prefs_array['outdir'] self.outpath.insert(0, dpath) button = Tkinter.Button(body, text="...", command=self.get_outpath) - button.grid(row=6, column=2) + button.grid(row=cur_row, column=2) - Tkinter.Label(body, text='').grid(row=7, column=0, columnspan=2, sticky=Tkconstants.N) + cur_row = cur_row + 1 + Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N) - Tkinter.Label(body, text='Alternatively Process an eBook').grid(row=8, column=0, columnspan=2, sticky=Tkconstants.N) + cur_row = cur_row + 1 + Tkinter.Label(body, text='Alternatively Process an eBook').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.N) - Tkinter.Label(body, text='Select an eBook to Process*').grid(row=9, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='Select an eBook to Process*').grid(row=cur_row, sticky=Tkconstants.E) self.bookpath = Tkinter.Entry(body, width=50) - self.bookpath.grid(row=9, column=1, sticky=sticky) + self.bookpath.grid(row=cur_row, column=1, sticky=sticky) button = Tkinter.Button(body, text="...", command=self.get_bookpath) - button.grid(row=9, column=2) + button.grid(row=cur_row, column=2) - Tkinter.Label(body, font=("Helvetica", "10", "italic"), text='*To DeDRM multiple ebooks simultaneously, set your preferences and quit.\nThen drag and drop ebooks or folders onto the DeDRM_Drop_Target').grid(row=10, column=1, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, font=("Helvetica", "10", "italic"), text='*To DeDRM multiple ebooks simultaneously, set your preferences and quit.\nThen drag and drop ebooks or folders onto the DeDRM_Drop_Target').grid(row=cur_row, column=1, sticky=Tkconstants.E) - Tkinter.Label(body, text='').grid(row=11, column=0, columnspan=2, sticky=Tkconstants.E) + cur_row = cur_row + 1 + Tkinter.Label(body, text='').grid(row=cur_row, column=0, columnspan=2, sticky=Tkconstants.E) buttons = Tkinter.Frame(self) buttons.pack() @@ -304,6 +329,24 @@ class PrefsDialog(Toplevel): self.kkpath.insert(0, kkpath) return + def get_akkpath(self): + akkbpath = tkFileDialog.askopenfilename(parent=None, title='Select Android for Kindle backup file', + defaultextension='.ab', filetypes=[('Kindle for Android backup file', '.ab'), ('All Files', '.*')]) + if akkbpath: + # call androidkindlekey here + prefdir = self.prefs_array['dir'] + androidkindlekeyfile = os.path.join(prefdir,'kindlekey.k4a') + import androidkindlekey + try: + androidkindlekey.getkey(androidkindlekeyfile, akkbpath) + except: + traceback.print_exc() + pass + if os.path.isfile(androidkindlekeyfile): + self.akkpath.delete(0, Tkconstants.END) + self.akkpath.insert(0, androidkindlekeyfile) + return + def get_bnkpath(self): cpath = self.bnkpath.get() bnkpath = tkFileDialog.askopenfilename(initialdir = cpath, parent=None, title='Select Barnes and Noble Key file', diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py index fd4c193..6e6aef7 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/androidkindlekey.py @@ -5,7 +5,7 @@ from __future__ import with_statement # androidkindlekey.py # Copyright © 2013-15 by Thom -# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper +# Some portions Copyright © 2010-15 by some_updates, Apprentice Alf and Apprentice Harper # # Revision history: @@ -14,14 +14,14 @@ from __future__ import with_statement # 1.2 - Changed to be callable from AppleScript by returning only serial number # - and changed name to androidkindlekey.py # - and added in unicode command line support - +# 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. """ Retrieve Kindle for Android Serial Number. """ __license__ = 'GPL v3' -__version__ = '1.2' +__version__ = '1.3' import os import sys @@ -272,7 +272,32 @@ def get_serials(path=STORAGE): return serials -__all__ = [ 'get_serials' ] +__all__ = [ 'get_serials', 'getkey'] + +# interface for Python DeDRM +# returns single key or multiple keys, depending on path or file passed in +def getkey(outpath, inpath): + keys = get_serials(inpath) + if len(keys) > 0: + if not os.path.isdir(outpath): + outfile = outpath + with file(outfile, 'w') as keyfileout: + keyfileout.write(keys[0]) + print u"Saved a key to {0}".format(outfile) + else: + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(outpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + print u"Saved a key to {0}".format(outfile) + return True + return False + def usage(progname): print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__) @@ -283,7 +308,7 @@ def usage(progname): print u"" print u"Serial number is written to standard output." print u"Usage:" - print u" {0:s} [-h] ".format(progname) + print u" {0:s} [-h] [-b ] []".format(progname) def cli_main(): @@ -291,47 +316,131 @@ def cli_main(): sys.stderr=SafeUnbuffered(sys.stderr) argv=unicode_argv() progname = os.path.basename(argv[0]) + print u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__) try: - opts, args = getopt.getopt(argv[1:], "h") + opts, args = getopt.getopt(argv[1:], "hb:") except getopt.GetoptError, err: usage(progname) print u"\nError in options or arguments: {0}".format(err.args[0]) return 2 - files = [] + inpath = "" for o, a in opts: if o == "-h": usage(progname) return 0 + if o == "-b": + inpath = a if len(args) > 1: usage(progname) return 2 - inpath = args[0] - if not os.path.isabs(inpath): - inpath = os.path.abspath(inpath) + if len(args) == 1: + # save to the specified file or directory + outpath = args[0] + if not os.path.isabs(outpath): + outpath = os.path.join(os.path.dirname(argv[0]),outpath) + outpath = os.path.abspath(outpath) + else: + # save to the same directory as the script + outpath = os.path.dirname(argv[0]) - inpath = os.path.realpath(os.path.normpath(inpath)) + # make sure the outpath is OK + outpath = os.path.realpath(os.path.normpath(outpath)) if not os.path.isfile(inpath): usage(progname) print u"\n{0:s} file not found".format(inpath) return 2 - serials = get_serials(inpath) + if not getkey(outpath, inpath): + print u"Could not retrieve Kindle for Android key." + return 0 - if len(serials) == 0: - print u"No keys found in {0:s}".format(inpath) - return 2 - for serial in serials: - print serial +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + import tkFileDialog + import traceback + except: + print "Tkinter not installed" + return cli_main() + + class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.status = Tkinter.Label(self, text=u"Select backup.ab file") + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0) + self.keypath = Tkinter.Entry(body, width=40) + self.keypath.grid(row=0, column=1, sticky=sticky) + self.keypath.insert(2, u"backup.ab") + button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + button.grid(row=0, column=2) + buttons = Tkinter.Frame(self) + buttons.pack() + button2 = Tkinter.Button( + buttons, text=u"Extract", width=10, command=self.generate) + button2.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button3 = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button3.pack(side=Tkconstants.RIGHT) + + def get_keypath(self): + keypath = tkFileDialog.askopenfilename( + parent=None, title=u"Select backup.ab file", + defaultextension=u".ab", + filetypes=[('adb backup com.amazon.kindle', '.ab'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + self.keypath.delete(0, Tkconstants.END) + self.keypath.insert(0, keypath) + return + + def generate(self): + inpath = self.keypath.get() + self.status['text'] = u"Getting key..." + try: + keys = get_serials(inpath) + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + success = True + tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + except Exception, e: + self.status['text'] = u"Error: {0}".format(e.args[0]) + return + self.status['text'] = u"Select backup.ab file" + + argv=unicode_argv() + progpath, progname = os.path.split(argv[0]) + root = Tkinter.Tk() + root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__)) + root.resizable(True, False) + root.minsize(300, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() return 0 if __name__ == '__main__': if len(sys.argv) > 1: sys.exit(cli_main()) - usage(os.path.basename(unicode_argv()[0])) - sys.exit(0); + sys.exit(gui_main()) diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/scriptinterface.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/scriptinterface.py index 3be643f..1212f51 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/scriptinterface.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/scriptinterface.py @@ -158,6 +158,14 @@ def decryptk4mobi(infile, outdir, rscpath): serialstr = serialstr.strip() if serialstr != '': serialnums = serialstr.split(',') + files = os.listdir(rscpath) + filefilter = re.compile("\.k4a$", re.IGNORECASE) + files = filter(filefilter.search, files) + if files: + for filename in files: + dpath = os.path.join(rscpath,filename) + androidserial = open(keypath,'r').read() + serialnums.append(androidserial) kDatabaseFiles = [] files = os.listdir(rscpath) filefilter = re.compile("\.k4i$", re.IGNORECASE) diff --git a/DeDRM_calibre_plugin/DeDRM_plugin/androidkindlekey.py b/DeDRM_calibre_plugin/DeDRM_plugin/androidkindlekey.py index fd4c193..6e6aef7 100644 --- a/DeDRM_calibre_plugin/DeDRM_plugin/androidkindlekey.py +++ b/DeDRM_calibre_plugin/DeDRM_plugin/androidkindlekey.py @@ -5,7 +5,7 @@ from __future__ import with_statement # androidkindlekey.py # Copyright © 2013-15 by Thom -# Some portions Copyright © 2011-15 by Apprentice Alf and Apprentice Harper +# Some portions Copyright © 2010-15 by some_updates, Apprentice Alf and Apprentice Harper # # Revision history: @@ -14,14 +14,14 @@ from __future__ import with_statement # 1.2 - Changed to be callable from AppleScript by returning only serial number # - and changed name to androidkindlekey.py # - and added in unicode command line support - +# 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. """ Retrieve Kindle for Android Serial Number. """ __license__ = 'GPL v3' -__version__ = '1.2' +__version__ = '1.3' import os import sys @@ -272,7 +272,32 @@ def get_serials(path=STORAGE): return serials -__all__ = [ 'get_serials' ] +__all__ = [ 'get_serials', 'getkey'] + +# interface for Python DeDRM +# returns single key or multiple keys, depending on path or file passed in +def getkey(outpath, inpath): + keys = get_serials(inpath) + if len(keys) > 0: + if not os.path.isdir(outpath): + outfile = outpath + with file(outfile, 'w') as keyfileout: + keyfileout.write(keys[0]) + print u"Saved a key to {0}".format(outfile) + else: + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(outpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + print u"Saved a key to {0}".format(outfile) + return True + return False + def usage(progname): print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__) @@ -283,7 +308,7 @@ def usage(progname): print u"" print u"Serial number is written to standard output." print u"Usage:" - print u" {0:s} [-h] ".format(progname) + print u" {0:s} [-h] [-b ] []".format(progname) def cli_main(): @@ -291,47 +316,131 @@ def cli_main(): sys.stderr=SafeUnbuffered(sys.stderr) argv=unicode_argv() progname = os.path.basename(argv[0]) + print u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__) try: - opts, args = getopt.getopt(argv[1:], "h") + opts, args = getopt.getopt(argv[1:], "hb:") except getopt.GetoptError, err: usage(progname) print u"\nError in options or arguments: {0}".format(err.args[0]) return 2 - files = [] + inpath = "" for o, a in opts: if o == "-h": usage(progname) return 0 + if o == "-b": + inpath = a if len(args) > 1: usage(progname) return 2 - inpath = args[0] - if not os.path.isabs(inpath): - inpath = os.path.abspath(inpath) + if len(args) == 1: + # save to the specified file or directory + outpath = args[0] + if not os.path.isabs(outpath): + outpath = os.path.join(os.path.dirname(argv[0]),outpath) + outpath = os.path.abspath(outpath) + else: + # save to the same directory as the script + outpath = os.path.dirname(argv[0]) - inpath = os.path.realpath(os.path.normpath(inpath)) + # make sure the outpath is OK + outpath = os.path.realpath(os.path.normpath(outpath)) if not os.path.isfile(inpath): usage(progname) print u"\n{0:s} file not found".format(inpath) return 2 - serials = get_serials(inpath) + if not getkey(outpath, inpath): + print u"Could not retrieve Kindle for Android key." + return 0 - if len(serials) == 0: - print u"No keys found in {0:s}".format(inpath) - return 2 - for serial in serials: - print serial +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + import tkFileDialog + import traceback + except: + print "Tkinter not installed" + return cli_main() + + class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.status = Tkinter.Label(self, text=u"Select backup.ab file") + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0) + self.keypath = Tkinter.Entry(body, width=40) + self.keypath.grid(row=0, column=1, sticky=sticky) + self.keypath.insert(2, u"backup.ab") + button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + button.grid(row=0, column=2) + buttons = Tkinter.Frame(self) + buttons.pack() + button2 = Tkinter.Button( + buttons, text=u"Extract", width=10, command=self.generate) + button2.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button3 = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button3.pack(side=Tkconstants.RIGHT) + + def get_keypath(self): + keypath = tkFileDialog.askopenfilename( + parent=None, title=u"Select backup.ab file", + defaultextension=u".ab", + filetypes=[('adb backup com.amazon.kindle', '.ab'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + self.keypath.delete(0, Tkconstants.END) + self.keypath.insert(0, keypath) + return + + def generate(self): + inpath = self.keypath.get() + self.status['text'] = u"Getting key..." + try: + keys = get_serials(inpath) + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + success = True + tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + except Exception, e: + self.status['text'] = u"Error: {0}".format(e.args[0]) + return + self.status['text'] = u"Select backup.ab file" + + argv=unicode_argv() + progpath, progname = os.path.split(argv[0]) + root = Tkinter.Tk() + root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__)) + root.resizable(True, False) + root.minsize(300, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() return 0 if __name__ == '__main__': if len(sys.argv) > 1: sys.exit(cli_main()) - usage(os.path.basename(unicode_argv()[0])) - sys.exit(0); + sys.exit(gui_main()) diff --git a/DeDRM_calibre_plugin/DeDRM_plugin/config.py b/DeDRM_calibre_plugin/DeDRM_plugin/config.py index 1357b49..d474257 100644 --- a/DeDRM_calibre_plugin/DeDRM_plugin/config.py +++ b/DeDRM_calibre_plugin/DeDRM_plugin/config.py @@ -109,14 +109,15 @@ class ConfigWidget(QWidget): self.ereader_button.setToolTip(_(u"Click to manage keys for eReader ebooks")) self.ereader_button.setText(u"eReader ebooks") self.ereader_button.clicked.connect(self.ereader_keys) + + button_layout.addWidget(self.adept_button) + button_layout.addWidget(self.kindle_key_button) button_layout.addWidget(self.kindle_serial_button) button_layout.addWidget(self.kindle_android_button) button_layout.addWidget(self.bandn_button) button_layout.addWidget(self.mobi_button) button_layout.addWidget(self.ereader_button) - button_layout.addWidget(self.adept_button) - button_layout.addWidget(self.kindle_key_button) - + self.resize(self.sizeHint()) def kindle_serials(self): @@ -124,7 +125,7 @@ class ConfigWidget(QWidget): d.exec_() def kindle_android_serials(self): - d = ManageKeysDialog(self,u"Kindle for Andoid Serial Number",self.tempdedrmprefs['androidserials'], AddAndroidSerialDialog, 'ab') + d = ManageKeysDialog(self,u"Kindle for Andoid",self.tempdedrmprefs['androidserials'], AddAndroidSerialDialog, 'ab') d.exec_() def kindle_keys(self): @@ -904,7 +905,7 @@ class AddAndroidSerialDialog(QDialog): data_group_box_layout.addLayout(key_group) key_group.addWidget(QLabel(u"Kindle for Android Serial Number:", self)) self.key_ledit = QLineEdit("", self) - self.key_ledit.setToolTip(u"Enter a Kindle for ANdroid serial number. These can be found using the androidkindlekey.py script.") + self.key_ledit.setToolTip(u"Enter a Kindle for Android serial number. These can be found using the androidkindlekey.py script.") key_group.addWidget(self.key_ledit) key_label = QLabel(_(''), self) key_label.setAlignment(Qt.AlignHCenter) diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw index 5118c87..83bb2ce 100644 --- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw +++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw @@ -240,6 +240,7 @@ def gui_main(): import Tkinter import Tkconstants import tkMessageBox + import tkFileDialog import traceback except: return cli_main() diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw new file mode 100644 index 0000000..6e6aef7 --- /dev/null +++ b/Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw @@ -0,0 +1,446 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +# androidkindlekey.py +# Copyright © 2013-15 by Thom +# Some portions Copyright © 2010-15 by some_updates, Apprentice Alf and Apprentice Harper +# + +# Revision history: +# 1.0 - Android serial number extracted from AmazonSecureStorage.xml +# 1.1 - Fixes and enhancements of some kind +# 1.2 - Changed to be callable from AppleScript by returning only serial number +# - and changed name to androidkindlekey.py +# - and added in unicode command line support +# 1.3 - added in TkInter interface, output to a file and attempt to get backup from a connected android device. + +""" +Retrieve Kindle for Android Serial Number. +""" + +__license__ = 'GPL v3' +__version__ = '1.3' + +import os +import sys +import getopt +import tempfile +import zlib +import tarfile +from hashlib import md5 +from cStringIO import StringIO +from binascii import a2b_hex, b2a_hex + +# Routines common to Mac and PC + +# Wrap a stream so that output gets flushed immediately +# and also make sure that any unicode strings get +# encoded using "replace" before writing them. +class SafeUnbuffered: + def __init__(self, stream): + self.stream = stream + self.encoding = stream.encoding + if self.encoding == None: + self.encoding = "utf-8" + def write(self, data): + if isinstance(data,unicode): + data = data.encode(self.encoding,"replace") + self.stream.write(data) + self.stream.flush() + def __getattr__(self, attr): + return getattr(self.stream, attr) + +try: + from calibre.constants import iswindows, isosx +except: + iswindows = sys.platform.startswith('win') + isosx = sys.platform.startswith('darwin') + +def unicode_argv(): + if iswindows: + # Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode + # strings. + + # Versions 2.x of Python don't support Unicode in sys.argv on + # Windows, with the underlying Windows API instead replacing multi-byte + # characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv + # as a list of Unicode strings and encode them as utf-8 + + from ctypes import POINTER, byref, cdll, c_int, windll + from ctypes.wintypes import LPCWSTR, LPWSTR + + GetCommandLineW = cdll.kernel32.GetCommandLineW + GetCommandLineW.argtypes = [] + GetCommandLineW.restype = LPCWSTR + + CommandLineToArgvW = windll.shell32.CommandLineToArgvW + CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)] + CommandLineToArgvW.restype = POINTER(LPWSTR) + + cmd = GetCommandLineW() + argc = c_int(0) + argv = CommandLineToArgvW(cmd, byref(argc)) + if argc.value > 0: + # Remove Python executable and commands if present + start = argc.value - len(sys.argv) + return [argv[i] for i in + xrange(start, argc.value)] + # if we don't have any arguments at all, just pass back script name + # this should never happen + return [u"kindlekey.py"] + else: + argvencoding = sys.stdin.encoding + if argvencoding == None: + argvencoding = "utf-8" + return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv] + +class DrmException(Exception): + pass + +STORAGE = u"backup.ab" +STORAGE1 = u"AmazonSecureStorage.xml" +STORAGE2 = u"map_data_storage.db" + +class AndroidObfuscation(object): + '''AndroidObfuscation + For the key, it's written in java, and run in android dalvikvm + ''' + + key = a2b_hex('0176e04c9408b1702d90be333fd53523') + + def encrypt(self, plaintext): + cipher = self._get_cipher() + padding = len(self.key) - len(plaintext) % len(self.key) + plaintext += chr(padding) * padding + return b2a_hex(cipher.encrypt(plaintext)) + + def decrypt(self, ciphertext): + cipher = self._get_cipher() + plaintext = cipher.decrypt(a2b_hex(ciphertext)) + return plaintext[:-ord(plaintext[-1])] + + def _get_cipher(self): + try: + from Crypto.Cipher import AES + return AES.new(self.key) + except ImportError: + from aescbc import AES, noPadding + return AES(self.key, padding=noPadding()) + +class AndroidObfuscationV2(AndroidObfuscation): + '''AndroidObfuscationV2 + ''' + + count = 503 + password = 'Thomsun was here!' + + def __init__(self, salt): + key = self.password + salt + for _ in range(self.count): + key = md5(key).digest() + self.key = key[:8] + self.iv = key[8:16] + + def _get_cipher(self): + try : + from Crypto.Cipher import DES + return DES.new(self.key, DES.MODE_CBC, self.iv) + except ImportError: + from python_des import Des, CBC + return Des(self.key, CBC, self.iv) + +def parse_preference(path): + ''' parse android's shared preference xml ''' + storage = {} + read = open(path) + for line in read: + line = line.strip() + # value + if line.startswith(' adb backup com.amazon.kindle + or from individual files if they're passed. + ''' + if not os.path.isfile(path): + return [] + + basename = os.path.basename(path) + if basename == STORAGE1: + return get_serials1(path) + elif basename == STORAGE2: + return get_serials2(path) + + output = None + try : + read = open(path, 'rb') + head = read.read(24) + if head[:14] == 'ANDROID BACKUP': + output = StringIO(zlib.decompress(read.read())) + except Exception: + pass + finally: + read.close() + + if not output: + return [] + + serials = [] + tar = tarfile.open(fileobj=output) + for member in tar.getmembers(): + if member.name.strip().endswith(STORAGE1): + write = tempfile.NamedTemporaryFile(mode='wb', delete=False) + write.write(tar.extractfile(member).read()) + write.close() + write_path = os.path.abspath(write.name) + serials.extend(get_serials1(write_path)) + os.remove(write_path) + elif member.name.strip().endswith(STORAGE2): + write = tempfile.NamedTemporaryFile(mode='wb', delete=False) + write.write(tar.extractfile(member).read()) + write.close() + write_path = os.path.abspath(write.name) + serials.extend(get_serials2(write_path)) + os.remove(write_path) + + return serials + +__all__ = [ 'get_serials', 'getkey'] + +# interface for Python DeDRM +# returns single key or multiple keys, depending on path or file passed in +def getkey(outpath, inpath): + keys = get_serials(inpath) + if len(keys) > 0: + if not os.path.isdir(outpath): + outfile = outpath + with file(outfile, 'w') as keyfileout: + keyfileout.write(keys[0]) + print u"Saved a key to {0}".format(outfile) + else: + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(outpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + print u"Saved a key to {0}".format(outfile) + return True + return False + + +def usage(progname): + print u"{0} v{1}\nCopyright © 2013-2015 Thom and Apprentice Harper".format(progname,__version__) + print u"Decrypts the serial number of Kindle For Android from Android backup or file" + print u"Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+." + print u"Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml" + print u"Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db" + print u"" + print u"Serial number is written to standard output." + print u"Usage:" + print u" {0:s} [-h] [-b ] []".format(progname) + + +def cli_main(): + sys.stdout=SafeUnbuffered(sys.stdout) + sys.stderr=SafeUnbuffered(sys.stderr) + argv=unicode_argv() + progname = os.path.basename(argv[0]) + print u"{0} v{1}\nCopyright © 2010-2015 Thom, some_updates, Apprentice Alf and Apprentice Harper".format(progname,__version__) + + try: + opts, args = getopt.getopt(argv[1:], "hb:") + except getopt.GetoptError, err: + usage(progname) + print u"\nError in options or arguments: {0}".format(err.args[0]) + return 2 + + inpath = "" + for o, a in opts: + if o == "-h": + usage(progname) + return 0 + if o == "-b": + inpath = a + + if len(args) > 1: + usage(progname) + return 2 + + if len(args) == 1: + # save to the specified file or directory + outpath = args[0] + if not os.path.isabs(outpath): + outpath = os.path.join(os.path.dirname(argv[0]),outpath) + outpath = os.path.abspath(outpath) + else: + # save to the same directory as the script + outpath = os.path.dirname(argv[0]) + + # make sure the outpath is OK + outpath = os.path.realpath(os.path.normpath(outpath)) + + if not os.path.isfile(inpath): + usage(progname) + print u"\n{0:s} file not found".format(inpath) + return 2 + + if not getkey(outpath, inpath): + print u"Could not retrieve Kindle for Android key." + return 0 + + +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + import tkFileDialog + import traceback + except: + print "Tkinter not installed" + return cli_main() + + class DecryptionDialog(Tkinter.Frame): + def __init__(self, root): + Tkinter.Frame.__init__(self, root, border=5) + self.status = Tkinter.Label(self, text=u"Select backup.ab file") + self.status.pack(fill=Tkconstants.X, expand=1) + body = Tkinter.Frame(self) + body.pack(fill=Tkconstants.X, expand=1) + sticky = Tkconstants.E + Tkconstants.W + body.grid_columnconfigure(1, weight=2) + Tkinter.Label(body, text=u"Backup file").grid(row=0, column=0) + self.keypath = Tkinter.Entry(body, width=40) + self.keypath.grid(row=0, column=1, sticky=sticky) + self.keypath.insert(2, u"backup.ab") + button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + button.grid(row=0, column=2) + buttons = Tkinter.Frame(self) + buttons.pack() + button2 = Tkinter.Button( + buttons, text=u"Extract", width=10, command=self.generate) + button2.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button3 = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button3.pack(side=Tkconstants.RIGHT) + + def get_keypath(self): + keypath = tkFileDialog.askopenfilename( + parent=None, title=u"Select backup.ab file", + defaultextension=u".ab", + filetypes=[('adb backup com.amazon.kindle', '.ab'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + self.keypath.delete(0, Tkconstants.END) + self.keypath.insert(0, keypath) + return + + def generate(self): + inpath = self.keypath.get() + self.status['text'] = u"Getting key..." + try: + keys = get_serials(inpath) + keycount = 0 + for key in keys: + while True: + keycount += 1 + outfile = os.path.join(progpath,u"kindlekey{0:d}.k4a".format(keycount)) + if not os.path.exists(outfile): + break + + with file(outfile, 'w') as keyfileout: + keyfileout.write(key) + success = True + tkMessageBox.showinfo(progname, u"Key successfully retrieved to {0}".format(outfile)) + except Exception, e: + self.status['text'] = u"Error: {0}".format(e.args[0]) + return + self.status['text'] = u"Select backup.ab file" + + argv=unicode_argv() + progpath, progname = os.path.split(argv[0]) + root = Tkinter.Tk() + root.title(u"Kindle for Android Key Extraction v.{0}".format(__version__)) + root.resizable(True, False) + root.minsize(300, 0) + DecryptionDialog(root).pack(fill=Tkconstants.X, expand=1) + root.mainloop() + return 0 + +if __name__ == '__main__': + if len(sys.argv) > 1: + sys.exit(cli_main()) + sys.exit(gui_main()) diff --git a/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.py b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw similarity index 100% rename from Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.py rename to Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw