tools v5.4

This commit is contained in:
Apprentice Alf
2012-11-07 13:14:25 +00:00
parent 0028027f71
commit 0dcd18d524
119 changed files with 13790 additions and 8140 deletions

View File

@@ -1,899 +0,0 @@
#! /usr/bin/python
"""
Comprehensive Mazama Book DRM with Topaz Cryptography V2.2
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdBHJ4CNc6DNFCw4MRCw4SWAK6
M8hYfnNEI0yQmn5Ti+W8biT7EatpauE/5jgQMPBmdNrDr1hbHyHBSP7xeC2qlRWC
B62UCxeu/fpfnvNHDN/wPWWH4jynZ2M6cdcnE5LQ+FfeKqZn7gnG2No1U9h7oOHx
y2/pHuYme7U1TsgSjwIDAQAB
-----END PUBLIC KEY-----
"""
from __future__ import with_statement
import csv
import sys
import os
import getopt
import zlib
from struct import pack
from struct import unpack
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
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
import hashlib
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
global kindleDatabase
global bookFile
global bookPayloadOffset
global bookHeaderRecords
global bookMetadata
global bookKey
global command
#
# Various character maps used to decrypt books. Probably supposed to act as obfuscation
#
charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
charMap2 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_"
charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
#
# Exceptions for all the problems that might happen during the script
#
class CMBDTCError(Exception):
pass
class CMBDTCFatal(Exception):
pass
#
# Stolen stuff
#
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):
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()
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 CMBDTCFatal("Failed to Unprotect Data")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
#
# Returns the MD5 digest of "message"
#
def MD5(message):
ctx = hashlib.md5()
ctx.update(message)
return ctx.digest()
#
# Returns the MD5 digest of "message"
#
def SHA1(message):
ctx = hashlib.sha1()
ctx.update(message)
return ctx.digest()
#
# Open the book file at path
#
def openBook(path):
try:
return open(path,'rb')
except:
raise CMBDTCFatal("Could not open book file: " + path)
#
# 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),2):
high = map.find(data[i])
low = map.find(data[i+1])
value = (((high * 0x40) ^ 0x80) & 0xFF) + low
result += pack("B",value)
return result
#
# Locate and open the Kindle.info file (Hopefully in the way it is done in the Kindle application)
#
def openKindleInfo():
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
return open(path+'\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info','r')
#
# Parse the Kindle.info file and return the records as a list of key-values
#
def parseKindleInfo():
DB = {}
infoReader = openKindleInfo()
infoReader.read(1)
data = infoReader.read()
items = data.split('{')
for item in items:
splito = item.split(':')
DB[splito[0]] =splito[1]
return DB
#
# Find if the original string for a hashed/encoded string is known. If so return the original string othwise return an empty string. (Totally not optimal)
#
def findNameForHash(hash):
names = ["kindle.account.tokens","kindle.cookie.item","eulaVersionAccepted","login_date","kindle.token.item","login","kindle.key.item","kindle.name.info","kindle.device.info", "MazamaRandomNumber"]
result = ""
for name in names:
if hash == encodeHash(name, charMap2):
result = name
break
return name
#
# Print all the records from the kindle.info file (option -i)
#
def printKindleInfo():
for record in kindleDatabase:
name = findNameForHash(record)
if name != "" :
print (name)
print ("--------------------------\n")
else :
print ("Unknown Record")
print getKindleInfoValueForHash(record)
print "\n"
#
# Get a record from the Kindle.info file for the key "hashedKey" (already hashed and encoded). Return the decoded and decrypted record
#
def getKindleInfoValueForHash(hashedKey):
global kindleDatabase
encryptedValue = decode(kindleDatabase[hashedKey],charMap2)
return CryptUnprotectData(encryptedValue,"")
#
# Get a record from the Kindle.info file for the string in "key" (plaintext). Return the decoded and decrypted record
#
def getKindleInfoValueForKey(key):
return getKindleInfoValueForHash(encodeHash(key,charMap2))
#
# Get a 7 bit encoded number from the book file
#
def bookReadEncodedNumber():
flag = False
data = ord(bookFile.read(1))
if data == 0xFF:
flag = True
data = ord(bookFile.read(1))
if data >= 0x80:
datax = (data & 0x7F)
while data >= 0x80 :
data = ord(bookFile.read(1))
datax = (datax <<7) + (data & 0x7F)
data = datax
if flag:
data = -data
return data
#
# Encode a number in 7 bit format
#
def encodeNumber(number):
result = ""
negative = False
flag = 0
if number < 0 :
number = -number + 1
negative = True
while True:
byte = number & 0x7F
number = number >> 7
byte += flag
result += chr(byte)
flag = 0x80
if number == 0 :
if (byte == 0xFF and negative == False) :
result += chr(0x80)
break
if negative:
result += chr(0xFF)
return result[::-1]
#
# Get a length prefixed string from the file
#
def bookReadString():
stringLength = bookReadEncodedNumber()
return unpack(str(stringLength)+"s",bookFile.read(stringLength))[0]
#
# Returns a length prefixed string
#
def lengthPrefixString(data):
return encodeNumber(len(data))+data
#
# Read and return the data of one header record at the current book file position [[offset,compressedLength,decompressedLength],...]
#
def bookReadHeaderRecordData():
nbValues = bookReadEncodedNumber()
values = []
for i in range (0,nbValues):
values.append([bookReadEncodedNumber(),bookReadEncodedNumber(),bookReadEncodedNumber()])
return values
#
# Read and parse one header record at the current book file position and return the associated data [[offset,compressedLength,decompressedLength],...]
#
def parseTopazHeaderRecord():
if ord(bookFile.read(1)) != 0x63:
raise CMBDTCFatal("Parse Error : Invalid Header")
tag = bookReadString()
record = bookReadHeaderRecordData()
return [tag,record]
#
# Parse the header of a Topaz file, get all the header records and the offset for the payload
#
def parseTopazHeader():
global bookHeaderRecords
global bookPayloadOffset
magic = unpack("4s",bookFile.read(4))[0]
if magic != 'TPZ0':
raise CMBDTCFatal("Parse Error : Invalid Header, not a Topaz file")
nbRecords = bookReadEncodedNumber()
bookHeaderRecords = {}
for i in range (0,nbRecords):
result = parseTopazHeaderRecord()
bookHeaderRecords[result[0]] = result[1]
if ord(bookFile.read(1)) != 0x64 :
raise CMBDTCFatal("Parse Error : Invalid Header")
bookPayloadOffset = bookFile.tell()
#
# Get a record in the book payload, given its name and index. If necessary the record is decrypted. The record is not decompressed
#
def getBookPayloadRecord(name, index):
encrypted = False
try:
recordOffset = bookHeaderRecords[name][index][0]
except:
raise CMBDTCFatal("Parse Error : Invalid Record, record not found")
bookFile.seek(bookPayloadOffset + recordOffset)
tag = bookReadString()
if tag != name :
raise CMBDTCFatal("Parse Error : Invalid Record, record name doesn't match")
recordIndex = bookReadEncodedNumber()
if recordIndex < 0 :
encrypted = True
recordIndex = -recordIndex -1
if recordIndex != index :
raise CMBDTCFatal("Parse Error : Invalid Record, index doesn't match")
if bookHeaderRecords[name][index][2] != 0 :
record = bookFile.read(bookHeaderRecords[name][index][2])
else:
record = bookFile.read(bookHeaderRecords[name][index][1])
if encrypted:
ctx = topazCryptoInit(bookKey)
record = topazCryptoDecrypt(record,ctx)
return record
#
# Extract, decrypt and decompress a book record indicated by name and index and print it or save it in "filename"
#
def extractBookPayloadRecord(name, index, filename):
compressed = False
try:
compressed = bookHeaderRecords[name][index][2] != 0
record = getBookPayloadRecord(name,index)
except:
print("Could not find record")
if compressed:
try:
record = zlib.decompress(record)
except:
raise CMBDTCFatal("Could not decompress record")
if filename != "":
try:
file = open(filename,"wb")
file.write(record)
file.close()
except:
raise CMBDTCFatal("Could not write to destination file")
else:
print(record)
#
# return next record [key,value] from the book metadata from the current book position
#
def readMetadataRecord():
return [bookReadString(),bookReadString()]
#
# Parse the metadata record from the book payload and return a list of [key,values]
#
def parseMetadata():
global bookHeaderRecords
global bookPayloadAddress
global bookMetadata
bookMetadata = {}
bookFile.seek(bookPayloadOffset + bookHeaderRecords["metadata"][0][0])
tag = bookReadString()
if tag != "metadata" :
raise CMBDTCFatal("Parse Error : Record Names Don't Match")
flags = ord(bookFile.read(1))
nbRecords = ord(bookFile.read(1))
for i in range (0,nbRecords) :
record =readMetadataRecord()
bookMetadata[record[0]] = record[1]
#
# Returns two bit at offset from a bit field
#
def getTwoBitsFromBitField(bitField,offset):
byteNumber = offset // 4
bitPosition = 6 - 2*(offset % 4)
return ord(bitField[byteNumber]) >> bitPosition & 3
#
# Returns the six bits at offset from a bit field
#
def getSixBitsFromBitField(bitField,offset):
offset *= 3
value = (getTwoBitsFromBitField(bitField,offset) <<4) + (getTwoBitsFromBitField(bitField,offset+1) << 2) +getTwoBitsFromBitField(bitField,offset+2)
return value
#
# 8 bits to six bits encoding from hash to generate PID string
#
def encodePID(hash):
global charMap3
PID = ""
for position in range (0,8):
PID += charMap3[getSixBitsFromBitField(hash,position)]
return PID
#
# Context initialisation for the Topaz Crypto
#
def topazCryptoInit(key):
ctx1 = 0x0CAFFE19E
for keyChar in key:
keyByte = ord(keyChar)
ctx2 = ctx1
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
return [ctx1,ctx2]
#
# decrypt data with the context prepared by topazCryptoInit()
#
def topazCryptoDecrypt(data, ctx):
ctx1 = ctx[0]
ctx2 = ctx[1]
plainText = ""
for dataChar in data:
dataByte = ord(dataChar)
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
ctx2 = ctx1
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
plainText += chr(m)
return plainText
#
# Decrypt a payload record with the PID
#
def decryptRecord(data,PID):
ctx = topazCryptoInit(PID)
return topazCryptoDecrypt(data, ctx)
#
# Try to decrypt a dkey record (contains the book PID)
#
def decryptDkeyRecord(data,PID):
record = decryptRecord(data,PID)
fields = unpack("3sB8sB8s3s",record)
if fields[0] != "PID" or fields[5] != "pid" :
raise CMBDTCError("Didn't find PID magic numbers in record")
elif fields[1] != 8 or fields[3] != 8 :
raise CMBDTCError("Record didn't contain correct length fields")
elif fields[2] != PID :
raise CMBDTCError("Record didn't contain PID")
return fields[4]
#
# Decrypt all the book's dkey records (contain the book PID)
#
def decryptDkeyRecords(data,PID):
nbKeyRecords = ord(data[0])
records = []
data = data[1:]
for i in range (0,nbKeyRecords):
length = ord(data[0])
try:
key = decryptDkeyRecord(data[1:length+1],PID)
records.append(key)
except CMBDTCError:
pass
data = data[1+length:]
return records
#
# Encryption table used to generate the device PID
#
def generatePidEncryptionTable() :
table = []
for counter1 in range (0,0x100):
value = counter1
for counter2 in range (0,8):
if (value & 1 == 0) :
value = value >> 1
else :
value = value >> 1
value = value ^ 0xEDB88320
table.append(value)
return table
#
# Seed value used to generate the device PID
#
def generatePidSeed(table,dsn) :
value = 0
for counter in range (0,4) :
index = (ord(dsn[counter]) ^ value) &0xFF
value = (value >> 8) ^ table[index]
return value
#
# Generate the device PID
#
def generateDevicePID(table,dsn,nbRoll):
seed = generatePidSeed(table,dsn)
pidAscii = ""
pid = [(seed >>24) &0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF,(seed>>24) & 0xFF,(seed >> 16) &0xff,(seed >> 8) &0xFF,(seed) & 0xFF]
index = 0
for counter in range (0,nbRoll):
pid[index] = pid[index] ^ ord(dsn[counter])
index = (index+1) %8
for counter in range (0,8):
index = ((((pid[counter] >>5) & 3) ^ pid[counter]) & 0x1f) + (pid[counter] >> 7)
pidAscii += charMap4[index]
return pidAscii
#
# Create decrypted book payload
#
def createDecryptedPayload(payload):
# store data to be able to create the header later
headerData= []
currentOffset = 0
# Add social DRM to decrypted files
try:
data = getKindleInfoValueForKey("kindle.name.info")+":"+ getKindleInfoValueForKey("login")
if payload!= None:
payload.write(lengthPrefixString("sdrm"))
payload.write(encodeNumber(0))
payload.write(data)
else:
currentOffset += len(lengthPrefixString("sdrm"))
currentOffset += len(encodeNumber(0))
currentOffset += len(data)
except:
pass
for headerRecord in bookHeaderRecords:
name = headerRecord
newRecord = []
if name != "dkey" :
for index in range (0,len(bookHeaderRecords[name])) :
offset = currentOffset
if payload != None:
# write tag
payload.write(lengthPrefixString(name))
# write data
payload.write(encodeNumber(index))
payload.write(getBookPayloadRecord(name, index))
else :
currentOffset += len(lengthPrefixString(name))
currentOffset += len(encodeNumber(index))
currentOffset += len(getBookPayloadRecord(name, index))
newRecord.append([offset,bookHeaderRecords[name][index][1],bookHeaderRecords[name][index][2]])
headerData.append([name,newRecord])
return headerData
#
# Create decrypted book
#
def createDecryptedBook(outputFile):
outputFile = open(outputFile,"wb")
# Write the payload in a temporary file
headerData = createDecryptedPayload(None)
outputFile.write("TPZ0")
outputFile.write(encodeNumber(len(headerData)))
for header in headerData :
outputFile.write(chr(0x63))
outputFile.write(lengthPrefixString(header[0]))
outputFile.write(encodeNumber(len(header[1])))
for numbers in header[1] :
outputFile.write(encodeNumber(numbers[0]))
outputFile.write(encodeNumber(numbers[1]))
outputFile.write(encodeNumber(numbers[2]))
outputFile.write(chr(0x64))
createDecryptedPayload(outputFile)
outputFile.close()
#
# Set the command to execute by the programm according to cmdLine parameters
#
def setCommand(name) :
global command
if command != "" :
raise CMBDTCFatal("Invalid command line parameters")
else :
command = name
#
# Program usage
#
def usage():
print("\nUsage:")
print("\nCMBDTC.py [options] bookFileName\n")
print("-p Adds a PID to the list of PIDs that are tried to decrypt the book key (can be used several times)")
print("-d Saves a decrypted copy of the book")
print("-r Prints or writes to disk a record indicated in the form name:index (e.g \"img:0\")")
print("-o Output file name to write records and decrypted books")
print("-v Verbose (can be used several times)")
print("-i Prints kindle.info database")
#
# Main
#
def main(argv=sys.argv):
global kindleDatabase
global bookMetadata
global bookKey
global bookFile
global command
progname = os.path.basename(argv[0])
verbose = 0
recordName = ""
recordIndex = 0
outputFile = ""
PIDs = []
kindleDatabase = None
command = ""
try:
opts, args = getopt.getopt(sys.argv[1:], "vdir:o:p:")
except getopt.GetoptError, err:
# print help information and exit:
print str(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
if len(opts) == 0 and len(args) == 0 :
usage()
sys.exit(2)
for o, a in opts:
if o == "-v":
verbose+=1
if o == "-i":
setCommand("printInfo")
if o =="-o":
if a == None :
raise CMBDTCFatal("Invalid parameter for -o")
outputFile = a
if o =="-r":
setCommand("printRecord")
try:
recordName,recordIndex = a.split(':')
except:
raise CMBDTCFatal("Invalid parameter for -r")
if o =="-p":
PIDs.append(a)
if o =="-d":
setCommand("doit")
if command == "" :
raise CMBDTCFatal("No action supplied on command line")
#
# Read the encrypted database
#
try:
kindleDatabase = parseKindleInfo()
except Exception, message:
if verbose>0:
print(message)
if kindleDatabase != None :
if command == "printInfo" :
printKindleInfo()
#
# Compute the DSN
#
# Get the Mazama Random number
MazamaRandomNumber = getKindleInfoValueForKey("MazamaRandomNumber")
# Get the HDD serial
encodedSystemVolumeSerialNumber = encodeHash(str(GetVolumeSerialNumber(GetSystemDirectory().split('\\')[0] + '\\')),charMap1)
# Get the current user name
encodedUsername = encodeHash(GetUserName(),charMap1)
# concat, hash and encode
DSN = encode(SHA1(MazamaRandomNumber+encodedSystemVolumeSerialNumber+encodedUsername),charMap1)
if verbose >1:
print("DSN: " + DSN)
#
# Compute the device PID
#
table = generatePidEncryptionTable()
devicePID = generateDevicePID(table,DSN,4)
PIDs.append(devicePID)
if verbose > 0:
print("Device PID: " + devicePID)
#
# Open book and parse metadata
#
if len(args) == 1:
bookFile = openBook(args[0])
parseTopazHeader()
parseMetadata()
#
# Compute book PID
#
# Get the account token
if kindleDatabase != None:
kindleAccountToken = getKindleInfoValueForKey("kindle.account.tokens")
if verbose >1:
print("Account Token: " + kindleAccountToken)
keysRecord = bookMetadata["keys"]
keysRecordRecord = bookMetadata[keysRecord]
pidHash = SHA1(DSN+kindleAccountToken+keysRecord+keysRecordRecord)
bookPID = encodePID(pidHash)
PIDs.append(bookPID)
if verbose > 0:
print ("Book PID: " + bookPID )
#
# Decrypt book key
#
dkey = getBookPayloadRecord('dkey', 0)
bookKeys = []
for PID in PIDs :
bookKeys+=decryptDkeyRecords(dkey,PID)
if len(bookKeys) == 0 :
if verbose > 0 :
print ("Book key could not be found. Maybe this book is not registered with this device.")
else :
bookKey = bookKeys[0]
if verbose > 0:
print("Book key: " + bookKey.encode('hex'))
if command == "printRecord" :
extractBookPayloadRecord(recordName,int(recordIndex),outputFile)
if outputFile != "" and verbose>0 :
print("Wrote record to file: "+outputFile)
elif command == "doit" :
if outputFile!="" :
createDecryptedBook(outputFile)
if verbose >0 :
print ("Decrypted book saved. Don't pirate!")
elif verbose > 0:
print("Output file name was not supplied.")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -20,7 +20,7 @@ class ConfigWidget(QWidget):
self.l = QVBoxLayout()
self.setLayout(self.l)
self.serialLabel = QLabel('Kindle Serial numbers (separate with commas, no spaces)')
self.serialLabel = QLabel('eInk Kindle Serial numbers (First character B, 16 characters, use commas if more than one)')
self.l.addWidget(self.serialLabel)
self.serials = QLineEdit(self)
@@ -28,7 +28,7 @@ class ConfigWidget(QWidget):
self.l.addWidget(self.serials)
self.serialLabel.setBuddy(self.serials)
self.pidLabel = QLabel('Mobipocket PIDs (separate with commas, no spaces)')
self.pidLabel = QLabel('Mobipocket PIDs (8 or 10 characters, use commas if more than one)')
self.l.addWidget(self.pidLabel)
self.pids = QLineEdit(self)
@@ -50,8 +50,8 @@ class ConfigWidget(QWidget):
self.wpLabel.setBuddy(self.wineprefix)
def save_settings(self):
prefs['pids'] = str(self.pids.text())
prefs['serials'] = str(self.serials.text())
prefs['pids'] = str(self.pids.text()).replace(" ","")
prefs['serials'] = str(self.serials.text()).replace(" ","")
winepref=str(self.wineprefix.text())
if winepref.strip() != '':
prefs['WINEPREFIX'] = winepref

View File

@@ -2,7 +2,7 @@
from __future__ import with_statement
# ignobleepub.pyw, version 3.4
# ignobleepub.pyw, version 3.5
# To run this program install Python 2.6 from <http://www.python.org/download/>
# and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
@@ -17,6 +17,7 @@ from __future__ import with_statement
# 3.2 - add support for encoding to 'utf-8' when building up list of files to cecrypt from encryption.xml
# 3.3 - On Windows try PyCrypto first and OpenSSL next
# 3.4 - Modify interace to allow use with import
# 3.5 - Fix for potential problem with PyCrypto
__license__ = 'GPL v3'
@@ -100,7 +101,7 @@ def _load_crypto_pycrypto():
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC)
self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)
@@ -143,7 +144,7 @@ class ZipInfo(zipfile.ZipInfo):
class Decryptor(object):
def __init__(self, bookkey, encryption):
enc = lambda tag: '{%s}%s' % (NSMAP['enc'], tag)
# self._aes = AES.new(bookkey, AES.MODE_CBC)
# self._aes = AES.new(bookkey, AES.MODE_CBC, '\x00'*16)
self._aes = AES(bookkey)
encryption = etree.fromstring(encryption)
self._encrypted = encrypted = set()
@@ -271,7 +272,7 @@ def decryptBook(keypath, inpath, outpath):
with open(keypath, 'rb') as f:
keyb64 = f.read()
key = keyb64.decode('base64')[:16]
# aes = AES.new(key, AES.MODE_CBC)
# aes = AES.new(key, AES.MODE_CBC, '\x00'*16)
aes = AES(key)
with closing(ZipFile(open(inpath, 'rb'))) as inf:

