diff options
author | Apprentice Harper <[email protected]> | 2015-06-30 17:27:33 +0100 |
---|---|---|
committer | Apprentice Alf <[email protected]> | 2015-07-11 13:50:36 +0100 |
commit | 9a8d5f74a68536cd105ab08f640631f085490ec4 (patch) | |
tree | a2bf30affda2d01b730a0baee1b5e3dd57029a54 /Other_Tools | |
parent | e729ae890406c5bd13e3c8f9e36a5edc6c289640 (diff) |
Improvements to nook Study key retrieval, and addition of retrieval of nook keys via the internet.
Diffstat (limited to 'Other_Tools')
4 files changed, 521 insertions, 5 deletions
diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw index 4e9eead..dbadc5d 100644 --- a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw +++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw @@ -4,7 +4,7 @@ from __future__ import with_statement # ignoblekey.py -# Copyright © 2015 Apprentice Alf +# Copyright © 2015 Apprentice Alf and Apprentice Harper # Based on kindlekey.py, Copyright © 2010-2013 by some_updates and Apprentice Alf @@ -13,13 +13,14 @@ from __future__ import with_statement # Revision history: # 1.0 - Initial release +# 1.1 - remove duplicates and return last key as single key """ Get Barnes & Noble EPUB user key from nook Studio log file """ __license__ = 'GPL v3' -__version__ = "1.0" +__version__ = "1.1" import sys import os @@ -143,7 +144,7 @@ def getNookLogFiles(): paths.add(path) except WindowsError: pass - + for path in paths: # look for nookStudy log file logpath = path +'\\Barnes & Noble\\NOOKstudy\\logs\\BNClientLog.txt' @@ -199,7 +200,7 @@ def nookkeys(files = []): if fileKeys: print u"Found {0} keys in the Nook Study log files".format(len(fileKeys)) keys.extend(fileKeys) - return keys + return list(set(keys)) # interface for Python DeDRM # returns single key or multiple keys, depending on path or file passed in @@ -209,7 +210,7 @@ def getkey(outpath, files=[]): if not os.path.isdir(outpath): outfile = outpath with file(outfile, 'w') as keyfileout: - keyfileout.write(keys[0]) + keyfileout.write(keys[-1]) print u"Saved a key to {0}".format(outfile) else: keycount = 0 diff --git a/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw new file mode 100644 index 0000000..2ecbe96 --- /dev/null +++ b/Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw @@ -0,0 +1,239 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +# ignoblekeyfetch.pyw, version 1.0 +# Copyright © 2015 Apprentice Harper + +# Released under the terms of the GNU General Public Licence, version 3 +# <http://www.gnu.org/licenses/> + +# Based on discoveries by "Nobody You Know" + +# Windows users: Before running this program, you must first install Python. +# We recommend ActiveState Python 2.7.X for Windows from +# http://www.activestate.com/activepython/downloads. +# Then save this script file as ignoblekeyfetch.pyw and double-click on it to run it. +# +# Mac OS X users: Save this script file as ignoblekeyfetch.pyw. You can run this +# program from the command line (python ignoblekeygen.pyw) or by double-clicking +# it when it has been associated with PythonLauncher. + +# Revision history: +# 1.0 - Initial release + +""" +Fetch Barnes & Noble EPUB user key from B&N servers using email and password +""" + +__license__ = 'GPL v3' +__version__ = "1.0" + +import sys +import os + +# 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"ignoblekeygen.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 IGNOBLEError(Exception): + pass + +def fetch_key(email, password): + # remove spaces and case from name and CC numbers. + if type(email)==unicode: + email = email.encode('utf-8') + if type(password)==unicode: + password = password.encode('utf-8') + + import random + random = "%030x" % random.randrange(16**30) + + import urllib, urllib2 + fetch_url = "https://cart4.barnesandnoble.com/services/service.aspx?Version=2&acctPassword=" + fetch_url += urllib.quote(password,'')+"&devID=PC_BN_2.5.6.9575_"+random+"&emailAddress=" + fetch_url += urllib.quote(email,"")+"&outFormat=5&schema=1&service=1&stage=deviceHashB" + #print fetch_url + + found = '' + try: + req = urllib2.Request(fetch_url) + response = urllib2.urlopen(req) + the_page = response.read() + #print the_page + + import re + + found = re.search('ccHash>(.+?)</ccHash', the_page).group(1) + except: + found = '' + return found + + + + +def cli_main(): + sys.stdout=SafeUnbuffered(sys.stdout) + sys.stderr=SafeUnbuffered(sys.stderr) + argv=unicode_argv() + progname = os.path.basename(argv[0]) + if len(argv) != 4: + print u"usage: {0} <email> <password> <keyfileout.b64>".format(progname) + return 1 + email, password, keypath = argv[1:] + userkey = fetch_key(email, password) + if len(userkey) == 28: + open(keypath,'wb').write(userkey) + return 0 + print u"Failed to fetch key." + return 1 + + +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + import traceback + except: + 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"Enter parameters") + 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"Account email address").grid(row=0) + self.name = Tkinter.Entry(body, width=40) + self.name.grid(row=0, column=1, sticky=sticky) + Tkinter.Label(body, text=u"Account password").grid(row=1) + self.ccn = Tkinter.Entry(body, width=40) + self.ccn.grid(row=1, column=1, sticky=sticky) + Tkinter.Label(body, text=u"Output file").grid(row=2) + self.keypath = Tkinter.Entry(body, width=40) + self.keypath.grid(row=2, column=1, sticky=sticky) + self.keypath.insert(2, u"bnepubkey.b64") + button = Tkinter.Button(body, text=u"...", command=self.get_keypath) + button.grid(row=2, column=2) + buttons = Tkinter.Frame(self) + buttons.pack() + botton = Tkinter.Button( + buttons, text=u"Fetch", width=10, command=self.generate) + botton.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button.pack(side=Tkconstants.RIGHT) + + def get_keypath(self): + keypath = tkFileDialog.asksaveasfilename( + parent=None, title=u"Select B&N ePub key file to produce", + defaultextension=u".b64", + filetypes=[('base64-encoded files', '.b64'), + ('All Files', '.*')]) + if keypath: + keypath = os.path.normpath(keypath) + self.keypath.delete(0, Tkconstants.END) + self.keypath.insert(0, keypath) + return + + def generate(self): + email = self.name.get() + password = self.ccn.get() + keypath = self.keypath.get() + if not email: + self.status['text'] = u"Email address not given" + return + if not password: + self.status['text'] = u"Account password not given" + return + if not keypath: + self.status['text'] = u"Output keyfile path not set" + return + self.status['text'] = u"Fetching..." + try: + userkey = fetch_key(email, password) + except Exception, e: + self.status['text'] = u"Error: {0}".format(e.args[0]) + return + if len(userkey) == 28: + open(keypath,'wb').write(userkey) + self.status['text'] = u"Keyfile fetched successfully" + else: + self.status['text'] = u"Keyfile fetch failed." + + root = Tkinter.Tk() + root.title(u"Barnes & Noble ePub Keyfile Fetch 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/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_iOS/kindleiospidgen.pyw b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw new file mode 100644 index 0000000..0c0b11b --- /dev/null +++ b/Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw @@ -0,0 +1,275 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +# kindleforios4key.py +# Copyright © 2013 by Apprentice Alf +# Portions Copyright © 2007, 2009 Igor Skochinsky <[email protected]> + +# Revision history: +# 1.0 - Generates fixed PID for Kindle for iOS 3.1.1 running on iOS 4.x + + +""" +Generate fixed PID for Kindle for iOS 3.1.1 +""" + +__license__ = 'GPL v3' +__version__ = '1.0' + +import sys, os +import getopt +import binascii + +# 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) + +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)] + # if we don't have any arguments at all, just pass back script name + # this should never happen + return [u"mobidedrm.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] + +import hashlib + +def SHA256(message): + ctx = hashlib.sha256() + ctx.update(message) + return ctx.digest() + +def crc32(s): + return (~binascii.crc32(s,-1))&0xFFFFFFFF + +letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789' + +def checksumPid(s): + crc = crc32(s) + crc = crc ^ (crc >> 16) + res = s + l = len(letters) + for i in (0,1): + b = crc & 0xff + pos = (b // l) ^ (b % l) + res += letters[pos%l] + crc >>= 8 + + return res + +def pidFromSerial(s, l): + crc = crc32(s) + + arr1 = [0]*l + for i in xrange(len(s)): + arr1[i%l] ^= ord(s[i]) + + crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff] + for i in xrange(l): + arr1[i] ^= crc_bytes[i&3] + + pid = '' + for i in xrange(l): + b = arr1[i] & 0xff + pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))] + + return pid + +def generatekeys(email, mac): + keys = [] + email = email.encode('utf-8').lower() + mac = mac.encode('utf-8').lower() + cleanmac = "".join(c if (c in "0123456789abcdef") else "" for c in mac) + lowermac = cleanmac.lower() + #print lowermac + keyseed = lowermac + email.encode('utf-8') + #print keyseed + keysha256 = SHA256(keyseed) + keybase64 = keysha256.encode('base64') + #print keybase64 + cleankeybase64 = "".join(c if (c in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") else "0" for c in keybase64) + #print cleankeybase64 + pseudoudid = cleankeybase64[:40] + #print pseudoudid + keys.append(pidFromSerial(pseudoudid.encode("utf-8"),8)) + return keys + +# interface for Python DeDRM +# returns single key or multiple keys, depending on path or file passed in +def getkey(email, mac, outpath): + keys = generatekeys(email,mac) + 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"kindleios{0:d}.pid".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"Generates the key for Kindle for iOS 3.1.1" + print u"Requires email address of Amazon acccount" + print u"And MAC address for iOS device’s wifi" + print u"Outputs to a file or to stdout" + print u"Usage:" + print u" {0:s} [-h] <email address> <MAC address> [<outfile>]".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 © 2013 Apprentice Alf".format(progname,__version__) + + try: + opts, args = getopt.getopt(argv[1:], "h") + except getopt.GetoptError, err: + print u"Error in options or arguments: {0}".format(err.args[0]) + usage(progname) + sys.exit(2) + + for o, a in opts: + if o == "-h": + usage(progname) + sys.exit(0) + + + if len(args) < 2 or len(args) > 3: + usage(progname) + sys.exit(2) + + if len(args) == 3: + # save to the specified file or folder + getkey(args[0],args[1],args[2]) + else: + keys = generatekeys(args[0],args[1]) + for key in keys: + print key + + return 0 + + +def gui_main(): + try: + import Tkinter + import Tkconstants + import tkMessageBox + 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"Enter parameters") + 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"Amazon email address").grid(row=0) + self.email = Tkinter.Entry(body, width=40) + self.email.grid(row=0, column=1, sticky=sticky) + Tkinter.Label(body, text=u"iOS MAC address").grid(row=1) + self.mac = Tkinter.Entry(body, width=40) + self.mac.grid(row=1, column=1, sticky=sticky) + buttons = Tkinter.Frame(self) + buttons.pack() + button = Tkinter.Button( + buttons, text=u"Generate", width=10, command=self.generate) + button.pack(side=Tkconstants.LEFT) + Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT) + button = Tkinter.Button( + buttons, text=u"Quit", width=10, command=self.quit) + button.pack(side=Tkconstants.RIGHT) + + def generate(self): + email = self.email.get() + mac = self.mac.get() + if not email: + self.status['text'] = u"Email not specified" + return + if not mac: + self.status['text'] = u"MAC not specified" + return + self.status['text'] = u"Generating..." + try: + keys = generatekeys(email, mac) + except Exception, e: + self.status['text'] = u"Error: (0}".format(e.args[0]) + return + self.status['text'] = ", ".join(key for key in keys) + + root = Tkinter.Tk() + root.title(u"Kindle for iOS PID Generator 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()) |