From 036eaa683f670d6ea8dfbc21d3f9607cc5114f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?i=E2=99=A5cabbages?= Date: Wed, 30 Dec 2009 09:59:50 +0000 Subject: [PATCH] unswindle 6 --- Kindle_Mobi_Tools/unswindle.pyw | 57 +++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/Kindle_Mobi_Tools/unswindle.pyw b/Kindle_Mobi_Tools/unswindle.pyw index 6cb6aab..f04b1b6 100644 --- a/Kindle_Mobi_Tools/unswindle.pyw +++ b/Kindle_Mobi_Tools/unswindle.pyw @@ -1,6 +1,11 @@ #! /usr/bin/python +# -*- coding: utf-8 -*- -# unswindle.pyw, version 5 +# unswindle.pyw, version 6-rc1 +# Copyright © 2009 i♥cabbages + +# Released under the terms of the GNU General Public Licence, version 3 or +# later. # To run this program install a 32-bit version of Python 2.6 from # . Save this script file as unswindle.pyw. @@ -16,6 +21,7 @@ # 4 - Fix error opening threads; detect Topaz books; # detect unsupported versions of K4PC # 5 - Work with new (20091222) version of K4PC +# 6 - Detect and just copy DRM-free books """ Decrypt Kindle For PC encrypted Mobipocket books. @@ -636,7 +642,7 @@ class PC1KeyGrabber(object): def _i_like_wine(self, debugger, context): context.Eax = 1 return - + def _no_debugger_here(self, debugger, context): context.Eip += 2 context.Eax = 0 @@ -661,6 +667,7 @@ class PC1KeyGrabber(object): addr = debugger.read_process_memory(addr, type=ctypes.c_char_p) pid = debugger.read_process_memory(addr, 8) pid = self._checksum_pid(pid) + print pid self.book_pid = pid def _checksum_pid(self, s): @@ -676,6 +683,30 @@ class PC1KeyGrabber(object): crc >>= 8 return res +class MobiParser(object): + def __init__(self, data): + self.data = data + header = data[0:72] + if header[0x3C:0x3C+8] != 'BOOKMOBI': + raise UnswindleError("invalid file format") + self.nsections = nsections = struct.unpack('>H', data[76:78])[0] + self.sections = sections = [] + for i in xrange(nsections): + offset, a1, a2, a3, a4 = \ + struct.unpack('>LBBBB', data[78+i*8:78+i*8+8]) + flags, val = a1, ((a2 << 16) | (a3 << 8) | a4) + sections.append((offset, flags, val)) + sect = self.load_section(0) + self.crypto_type = struct.unpack('>H', sect[0x0c:0x0c+2])[0] + + def load_section(self, snum): + if (snum + 1) == self.nsections: + endoff = len(self.data) + else: + endoff = self.sections[snum + 1][0] + off = self.sections[snum][0] + return self.data[off:endoff] + class Unswindler(object): def __init__(self): self._exepath = self._get_exe_path() @@ -719,14 +750,19 @@ class Unswindler(object): if not PC1KeyGrabber.supported_version(hexdigest): raise UnswindleError("Unsupported version of Kindle For PC") return hexdigest - - def _is_topaz(self, path): + + def _check_topaz(self, path): with open(path, 'rb') as f: magic = f.read(4) if magic == 'TPZ0': return True return False + def _check_drm_free(self, path): + with open(path, 'rb') as f: + crypto = MobiParser(f.read()).crypto_type + return (crypto == 0) + def get_book(self): creation_flags = (CREATE_UNICODE_ENVIRONMENT | DEBUG_PROCESS | @@ -752,15 +788,22 @@ class Unswindler(object): CloseHandle(process_info.hProcess) if path is None: raise UnswindleError("failed to determine book path") - if self._is_topaz(path): + if self._check_topaz(path): raise UnswindleError("cannot decrypt Topaz format book") - if pid is None: - raise UnswindleError("failed to determine book PID") return (path, pid) def decrypt_book(self, inpath, outpath, pid): + if self._check_drm_free(inpath): + shutil.copy(inpath, outpath) + else: + self._mobidedrm(inpath, outpath, pid) + return + + def _mobidedrm(self, inpath, outpath, pid): # darkreverser didn't protect mobidedrm's script execution to allow # importing, so we have to just run it in a subprocess + if pid is None: + raise UnswindleError("failed to determine book PID") with tempfile.NamedTemporaryFile(delete=False) as tmpf: tmppath = tmpf.name args = [sys.executable, self._mobidedrmpath, inpath, tmppath, pid]