Amazon's KINDLE is cracked

Computer security issues, hacker & phreak news, conferences, Q&A, etc.

Computer chat, coding, reviews, hardware or software...

Share your infos!
Post Reply
User avatar
Stevyn
SysOp
Posts:1786
Joined:Mon Nov 09, 2009 10:03 am
Location:Japan
Contact:
Amazon's KINDLE is cracked

Post by Stevyn » Sat Dec 26, 2009 4:48 pm

source: http://translate.googleusercontent.com/ ... 7Rg8YQhOrA

Thursday, December 17, 2009
Circumventing Kindle For PC DRM (updated) Circumventing DRM Kindle For PC (updated)
Amazon actually put a bit effort behind the DRM obfuscation in their Kindle for PC application (K4PC). Amazon actually put a bit effort behind the DRM Obfuscation in their Kindle for PC application (K4PC). The Kindle proper and Kindle for iPhone/iPod app both use a single "device" encryption key for all DRMed content. The Kindle and Kindle for proper iPhone / iPod app both use a single "device" encryption key for all DRMed content. K4PC uses the same encryption algorithms, but ups the ante with a per-book session key for the actual en/decryption. K4PC uses the same encryption algorithms, but ups the ante with a per-book session key for the actual en / Decryption. And they seem to have done a reasonable job on the obfuscation. And they seem to have done a reasonable job on the Obfuscation. Way to go Amazon! Way to go Amazon! It's good enough that I got bored unwinding it all and just got lazy with the Windows debugging APIs instead. It's good enough that I got bored Unwinding it all and just got lazy with the Windows debugging APIs instead.

So here you go: unswindle v6-rc1 (previous versions: v5 v4 v3 ). So here you go: Unswindle v6-rc1 (previous versions: v5 v4 v3).

You'll also need a copy of darkreverser's mobidedrm (check the most recent comments for the newest links). You'll also need a copy of Darkreverser's Mobidedrm (check the most recent comments for the newest links).

Put those kids together (in the same directory) and run unswindle.pyw. Put those kids together (in the same directory) and run Unswindle.pyw. It launches KindleForPC.exe. It launches KindleForPC.exe. Pick the book you want to decrypt. Pick the book you want to decrypt. Close KindleForPC. Close KindleForPC. Pick your output file. Pick your output file. And enjoy the sweet taste of freedom. And enjoy the sweet taste of freedom.

Script name in honor of rms and The Right to Read . Script name in honor of rms and The Right to Read. Don't use this to steal, or I'm taking my toys and going home. Do not use this to steal, or I'm taking my toys and going home.

Updates . Updates. It came to my attention that unswindle version 1 did not work if KindleForPC was installed as a non-administrator and did not work on versions of Windows other than XP. It came to my attention that Unswindle version 1 did not work if KindleForPC was installed as a non-administrator and did not work on versions of Windows other than XP. Version 2 should fix these issues. Version 2 should fix these issues. Version 3 fixes an intermittent path-getting issue. Version 3 fixes an Intermittent path-getting issue. Version 4 fixes an exception related to opening thread handles, detect Topaz format books, and detects that you have the proper version of Kindle For PC installed. Version 4 fixes an exception related to opening thread handles, detect Topaz format books, and Detects that you have the proper version of Kindle For PC installed. Version 5 works with the new (20091222) version of the K4PC executable. Version 5 works with the new (20,091,222) version of the K4PC executable. Version 6 cleanly handles already DRM-free files. Version 6 Cleanly handles already DRM-free files.

Update 2009-12-22 . Update 2009-12-22. Amazon has demonstrated that they (unlike Adobe) take their DRM seriously: they've already pushed out a new version of K4PC which breaks this particular script. Amazon has Demonstrated that they (unlike Adobe) take their DRM seriously: they've already Pushed out a new version of K4PC which breaks this particular script. As you can clearly see via their SHA-1 hashes: As you can clearly see via their SHA-1 Hashes:

fd386003520f7af7a15d77fcc2b859dd53e44bc1 KindleForPC-installer-20091217.exe Fd386003520f7af7a15d77fcc2b859dd53e44bc1 KindleForPC-installer-20091217.exe

13a816a3abf7a71e7b6a55228099b03b1dc3789b KindleForPC-installer-20091222.exe 13a816a3abf7a71e7b6a55228099b03b1dc3789b KindleForPC-installer-20091222.exe

The application doesn't seem to auto-update, so if you can find a copy of the original installer you should be fine. The application does not seem to auto-update, so if you can find a copy of the original installer you should be fine. Otherwise you'll have to hang tight. Otherwise you'll have to hang tight. Newest unswindle version detects if you have the wrong K4PC executable installed. Detects Unswindle newest version if you have the wrong K4PC executable installed.

