]> xmof Git - DeDRM.git/commitdiff
unswindle 6
authori♥cabbages <i♥cabbages@blogspot.co.uk>
Wed, 30 Dec 2009 09:59:50 +0000 (09:59 +0000)
committerApprentice Alf <apprenticealf@gmail.com>
Sat, 28 Feb 2015 10:00:28 +0000 (10:00 +0000)
Kindle_Mobi_Tools/unswindle.pyw

index 6cb6aab8c38678f960d891a6e0cfe4d6024a0068..f04b1b68960f667d3a974e934f406d8b35d01aa6 100644 (file)
@@ -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.  <http://www.gnu.org/licenses/>
 
 # To run this program install a 32-bit version of Python 2.6 from
 # <http://www.python.org/download/>.  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]