View File

@@ -2,7 +2,7 @@
from __future__ import with_statement
# ignoblekeygen.pyw, version 2.3
# ignoblekeygen.pyw, version 2.4
# To run this program install Python 2.6 from <http://www.python.org/download/>
# and OpenSSL or PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
@@ -15,6 +15,7 @@ from __future__ import with_statement
# 2.1 - Allow Windows versions of libcrypto to be found
# 2.2 - On Windows try PyCrypto first and then OpenSSL next
# 2.3 - Modify interface to allow use of import
# 2.4 - Improvements to UI and now works in plugins
"""
Generate Barnes & Noble EPUB user key from name and credit card number.
@@ -25,10 +26,6 @@ __license__ = 'GPL v3'
import sys
import os
import hashlib
import Tkinter
import Tkconstants
import tkFileDialog
import tkMessageBox
@@ -124,8 +121,10 @@ def normalize_name(name):
def generate_keyfile(name, ccn, outpath):
# remove spaces and case from name and CC numbers.
name = normalize_name(name) + '\x00'
ccn = ccn + '\x00'
ccn = normalize_name(ccn) + '\x00'
name_sha = hashlib.sha1(name).digest()[:16]
ccn_sha = hashlib.sha1(ccn).digest()[:16]
both_sha = hashlib.sha1(name + ccn).digest()
@@ -137,69 +136,6 @@ def generate_keyfile(name, ccn, outpath):
return userkey
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
self.status = Tkinter.Label(self, text='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='Name').grid(row=1)
self.name = Tkinter.Entry(body, width=30)
self.name.grid(row=1, column=1, sticky=sticky)
Tkinter.Label(body, text='CC#').grid(row=2)
self.ccn = Tkinter.Entry(body, width=30)
self.ccn.grid(row=2, column=1, sticky=sticky)
Tkinter.Label(body, text='Output file').grid(row=0)
self.keypath = Tkinter.Entry(body, width=30)
self.keypath.grid(row=0, column=1, sticky=sticky)
self.keypath.insert(0, 'bnepubkey.b64')
button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
buttons, text="Generate", width=10, command=self.generate)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.asksaveasfilename(
parent=None, title='Select B&N EPUB key file to produce',
defaultextension='.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):
name = self.name.get()
ccn = self.ccn.get()
keypath = self.keypath.get()
if not name:
self.status['text'] = 'Name not specified'
return
if not ccn:
self.status['text'] = 'Credit card number not specified'
return
if not keypath:
self.status['text'] = 'Output keyfile path not specified'
return
self.status['text'] = 'Generating...'
try:
generate_keyfile(name, ccn, keypath)
except Exception, e:
self.status['text'] = 'Error: ' + str(e)
return
self.status['text'] = 'Keyfile successfully generated'
def cli_main(argv=sys.argv):
@@ -218,6 +154,75 @@ def cli_main(argv=sys.argv):
def gui_main():
import Tkinter
import Tkconstants
import tkFileDialog
import tkMessageBox
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
self.status = Tkinter.Label(self, text='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='Account Name').grid(row=0)
self.name = Tkinter.Entry(body, width=40)
self.name.grid(row=0, column=1, sticky=sticky)
Tkinter.Label(body, text='CC#').grid(row=1)
self.ccn = Tkinter.Entry(body, width=40)
self.ccn.grid(row=1, column=1, sticky=sticky)
Tkinter.Label(body, text='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, 'bnepubkey.b64')
button = Tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=2, column=2)
buttons = Tkinter.Frame(self)
buttons.pack()
botton = Tkinter.Button(
buttons, text="Generate", width=10, command=self.generate)
botton.pack(side=Tkconstants.LEFT)
Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
button = Tkinter.Button(
buttons, text="Quit", width=10, command=self.quit)
button.pack(side=Tkconstants.RIGHT)
def get_keypath(self):
keypath = tkFileDialog.asksaveasfilename(
parent=None, title='Select B&N EPUB key file to produce',
defaultextension='.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):
name = self.name.get()
ccn = self.ccn.get()
keypath = self.keypath.get()
if not name:
self.status['text'] = 'Name not specified'
return
if not ccn:
self.status['text'] = 'Credit card number not specified'
return
if not keypath:
self.status['text'] = 'Output keyfile path not specified'
return
self.status['text'] = 'Generating...'
try:
generate_keyfile(name, ccn, keypath)
except Exception, e:
self.status['text'] = 'Error: ' + str(e)
return
self.status['text'] = 'Keyfile successfully generated'
root = Tkinter.Tk()
if AES is None:
root.withdraw()

View File

@@ -30,6 +30,8 @@ from __future__ import with_statement
# 5.4 - add support for encoding to 'utf-8' when building up list of files to decrypt from encryption.xml
# 5.5 - On Windows try PyCrypto first, OpenSSL next
# 5.6 - Modify interface to allow use with import
# 5.7 - Fix for potential problem with PyCrypto
"""
Decrypt Adobe ADEPT-encrypted EPUB books.
"""
@@ -235,7 +237,7 @@ def _load_crypto_pycrypto():
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC)
self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)

View File

@@ -3,7 +3,7 @@
from __future__ import with_statement
# ineptkey.pyw, version 5.4
# ineptkey.pyw, version 5.6
# Copyright © 2009-2010 i♥cabbages
# Released under the terms of the GNU General Public Licence, version 3 or
@@ -36,6 +36,8 @@ from __future__ import with_statement
# 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 - Revise to allow use in Plugins to eliminate need for duplicate code
"""
Retrieve Adobe ADEPT user key.
@@ -46,15 +48,17 @@ __license__ = 'GPL v3'
import sys
import os
import struct
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
class ADEPTError(Exception):
pass
if sys.platform.startswith('win'):
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, \
@@ -76,13 +80,13 @@ if sys.platform.startswith('win'):
_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',
@@ -110,7 +114,7 @@ if sys.platform.startswith('win'):
from Crypto.Cipher import AES as _AES
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC)
self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)
return AES
@@ -292,13 +296,9 @@ if sys.platform.startswith('win'):
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def retrieve_key(keypath):
def retrieve_keys():
if AES is None:
tkMessageBox.showerror(
"ADEPT Key",
"This script requires PyCrypto or OpenSSL which must be installed "
"separately. Read the top-of-script comment for details.")
return False
raise ADEPTError("PyCrypto or OpenSSL must be installed")
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
@@ -313,6 +313,7 @@ if sys.platform.startswith('win'):
device = winreg.QueryValueEx(regkey, 'key')[0]
keykey = CryptUnprotectData(device, entropy)
userkey = None
keys = []
try:
plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
except WindowsError:
@@ -334,50 +335,43 @@ if sys.platform.startswith('win'):
if ktype != 'privateLicenseKey':
continue
userkey = winreg.QueryValueEx(plkkey, 'value')[0]
break
if userkey is not None:
break
if userkey is None:
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')
userkey = userkey.decode('base64')
aes = AES(keykey)
userkey = aes.decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
with open(keypath, 'wb') as f:
f.write(userkey)
return True
return keys
elif sys.platform.startswith('darwin'):
elif isosx:
import xml.etree.ElementTree as etree
import Carbon.File
import Carbon.Folder
import Carbon.Folders
import MacOS
import subprocess
ACTIVATION_PATH = 'Adobe/Digital Editions/activation.dat'
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
def find_folder(domain, dtype):
try:
fsref = Carbon.Folder.FSFindFolder(domain, dtype, False)
return Carbon.File.pathname(fsref)
except MacOS.Error:
return None
def find_app_support_file(subpath):
dtype = Carbon.Folders.kApplicationSupportFolderType
for domain in Carbon.Folders.kUserDomain, Carbon.Folders.kLocalDomain:
path = find_folder(domain, dtype)
if path is None:
continue
path = os.path.join(path, subpath)
if os.path.isfile(path):
return path
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_key(keypath):
actpath = find_app_support_file(ACTIVATION_PATH)
def retrieve_keys():
actpath = findActivationDat()
if actpath is None:
raise ADEPTError("Could not locate ADE activation")
tree = etree.parse(actpath)
@@ -386,39 +380,18 @@ elif sys.platform.startswith('darwin'):
userkey = tree.findtext(expr)
userkey = userkey.decode('base64')
userkey = userkey[26:]
with open(keypath, 'wb') as f:
f.write(userkey)
return True
elif sys.platform.startswith('cygwin'):
def retrieve_key(keypath):
tkMessageBox.showerror(
"ADEPT Key",
"This script requires a Windows-native Python, and cannot be run "
"under Cygwin. Please install a Windows-native Python and/or "
"check your file associations.")
return False
return [userkey]
else:
def retrieve_key(keypath):
tkMessageBox.showerror(
"ADEPT Key",
"This script only supports Windows and Mac OS X. For Linux "
"you should be able to run ADE and this script under Wine (with "
"an appropriate version of Windows Python installed).")
return False
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
Tkinter.Frame.__init__(self, root, border=5)
label = Tkinter.Label(self, text="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)
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:
@@ -440,10 +413,27 @@ def cli_main(argv=sys.argv):
def main(argv=sys.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="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()
progname = os.path.basename(argv[0])
keypath = 'adeptkey.der'
keypath = os.path.abspath("adeptkey.der")
success = False
try:
success = retrieve_key(keypath)

View File

@@ -233,7 +233,7 @@ def GetVolumeSerialNumber():
def GetUserHomeAppSupKindleDirParitionName():
home = os.getenv('HOME')
dpath = home + '/Library/Application Support/Kindle'
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)
@@ -358,6 +358,10 @@ def isNewInstall():
# 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
@@ -491,22 +495,10 @@ class CryptUnprotectDataV3(object):
# Locate the .kindle-info files
def getKindleInfoFiles(kInfoFiles):
# first search for current .kindle-info files
home = os.getenv('HOME')
cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p1.communicate()
reslst = out1.split('\n')
kinfopath = 'NONE'
found = False
for resline in reslst:
if os.path.isfile(resline):
kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True
# add any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
home = os.getenv('HOME')
# search for any .kinf2011 files in new location (Sep 2012)
cmdline = 'find "' + home + '/Library/Containers/com.amazon.Kindle/Data/Library/Application Support" -name ".kinf2011"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p1.communicate()
@@ -514,9 +506,9 @@ def getKindleInfoFiles(kInfoFiles):
for resline in reslst:
if os.path.isfile(resline):
kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True
# add any .kinf2011 files
# search for any .kinf2011 files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kinf2011"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
@@ -527,6 +519,29 @@ def getKindleInfoFiles(kInfoFiles):
kInfoFiles.append(resline)
print('Found k4Mac kinf2011 file: ' + resline)
found = True
# search for any .kindle-info files
cmdline = 'find "' + home + '/Library/Application Support" -name ".kindle-info"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p1.communicate()
reslst = out1.split('\n')
kinfopath = 'NONE'
for resline in reslst:
if os.path.isfile(resline):
kInfoFiles.append(resline)
print('Found K4Mac kindle-info file: ' + resline)
found = True
# search for any .rainier*-kinf files
cmdline = 'find "' + home + '/Library/Application Support" -name ".rainier*-kinf"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p1 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p1.communicate()
reslst = out1.split('\n')
for resline in reslst:
if os.path.isfile(resline):
kInfoFiles.append(resline)
print('Found k4Mac kinf file: ' + resline)
found = True
if not found:
print('No k4Mac kindle-info/kinf/kinf2011 files have been found.')
return kInfoFiles

View File

@@ -204,45 +204,62 @@ CryptUnprotectData = CryptUnprotectData()
# Locate all of the kindle-info style files and return as list
def getKindleInfoFiles(kInfoFiles):
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\")
path = winreg.QueryValueEx(regkey, 'Local AppData')[0]
# some 64 bit machines do not have the proper registry key for some reason
# or the pythonn interface to the 32 vs 64 bit registry is broken
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]
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]
if not os.path.isdir(path):
path = ""
except RegError:
pass
except RegError:
pass
print('searching for kinfoFiles in ' + path)
found = False
# 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)
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.')