Update 2009-12-22 (2) . Update 2009-12-22 (2). The K4PC update may not actually have been targeted at unswindle, as Amazon seems to have done nothing in particular to make the basic approach more difficult. The K4PC update may not actually have been targeted at Unswindle, as Amazon seems to have done nothing in particular to make the basic approach more difficult. In any case, I've updated unswindle to handle the 20091222 version of the executable. In any case, I've updated Unswindle to handle the 20091222 version of the executable. We'll see if Amazon throws out another new build in short order, and I'll put some more elbow grease into figuring out the PID-generation algorithm. We'll see if Amazon throws out another new build in short order, and I'll put some more elbow grease into Figuring out the PID-generation algorithm.
Contact me directly: Ironfeatherbooks (@) gmail.com

Image

User avatar
Stevyn
SysOp
Posts:1786
Joined:Mon Nov 09, 2009 10:03 am
Location:Japan
Contact:

Re: Amazon's KINDLE is cracked

Post by Stevyn » Sat Dec 26, 2009 4:48 pm

Code: Select all

#! / usr / bin / python
# -*- Coding: utf-8 -*-

# Unswindle.pyw, version 6-rc1
# Copyright © 2009 i ♥ Cabbages

# Released under the terms of the GNU General Public License, 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.
# Find and save in the same directory a copy of Mobidedrm.py. Double-click on
# Unswindle.pyw. It will run Kindle For PC. Open the book you want to
# Decrypt. Close Kindle For PC. A dialog will open Allowing you to select the
# Output file. And you're done!

# Revision history:
# 1 - Initial release
# 2 - Fixes to work properly on Windows versions> XP
# 3 - Fix minor bug in path extraction
# 4 - Fix error opening threads; detect Topaz books;
# Detect unsupported versions of K4PC
# 5 - Work with new (20,091,222) version of K4PC
# 6 - Detect and just copy DRM-free books

' ""
For PC Kindle decrypt encrypted Mobipocket books.
' ""

__license__ = 'GPL v3'

import sys
import os
import re
import tempfile
import Shutil
import Subprocess
import struct
import Hashlib
import Ctypes
from Ctypes import *
from Ctypes.wintypes import *
import Binascii
import _winreg as Winreg
import Tkinter
import Tkconstants
import TkMessageBox
import TkFileDialog
import Traceback

#
# _extrawintypes.py

UBYTE = C_ubyte
ULONG_PTR = POINTER (ULONG)
PULONG = ULONG_PTR
PVOID = LPVOID
LPCTSTR = LPTSTR = C_wchar_p
LPBYTE = C_char_p
SIZE_T = C_uint
SIZE_T_p = POINTER (SIZE_T)

#
# _ntdll.py

NTSTATUS = DWORD

ntdll = Windll.ntdll

