# standlone set of Mac OSX specific routines needed for K4DeDRM from __future__ import with_statement import sys import os import subprocess class DrmException(Exception): pass # interface to needed routines in openssl's libcrypto def _load_crypto_libcrypto(): from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \ Structure, c_ulong, create_string_buffer, addressof, string_at, cast from ctypes.util import find_library libcrypto = find_library('crypto') if libcrypto is None: raise DrmException('libcrypto not found') libcrypto = CDLL(libcrypto) AES_MAXNR = 14 c_char_pp = POINTER(c_char_p) c_int_p = POINTER(c_int) class AES_KEY(Structure): _fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)] AES_KEY_p = POINTER(AES_KEY) def F(restype, name, argtypes): func = getattr(libcrypto, name) func.restype = restype func.argtypes = argtypes return func AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,c_int]) AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p]) PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1', [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p]) class LibCrypto(object): def __init__(self): self._blocksize = 0 self._keyctx = None self.iv = 0 def set_decrypt_key(self, userkey, iv): self._blocksize = len(userkey) if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) : raise DrmException('AES improper key used') return keyctx = self._keyctx = AES_KEY() self.iv = iv rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx) if rv < 0: raise DrmException('Failed to initialize AES key') def decrypt(self, data): out = create_string_buffer(len(data)) rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, self.iv, 0) if rv == 0: raise DrmException('AES decryption failed') return out.raw def keyivgen(self, passwd): salt = '16743' saltlen = 5 passlen = len(passwd) iter = 0x3e8 keylen = 80 out = create_string_buffer(keylen) rv = PKCS5_PBKDF2_HMAC_SHA1(passwd, passlen, salt, saltlen, iter, keylen, out) return out.raw return LibCrypto def _load_crypto(): LibCrypto = None try: LibCrypto = _load_crypto_libcrypto() except (ImportError, DrmException): pass return LibCrypto LibCrypto = _load_crypto() # # Utility Routines # # Various character maps used to decrypt books. Probably supposed to act as obfuscation charMap1 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M" charMap2 = "ZB0bYyc1xDdW2wEV3Ff7KkPpL8UuGA4gz-Tme9Nn_tHh5SvXCsIiR6rJjQaqlOoM" charMap3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" charMap4 = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789" # uses a sub process to get the Hard Drive Serial Number using ioreg # returns with the serial number of drive whose BSD Name is "disk0" def GetVolumeSerialNumber(): sernum = os.getenv('MYSERIALNUMBER') if sernum != None: return sernum cmdline = '/usr/sbin/ioreg -l -S -w 0 -r -c AppleAHCIDiskDriver' cmdline = cmdline.encode(sys.getfilesystemencoding()) p = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) out1, out2 = p.communicate() reslst = out1.split('\n') cnt = len(reslst) bsdname = None sernum = None foundIt = False for j in xrange(cnt): resline = reslst[j] pp = resline.find('"Serial Number" = "') if pp >= 0: sernum = resline[pp+19:-1] sernum = sernum.strip() bb = resline.find('"BSD Name" = "') if bb >= 0: bsdname = resline[bb+14:-1] bsdname = bsdname.strip() if (bsdname == 'disk0') and (sernum != None): foundIt = True break if not foundIt: sernum = '9999999999' return sernum # uses unix env to get username instead of using sysctlbyname def GetUserName(): username = os.getenv('USER') return username 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 import hashlib def SHA256(message): ctx = hashlib.sha256() ctx.update(message) return ctx.digest() # implements an Pseudo Mac Version of Windows built-in Crypto routine def CryptUnprotectData(encryptedData): sp = GetVolumeSerialNumber() + '!@#' + GetUserName() passwdData = encode(SHA256(sp),charMap1) crp = LibCrypto() key_iv = crp.keyivgen(passwdData) key = key_iv[0:32] iv = key_iv[32:48] crp.set_decrypt_key(key,iv) cleartext = crp.decrypt(encryptedData) return cleartext # Locate the .kindle-info files def getKindleInfoFiles(kInfoFiles): 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 cnt = len(reslst) for resline in reslst: if os.path.isfile(resline): kInfoFiles.append(resline) found = True if not found: print('No .kindle-info files have been found.') return kInfoFiles # Parse the Kindle.info file and return the records as a list of key-values def parseKindleInfo(kInfoFile): DB = {} infoReader = open(kInfoFile, 'r') infoReader.read(1) data = infoReader.read() items = data.split('[') for item in items: splito = item.split(':') DB[splito[0]] =splito[1] return DB