View File

@@ -1,68 +0,0 @@
# A simple implementation of pbkdf2 using stock python modules. See RFC2898
# for details. Basically, it derives a key from a password and salt.
# Copyright 2004 Matt Johnston <matt @ ucc asn au>
# Copyright 2009 Daniel Holth <dholth@fastmail.fm>
# This code may be freely used and modified for any purpose.
# Revision history
# v0.1 October 2004 - Initial release
# v0.2 8 March 2007 - Make usable with hashlib in Python 2.5 and use
# v0.3 "" the correct digest_size rather than always 20
# v0.4 Oct 2009 - Rescue from chandler svn, test and optimize.
import sys
import hmac
from struct import pack
try:
# only in python 2.5
import hashlib
sha = hashlib.sha1
md5 = hashlib.md5
sha256 = hashlib.sha256
except ImportError: # pragma: NO COVERAGE
# fallback
import sha
import md5
# this is what you want to call.
def pbkdf2( password, salt, itercount, keylen, hashfn = sha ):
try:
# depending whether the hashfn is from hashlib or sha/md5
digest_size = hashfn().digest_size
except TypeError: # pragma: NO COVERAGE
digest_size = hashfn.digest_size
# l - number of output blocks to produce
l = keylen / digest_size
if keylen % digest_size != 0:
l += 1
h = hmac.new( password, None, hashfn )
T = ""
for i in range(1, l+1):
T += pbkdf2_F( h, salt, itercount, i )
return T[0: keylen]
def xorstr( a, b ):
if len(a) != len(b):
raise ValueError("xorstr(): lengths differ")
return ''.join((chr(ord(x)^ord(y)) for x, y in zip(a, b)))
def prf( h, data ):
hm = h.copy()
hm.update( data )
return hm.digest()
# Helper as per the spec. h is a hmac which has been created seeded with the
# password, it will be copy()ed and not modified.
def pbkdf2_F( h, salt, itercount, blocknum ):
U = prf( h, salt + pack('>i',blocknum ) )
T = U
for i in range(2, itercount+1):
U = prf( h, U )
T = xorstr( T, U )
return T