class PROCESS_BASIC_INFORMATION (Structure):
    _fields_ = [( 'Reserved1, PVOID)
                (PebBaseAddress, PVOID)
                (Reserved2, PVOID * 2),
                (UniqueProcessId, ULONG_PTR)
                (Reserved3, PVOID)]

# NTSTATUS WINAPI NtQueryInformationProcess (
# __in HANDLE ProcessHandle,
# __in PROCESSINFOCLASS ProcessInformationClass,
# __out PVOID ProcessInformation,
# __in ULONG ProcessInformationLength,
# __out_opt PULONG ReturnLength
#);
NtQueryInformationProcess = Ntdll.NtQueryInformationProcess
NtQueryInformationProcess.argtypes = [HANDLE, DWORD, PVOID, ULONG, PULONG]
NtQueryInformationProcess.restype = NTSTATUS

#
# _kernel32.py

INFINITE = 0xffffffff

CREATE_UNICODE_ENVIRONMENT = 0x00000400
DEBUG_ONLY_THIS_PROCESS = 0x00000002
DEBUG_PROCESS = 0x00000001

THREAD_GET_CONTEXT = 0x0008
THREAD_QUERY_INFORMATION = 0x0040
THREAD_SET_CONTEXT = 0x0010
THREAD_SET_INFORMATION = 0x0020

EXCEPTION_BREAKPOINT = 0x80000003
EXCEPTION_SINGLE_STEP = 0x80000004
EXCEPTION_ACCESS_VIOLATION = 0xC0000005

DBG_CONTINUE = 0x00010002L
DBG_EXCEPTION_NOT_HANDLED = 0x80010001L

EXCEPTION_DEBUG_EVENT = 1
CREATE_THREAD_DEBUG_EVENT = 2
CREATE_PROCESS_DEBUG_EVENT = 3
EXIT_THREAD_DEBUG_EVENT = 4
EXIT_PROCESS_DEBUG_EVENT = 5
LOAD_DLL_DEBUG_EVENT = 6
UNLOAD_DLL_DEBUG_EVENT = 7
OUTPUT_DEBUG_STRING_EVENT = 8
RIP_EVENT = 9

class DataBlob (Structure):
    _fields_ = [( 'CbData, C_uint)
                (PbData, C_void_p)]
DataBlob_p = POINTER (DataBlob)

class SECURITY_ATTRIBUTES (Structure):
    _fields_ = [( 'NLength, DWORD),
                (LpSecurityDescriptor, LPVOID)
                (BInheritHandle, BOOL)]
LPSECURITY_ATTRIBUTES = POINTER (SECURITY_ATTRIBUTES)

class STARTUPINFO (Structure):
    _fields_ = [( 'cb', DWORD),
                (LpReserved, LPTSTR),
                (LpDesktop, LPTSTR),
                (LpTitle, LPTSTR),
                (DwX, DWORD),
                (DwY, DWORD),
                (DwXSize, DWORD),
                (DwYSize, DWORD),
                (DwXCountChars, DWORD),
                (DwYCountChars, DWORD),
                (DwFillAttribute, DWORD),
                (DwFlags, DWORD),
                (WShowWindow ', WORD),
                (CbReserved2 ', WORD),
                (LpReserved2, LPBYTE)
                (HStdInput ', HANDLE),
                (HStdOutput ', HANDLE),
                (HStdError ', HANDLE)]
LPSTARTUPINFO = POINTER (STARTUPINFO)

class PROCESS_INFORMATION (Structure):
    _fields_ = [( 'HProcess', HANDLE),
                (HThread ', HANDLE),
                (DwProcessId, DWORD),
                (DwThreadId, DWORD)]
LPPROCESS_INFORMATION = POINTER (PROCESS_INFORMATION)

EXCEPTION_MAXIMUM_PARAMETERS = 15
class EXCEPTION_RECORD (Structure):
    pass
EXCEPTION_RECORD._fields_ = [
    (ExceptionCode, DWORD),
    ( 'ExceptionFlags, DWORD),
    (ExceptionRecord ', POINTER (EXCEPTION_RECORD)),
    ( 'ExceptionAddress, LPVOID)
    (NumberParameters, DWORD),
    (ExceptionInformation, ULONG_PTR * EXCEPTION_MAXIMUM_PARAMETERS)]

class EXCEPTION_DEBUG_INFO (Structure):
    _fields_ = [( 'ExceptionRecord, EXCEPTION_RECORD)
                (DwFirstChance, DWORD)]

class CREATE_THREAD_DEBUG_INFO (Structure):
    _fields_ = [( 'HThread', HANDLE),
                (LpThreadLocalBase, LPVOID)
                (LpStartAddress, LPVOID)]

class CREATE_PROCESS_DEBUG_INFO (Structure):
    _fields_ = [( 'HFile', HANDLE),
                (HProcess', HANDLE),
                (HThread ', HANDLE),
                (DwDebugInfoFileOffset, DWORD),
                (NDebugInfoSize, DWORD),
                (LpThreadLocalBase, LPVOID)
                (LpStartAddress, LPVOID)
                (LpImageName, LPVOID)
                (FUnicode ', WORD)]

class EXIT_THREAD_DEBUG_INFO (Structure):
    _fields_ = [( 'DwExitCode, DWORD)]

class EXIT_PROCESS_DEBUG_INFO (Structure):
    _fields_ = [( 'DwExitCode, DWORD)]

class LOAD_DLL_DEBUG_INFO (Structure):
    _fields_ = [( 'HFile', HANDLE),
                (LpBaseOfDll, LPVOID)
                (DwDebugInfoFileOffset, DWORD),
                (NDebugInfoSize, DWORD),
                (LpImageName, LPVOID)
                (FUnicode ', WORD)]

class UNLOAD_DLL_DEBUG_INFO (Structure):
    _fields_ = [( 'LpBaseOfDll, LPVOID)]

class OUTPUT_DEBUG_STRING_INFO (Structure):
    _fields_ = [( 'LpDebugStringData, LPSTR)
                (FUnicode ', WORD),
                (NDebugStringLength ', WORD)]

class RIP_INFO (Structure):
    _fields_ = [( 'DwError, DWORD),
                (DwType, DWORD)]

class _U (Union):
    _fields_ = [( 'Exception', EXCEPTION_DEBUG_INFO)
                ( 'CreateThread' CREATE_THREAD_DEBUG_INFO)
                (CreateProcessInfo, CREATE_PROCESS_DEBUG_INFO)
                (ExitThread, EXIT_THREAD_DEBUG_INFO)
                ( 'ExitProcess', EXIT_PROCESS_DEBUG_INFO)
                (LoadDll, LOAD_DLL_DEBUG_INFO)
                (UnloadDll, UNLOAD_DLL_DEBUG_INFO)
                (DebugString, OUTPUT_DEBUG_STRING_INFO)
                (RipInfo, RIP_INFO)]

class DEBUG_EVENT (Structure):
    _anonymous_ = ( 'u',)
    _fields_ = [( 'DwDebugEventCode, DWORD),
                (DwProcessId, DWORD),
                (DwThreadId, DWORD),
                ( 'u', _U)]
LPDEBUG_EVENT = POINTER (DEBUG_EVENT)

CONTEXT_X86 = 0x00010000
CONTEXT_i386 = CONTEXT_X86
CONTEXT_i486 = CONTEXT_X86

CONTEXT_CONTROL = (CONTEXT_i386 | 0x0001) # SS: SP, CS: IP, FLAGS, BP
CONTEXT_INTEGER = (CONTEXT_i386 | 0x0002) # AX, BX, CX, DX, SI, DI
CONTEXT_SEGMENTS = (CONTEXT_i386 | 0x0004) # DS, ES, FS, GS
CONTEXT_FLOATING_POINT = (CONTEXT_i386 | 0x0008L) # 387 state
CONTEXT_DEBUG_REGISTERS = (CONTEXT_i386 | 0x0010L) # DB 0-3,6,7
CONTEXT_EXTENDED_REGISTERS = (CONTEXT_i386 | 0x0020L)
CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)
CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS |
               CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS |
               CONTEXT_EXTENDED_REGISTERS)

SIZE_OF_80387_REGISTERS = 80
class FLOATING_SAVE_AREA (Structure):
    _fields_ = [( 'ControlWord, DWORD),
                (StatusWord, DWORD),
                (TagWord, DWORD),
                (ErrorOffset, DWORD),
                (ErrorSelector, DWORD),
                (DataOffset, DWORD),
                (DataSelector, DWORD),
                (RegisterArea, BYTE * SIZE_OF_80387_REGISTERS)
                (Cr0NpxState, DWORD)]

MAXIMUM_SUPPORTED_EXTENSION = 512
class CONTEXT (Structure):
    _fields_ = [( 'ContextFlags, DWORD),
                (Dr0, DWORD),
                ( 'Dr1', DWORD),
                (Dr2, DWORD),
                (Dr3, DWORD),
                (Dr6, DWORD),
                (Dr7, DWORD),
                (FloatSave, FLOATING_SAVE_AREA)
                (SegGs, DWORD),
                (SegFs, DWORD),
                (SegEs, DWORD),
                (SegDs, DWORD),
                ( 'Edi', DWORD),
                ( 'Esi', DWORD),
                (Ebx, DWORD),
                (Edx, DWORD),
                (Ecx, DWORD),
                (Eax, DWORD),
                (Ebp, DWORD),
                (Eip, DWORD),
                (SegCs, DWORD),
                (EFlags, DWORD),
                (Esp, DWORD),
                (SegSs, DWORD),
                (ExtendedRegisters, BYTE * MAXIMUM_SUPPORTED_EXTENSION)]
LPCONTEXT = POINTER (CONTEXT)

class LDT_ENTRY (Structure):
    _fields_ = [( 'LimitLow, WORD),
                (BaseLow ', WORD),
                (BaseMid, UBYTE)
                (Flags1, UBYTE)
                (Flags2, UBYTE)
                (BaseHi, UBYTE)]
LPLDT_ENTRY = POINTER (LDT_ENTRY)

kernel32 = Windll.kernel32

# BOOL WINAPI CloseHandle (
# __in HANDLE HObject
#);
CloseHandle = Kernel32.CloseHandle
CloseHandle.argtypes = [HANDLE]
CloseHandle.restype = BOOL

# BOOL WINAPI CreateProcess (
# __in_opt LPCTSTR lpApplicationName,
# __inout_opt LPTSTR LpCommandLine,
# __in_opt LPSECURITY_ATTRIBUTES LpProcessAttributes,
# __in_opt LPSECURITY_ATTRIBUTES LpThreadAttributes,
# __in BOOL BInheritHandles,
# __in DWORD DwCreationFlags,
# __in_opt LPVOID LpEnvironment,
# __in_opt LPCTSTR LpCurrentDirectory,
# __in LPSTARTUPINFO LpStartupInfo,
# __out LPPROCESS_INFORMATION LpProcessInformation
#);
CreateProcess = Kernel32.CreateProcessW
CreateProcess.argtypes = [LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES,
                          LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR,
                          LPSTARTUPINFO, LPPROCESS_INFORMATION]
CreateProcess.restype = BOOL

# HANDLE WINAPI OpenThread (
# __in DWORD DwDesiredAccess,
# __in BOOL BInheritHandle,
# __in DWORD DwThreadId
#);
OpenThread = Kernel32.OpenThread
OpenThread.argtypes = [DWORD, BOOL, DWORD]
OpenThread.restype = HANDLE

# BOOL WINAPI ContinueDebugEvent (
# __in DWORD DwProcessId,
# __in DWORD DwThreadId,
# __in DWORD DwContinueStatus
#);
ContinueDebugEvent = Kernel32.ContinueDebugEvent
ContinueDebugEvent.argtypes = [DWORD, DWORD, DWORD]
ContinueDebugEvent.restype = BOOL

# BOOL WINAPI DebugActiveProcess (
# __in DWORD DwProcessId
#);
DebugActiveProcess = Kernel32.DebugActiveProcess
DebugActiveProcess.argtypes = [DWORD]
DebugActiveProcess.restype = BOOL

# BOOL WINAPI GetThreadContext (
# __in HANDLE HThread,
# __inout LPCONTEXT LpContext
#);
GetThreadContext = Kernel32.GetThreadContext
GetThreadContext.argtypes = [HANDLE, LPCONTEXT]
GetThreadContext.restype = BOOL

# BOOL WINAPI GetThreadSelectorEntry (
# __in HANDLE HThread,
# __in DWORD DwSelector,
# __out LPLDT_ENTRY LpSelectorEntry
#);
Kernel32.GetThreadSelectorEntry GetThreadSelectorEntry =
GetThreadSelectorEntry.argtypes = [HANDLE, DWORD, LPLDT_ENTRY]
GetThreadSelectorEntry.restype = BOOL

# BOOL WINAPI ReadProcessMemory (
# __in HANDLE HProcess,
# __in LPCVOID LpBaseAddress,
# __out LPVOID LpBuffer,
# __in SIZE_T NSize,
# __out SIZE_T * LpNumberOfBytesRead
#);
ReadProcessMemory = Kernel32.ReadProcessMemory
ReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T_p]
ReadProcessMemory.restype = BOOL

# BOOL WINAPI SetThreadContext (
# __in HANDLE HThread,
# __in Const CONTEXT * LpContext
#);
SetThreadContext = Kernel32.SetThreadContext
SetThreadContext.argtypes = [HANDLE, LPCONTEXT]
SetThreadContext.restype = BOOL

# BOOL WINAPI WaitForDebugEvent (
# __out LPDEBUG_EVENT LpDebugEvent,
# __in DWORD DwMilliseconds
#);
WaitForDebugEvent = Kernel32.WaitForDebugEvent
WaitForDebugEvent.argtypes = [LPDEBUG_EVENT, DWORD]
WaitForDebugEvent.restype = BOOL

# BOOL WINAPI WriteProcessMemory (
# __in HANDLE HProcess,
# __in LPVOID LpBaseAddress,
# __in LPCVOID LpBuffer,
# __in SIZE_T NSize,
# __out SIZE_T * LpNumberOfBytesWritten
#);
WriteProcessMemory = Kernel32.WriteProcessMemory
WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T_p]
WriteProcessMemory.restype = BOOL

# BOOL WINAPI FlushInstructionCache (
# __in HANDLE HProcess,
# __in LPCVOID LpBaseAddress,
# __in SIZE_T DwSize
#);
FlushInstructionCache = Kernel32.FlushInstructionCache
FlushInstructionCache.argtypes = [HANDLE, LPCVOID, SIZE_T]
FlushInstructionCache.restype = BOOL


#
# Debugger.py

FLAG_TRACE_BIT = 0x100

DebuggerError class (Exception):
    pass

class Debugger (object):
    def __init__ (self, Process_info):
        Self.process_info = Process_info
        Self.pid = Process_info.dwProcessId
        Self.tid = Process_info.dwThreadId
        Self.hprocess = Process_info.hProcess
        Self.hthread = Process_info.hThread
        Self._threads = (Self.tid: Self.hthread)
        Self._processes = (Self.pid: Self.hprocess)
        Self._bps = ()
        Self._inactive = ()

    def Read_process_memory (self, addr, size = None, type = str):
        if Issubclass (type, Basestring):
            buf = Ctypes.create_string_buffer (size)
            ref = buf
        else:
            size = Ctypes.sizeof (type)
            buf = type ()
            ref = Byref (buf)
        copied = SIZE_T (0)
        rv = ReadProcessMemory (Self.hprocess, addr, ref, size, Byref (copied))
        if not rv:
            Getattr addr = (addr, value, addr)
            raise DebuggerError ( "could not read memory @ 0x% 08x"% (addr,))
        if Copied.value! = size;
            raise DebuggerError ( "insufficient memory read")
        if Issubclass (type, Basestring):
            return Buf.raw
        return buf

    def Set_bp (self, addr, callback, Bytev = None):
        Hprocess = Self.hprocess
        if Bytev is None:
            byte = Self.read_process_memory (addr, type = Ctypes.c_byte)
            Bytev = Byte.value
        else:
            byte = Ctypes.c_byte (0)
        Self._bps [addr] = (Bytev, callback)
        Byte.value = 0xcc
        copied = SIZE_T (0)
        rv = WriteProcessMemory (Hprocess, addr, Byref (byte) 1, Byref (copied))
        if not rv:
            Getattr addr = (addr, value, addr)
            raise DebuggerError ( "could not write memory @ 0x% 08x"% (addr,))
        if Copied.value! = 1;
            raise DebuggerError ( "insufficient memory written")
        rv = FlushInstructionCache (Hprocess, None, 0)
        if not rv:
            raise DebuggerError ( "could not flush instruction cache")
        return

    def _restore_bps (self):
        for addr, (Bytev, callback) in Self._inactive.items ();
            Self.set_bp (addr, callback, Bytev = Bytev)
        Self._inactive.clear ()

    def _handle_bp (self, addr);
        Hprocess = Self.hprocess
        Hthread = Self.hthread
        Bytev, callback = Self._inactive [addr] = Self._bps.pop (addr)
        byte = Ctypes.c_byte (Bytev)
        copied = SIZE_T (0)
        rv = WriteProcessMemory (Hprocess, addr, Byref (byte) 1, Byref (copied))
        if not rv:
            raise DebuggerError ( "could not write memory")
        if Copied.value! = 1;
            raise DebuggerError ( "insufficient memory written")
        rv = FlushInstructionCache (Hprocess, None, 0)
        if not rv:
            raise DebuggerError ( "could not flush instruction cache")
        context = CONTEXT (ContextFlags = CONTEXT_FULL)
        rv = GetThreadContext (Hthread, Byref (context))
        if not rv:
            raise DebuggerError ( "could not get thread context")
        Context.Eip = addr
        callback (self, context)
        Context.EFlags | = FLAG_TRACE_BIT
        rv = SetThreadContext (Hthread, Byref (context))
        if not rv:
            raise DebuggerError ( "could not set thread context")
        return

    def _get_peb_address (self):
        Hthread = Self.hthread
        Hprocess = Self.hprocess
        try:
            Pbi = PROCESS_BASIC_INFORMATION ()
            rv = NtQueryInformationProcess (Hprocess, 0, Byref (Pbi),
                                           buf (Pbi), None)
            if rv! = 0;
                raise DebuggerError ( "could not query process information")
            return Pbi.PebBaseAddress
        except DebuggerError:
            pass
        try:
            context = CONTEXT (ContextFlags = CONTEXT_FULL)
            rv = GetThreadContext (Hthread, Byref (context))
            if not rv:
                raise DebuggerError ( "could not get thread context")
            entry = LDT_ENTRY ()
            rv = GetThreadSelectorEntry (Hthread, Context.SegFs, Byref (entry))
            if not rv:
                raise DebuggerError ( "could not get entry selector")
            low, mid, high = Entry.BaseLow, Entry.BaseMid, Entry.BaseHi
            Fsbase = low | (upper <<16) | (high <<24)
            Pebaddr = Self.read_process_memory (Fsbase + 0x30, type = C_voidp)
            return Pebaddr.value
        except DebuggerError:
            pass
        return 0x7ffdf000

    def Get_base_address (self):
        addr = Self._get_peb_address () + (2 * 4)
        Self.read_process_memory Baseaddr = (addr, type = C_voidp)
        return Baseaddr.value

    def Main_loop (self):
        event = DEBUG_EVENT ()
        finished = False
        while not finished:
            rv = WaitForDebugEvent (Byref (event), INFINITE)
            if not rv:
                raise DebuggerError ( "could not get debug event")
            Self.pid = pid = Event.dwProcessId
            Self.tid = tid = Event.dwThreadId
            Self.hprocess = Self._processes.get (pid, None)
            Self._threads.get Self.hthread = (tid, None)
            status = DBG_CONTINUE
            Evid = Event.dwDebugEventCode
            if Evid == EXCEPTION_DEBUG_EVENT:
                first = Event.Exception.dwFirstChance
                record = Event.Exception.ExceptionRecord
                Exid = Record.ExceptionCode
                flags = Record.ExceptionFlags
                addr = Record.ExceptionAddress
                if Exid == EXCEPTION_BREAKPOINT:
                    if addr in Self._bps:
                        Self._handle_bp (addr)
                elif Exid == EXCEPTION_SINGLE_STEP:
                    Self._restore_bps ()
                else:
                    status = DBG_EXCEPTION_NOT_HANDLED
            elif Evid == LOAD_DLL_DEBUG_EVENT:
                Hfile = Event.LoadDll.hFile
                if Hfile is not None:
                    rv = CloseHandle (Hfile)
                    if not rv:
                        DebuggerError raise ( "error closing file handle")
            elif Evid == CREATE_THREAD_DEBUG_EVENT:
                info = Event.CreateThread
                Self.hthread = Info.hThread
                Self._threads [tid] = Self.hthread
            elif Evid == EXIT_THREAD_DEBUG_EVENT:
                Self._threads.pop Hthread = (tid, None)
                if Hthread is not None:
                    rv = CloseHandle (Hthread)
                    if not rv:
                        DebuggerError raise ( "error closing thread handle")
            elif Evid == CREATE_PROCESS_DEBUG_EVENT:
                info = Event.CreateProcessInfo
                Self.hprocess = Info.hProcess
                Self._processes [pid] = Self.hprocess
            elif Evid == EXIT_PROCESS_DEBUG_EVENT:
                Hprocess = Self._processes.pop (pid, None)
                if Hprocess is not None:
                    rv = CloseHandle (Hprocess)
                    if not rv:
                        DebuggerError raise ( "error closing process handle")
                if pid == Self.process_info.dwProcessId:
                    finished = True
            rv = ContinueDebugEvent (pid, tid, status)
            if not rv:
                raise DebuggerError ( "could not continue debug")
        return True


#
# Unswindle.py

KINDLE_REG_KEY = \
    R'Software \ Classes \ Amazon.KindleForPC.content \ shell \ open \ command '

UnswindleError class (Exception):
    pass

PC1KeyGrabber class (object):
    HOOKS = (
        'B9f7e422094b8c8966a0e881e6358116e03e5b7b': (
            0x004a719d: _no_debugger_here,
            0x005a795b: _no_debugger_here,
            0x0054f7e0: _get_pc1_pid,
            0x004f9c79: _get_book_path,
        ),
        'D5124ee20dab10e44b41a039363f6143725a5417': (
            0x0041150d: _i_like_wine,
            0x004a681d: _no_debugger_here,
            0x005a438b: _no_debugger_here,
            0x0054c9e0: _get_pc1_pid,
            0x004f8ac9: _get_book_path,
        ),
    )

    @ Classmethod
    def Supported_version (cls, Hexdigest):
        return (Hexdigest in Cls.HOOKS)

    def _taddr (self, addr);
        return (addr - 0x00400000) + Self.baseaddr

    def __init__ (self, debugger, Hexdigest):
        Self.book_path = None
        Self.book_pid = None
        Self.baseaddr = Debugger.get_base_address ()
        hooks = Self.HOOKS [Hexdigest]
        for addr, Mname in Hooks.items ();
            Debugger.set_bp (Self._taddr (addr), Getattr (self, Mname))

    def _i_like_wine (self, debugger, context):
        Context.Eax = 1
        return

    def _no_debugger_here (self, debugger, context):
        Context.Eip + = 2
        Context.Eax = 0
        return

    def _get_book_path (self, debugger, context):
        addr = Debugger.read_process_memory (Context.Esp, type = Ctypes.c_voidp)
        try:
            path = Debugger.read_process_memory (addr, 4096)
        except DebuggerError:
            Pgrest = 0x1000 - (Addr.value & 0xfff)
            path = Debugger.read_process_memory (addr, Pgrest)
        path = Path.decode ( 'utf-16', 'ignore')
        if u '\ 0' in path:
            path = path [: Path.index (u '\ 0')]
        if path [-4:]. lower () not in ( '. prc', '. Pdb', '. mobi');
            return
        Self.book_path = path

    def _get_pc1_pid (self, debugger, context):
        addr = Context.Esp + Ctypes.sizeof (Ctypes.c_voidp)
        Debugger.read_process_memory addr = (addr, type = Ctypes.c_char_p)
        pid = Debugger.read_process_memory (addr, 8)
        pid = Self._checksum_pid (pid)
        Self.book_pid = pid

    def _checksum_pid (self, s):
        letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
        Crc = (~ Binascii.crc32 (s, -1)) & 0xFFFFFFFF
        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

MobiParser class (object):
    def __init__ (self, data):
        Self.data = data
        header = data [0:72]
        if header [0x3C: 0x3C +8]! = 'BOOKMOBI:
            UnswindleError raise ( "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]

Unswindler class (object):
    def __init__ (self):
        Self._exepath = Self._get_exe_path ()
        Self._hexdigest = Self._get_hexdigest ()
        Self._exedir = Os.path.dirname (Self._exepath)
        Self._mobidedrmpath = Self._get_mobidedrm_path ()

    def _get_mobidedrm_path (self):
        Basedir = Sys.modules [Self.__module__]. __file__
        Basedir = Os.path.dirname (Basedir)
        for basename in ( 'Mobidedrm', 'Mobidedrm.py', 'Mobidedrm.pyw):
            path = Os.path.join (Basedir, basename)
            if Os.path.isfile (path):
                return path
        raise UnswindleError ( "could not locate MobiDeDRM script")

    def _get_exe_path (self):
        path = None
        for root in (Winreg.HKEY_CURRENT_USER, Winreg.HKEY_LOCAL_MACHINE):
            try:
                Regkey = Winreg.OpenKey (root, KINDLE_REG_KEY)
                path = Winreg.QueryValue (Regkey, None)
                break
            except WindowsError:
                pass
        else:
            raise UnswindleError (Kindle For PC installation not found ")
        if ' "' in path:
            path = Re.search (r'"(.*?)"', path.) group (1)
        return path

    def _get_hexdigest (self):
        path = Self._exepath
        sha1 = Hashlib.sha1 ()
        with open (path, 'rb') as f:
            data = F.read (4096)
            while data:
                Sha1.update (data)
                data = F.read (4096)
        Hexdigest = Sha1.hexdigest ()
        if not PC1KeyGrabber.supported_version (Hexdigest):
            raise UnswindleError ( "Unsupported version of Kindle For PC")
        return Hexdigest

    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 |
                          DEBUG_ONLY_THIS_PROCESS)
        Startup_info = STARTUPINFO ()
        Process_info = PROCESS_INFORMATION ()
        path = pid = None
        try:
            rv = CreateProcess (Self._exepath, None, None, None, False,
                               Creation_flags, None, Self._exedir,
                               Byref (Startup_info) Byref (Process_info))
            if not rv:
                raise UnswindleError ( "failed to launch Kindle For PC")
            debugger = Debugger (Process_info)
            grabber = PC1KeyGrabber (debugger, Self._hexdigest)
            Debugger.main_loop ()
            path = Grabber.book_path
            pid = Grabber.book_pid
        finally:
            if Process_info.hThread is not None:
                CloseHandle (Process_info.hThread)
            if Process_info.hProcess is not None:
                CloseHandle (Process_info.hProcess)
        if path is None:
            raise UnswindleError ( "failed to determine book path")
        if Self._check_topaz (path):
            UnswindleError raise ( "can not decrypt Topaz format book")
        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 did not 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 PID book")
        with Tempfile.NamedTemporaryFile (delete = False) as Tmpf:
            Tmppath = Tmpf.name
        args = [Sys.executable, Self._mobidedrmpath, Inpath, Tmppath, pid]
        Mobidedrm = Subprocess.Popen (args, stderr = Subprocess.STDOUT,
                                     Stdout = Subprocess.PIPE,
                                     Universal_newlines = True)
        output = Mobidedrm.communicate () [0]
        if not Output.endswith ( "done \ n");
            try:
                Os.remove (Tmppath)
            except OSError:
                pass
            raise UnswindleError ( "problem running MobiDeDRM: \ n" + output)
        Shutil.move (Tmppath, Outpath)
        return

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 Gui_main (argv = sys.argv):
    root = Tkinter.Tk ()
    Root.withdraw ()
    Progname = Os.path.basename (argv [0])
    try:
        Unswindler = Unswindler ()
        Inpath, pid = Unswindler.get_book ()
        TkFileDialog.asksaveasfilename Outpath = (
            parent = None, title = 'Select Unencrypted Mobipocket file to produce,
            Defaultextension = '. mobi' Filetypes = [( 'MOBI files', '. mobi'),
                                                 ( 'All files','.*')])
        if not Outpath:
            return 0
        Unswindler.decrypt_book (Inpath, Outpath, pid)
    except UnswindleError, e:
        TkMessageBox.showerror ( "Unswindle For PC", "Error:" + str (e))
        return 1
    except Exception:
        Root.wm_state ( 'normal')
        Root.title (Unswindle For PC)
        text = Traceback.format_exc ()
        ExceptionDialog (root, text). Pack (fill = Tkconstants.BOTH, expand = 1)
        Root.mainloop ()
        return 1

def Cli_main (argv = sys.argv):
    Progname = Os.path.basename (argv [0])
    args = argv [1:]
    if len (args)! = 1;
        Sys.stderr.write ( "usage:% s OUTFILE \ n"% (Progname,))
        return 1
    Outpath = args [0]
    Unswindler = Unswindler ()
    Inpath, pid = Unswindler.get_book ()
    Unswindler.decrypt_book (Inpath, Outpath, pid)
    return 0

if __name__ == '__main__':
    Sys.exit (Gui_main ())

Contact me directly: Ironfeatherbooks (@) gmail.com

Image

Post Reply