View File

@@ -296,7 +296,7 @@ class TopazBook:
break
if not bookKey:
raise TpzDRMError('Decryption Unsucessful; No valid pid found')
raise TpzDRMError("Topaz Book. No key found in " + str(len(pidlst)) + " keys tried. Please report this failure for help.")
self.setBookKey(bookKey)
self.createBookDirectory()

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
import sys
import zlib
import zipfile
import zipfilerugged
import os
import os.path
import getopt
@@ -15,7 +15,7 @@ _FILENAME_OFFSET = 30
_MAX_SIZE = 64 * 1024
_MIMETYPE = 'application/epub+zip'
class ZipInfo(zipfile.ZipInfo):
class ZipInfo(zipfilerugged.ZipInfo):
def __init__(self, *args, **kwargs):
if 'compress_type' in kwargs:
compress_type = kwargs.pop('compress_type')
@@ -27,10 +27,14 @@ class fixZip:
self.ztype = 'zip'
if zinput.lower().find('.epub') >= 0 :
self.ztype = 'epub'
self.inzip = zipfile.ZipFile(zinput,'r')
self.outzip = zipfile.ZipFile(zoutput,'w')
print "opening input"
self.inzip = zipfilerugged.ZipFile(zinput,'r')
print "opening outout"
self.outzip = zipfilerugged.ZipFile(zoutput,'w')
print "opening input as raw file"
# open the input zip for reading only as a raw file
self.bzf = file(zinput,'rb')
print "finished initialising"
def getlocalname(self, zi):
local_header_offset = zi.header_offset
@@ -76,11 +80,11 @@ class fixZip:
data = None
# if not compressed we are good to go
if zi.compress_type == zipfile.ZIP_STORED:
if zi.compress_type == zipfilerugged.ZIP_STORED:
data = self.bzf.read(zi.file_size)
# if compressed we must decompress it using zlib
if zi.compress_type == zipfile.ZIP_DEFLATED:
if zi.compress_type == zipfilerugged.ZIP_DEFLATED:
cmpdata = self.bzf.read(zi.compress_size)
data = self.uncompress(cmpdata)
@@ -95,7 +99,7 @@ class fixZip:
# if epub write mimetype file first, with no compression
if self.ztype == 'epub':
nzinfo = ZipInfo('mimetype', compress_type=zipfile.ZIP_STORED)
nzinfo = ZipInfo('mimetype', compress_type=zipfilerugged.ZIP_STORED)
self.outzip.writestr(nzinfo, _MIMETYPE)
# write the rest of the files
@@ -105,7 +109,7 @@ class fixZip:
nzinfo = zinfo
try:
data = self.inzip.read(zinfo.filename)
except zipfile.BadZipfile or zipfile.error:
except zipfilerugged.BadZipfile or zipfilerugged.error:
local_name = self.getlocalname(zinfo)
data = self.getfiledata(zinfo)
nzinfo.filename = local_name