pyth0n3 Posted December 24, 2009 Report Posted December 24, 2009 An Israeli hacker claims to have broken the copyright protection on Amazon's Kindle e-reader, reports say.The hack will allow the ebooks stored on the reader to be transferred as pdf files to any other device.The hacker, known as Labba, responded to a challenge posted on Israeli hacking forum, hacking.org.It is the latest in a series of Digital Rights Management hacks, the most famous being the reverse engineering of iTunes.The Kindle e-book reader has been very successful since it was launched in the US in 2007.Amazon hopes to have sold a million devices by the end of the year.It leaves it to individual publishers whether they want to apply DRM but books in its main proprietary format .azw, cannot be transferred to other devices.It did not immediately respond to the news but it is likely it will attempt to patch its DRM software.DRM has long divided opinion. While rights holders regard it as a crucial tool to protect copyright, consumers tend to hate it because it limits what can be done with content."DRM is not an effective way of preventing copying nor is it a good way of making sales. There isn't a customer out there saying 'what I need is an electronic book that does less," novelist and co-editor of the Boing Boing blog Cory Doctorow told the BBC when the Kindle was launched.As soon as a new DRM system is active, hackers begin to try and break it.Most famously Jon Lech Johansen, known as DVD Jon, cracked the copy protection on DVDs in 1999.He went on to break the copyright protection on iTunes, leading Apple to offer DRM-free music.DVD Jon now runs a company with an application to take the pain out of moving different types of content between devices.BBC News 23 December 2009 Quote
begood Posted December 25, 2009 Report Posted December 25, 2009 #! /usr/bin/python# unswindle.pyw, version 5-rc1# 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 on new (20091222) version of K4PC"""Decrypt Kindle For PC encrypted Mobipocket books."""__license__ = 'GPL v3'import sysimport osimport reimport tempfileimport shutilimport subprocessimport structimport hashlibimport ctypesfrom ctypes import *from ctypes.wintypes import *import binasciiimport _winreg as winregimport Tkinterimport Tkconstantsimport tkMessageBoximport tkFileDialogimport traceback## _extrawintypes.pyUBYTE = c_ubyteULONG_PTR = POINTER(ULONG)PULONG = ULONG_PTRPVOID = LPVOIDLPCTSTR = LPTSTR = c_wchar_pLPBYTE = c_char_pSIZE_T = c_uintSIZE_T_p = POINTER(SIZE_T)## _ntdll.pyNTSTATUS = DWORDntdll = windll.ntdllclass 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.NtQueryInformationProcessNtQueryInformationProcess.argtypes = [HANDLE, DWORD, PVOID, ULONG, PULONG]NtQueryInformationProcess.restype = NTSTATUS## _kernel32.pyINFINITE = 0xffffffffCREATE_UNICODE_ENVIRONMENT = 0x00000400DEBUG_ONLY_THIS_PROCESS = 0x00000002DEBUG_PROCESS = 0x00000001THREAD_GET_CONTEXT = 0x0008THREAD_QUERY_INFORMATION = 0x0040THREAD_SET_CONTEXT = 0x0010THREAD_SET_INFORMATION = 0x0020EXCEPTION_BREAKPOINT = 0x80000003EXCEPTION_SINGLE_STEP = 0x80000004EXCEPTION_ACCESS_VIOLATION = 0xC0000005DBG_CONTINUE = 0x00010002LDBG_EXCEPTION_NOT_HANDLED = 0x80010001LEXCEPTION_DEBUG_EVENT = 1CREATE_THREAD_DEBUG_EVENT = 2CREATE_PROCESS_DEBUG_EVENT = 3EXIT_THREAD_DEBUG_EVENT = 4EXIT_PROCESS_DEBUG_EVENT = 5LOAD_DLL_DEBUG_EVENT = 6UNLOAD_DLL_DEBUG_EVENT = 7OUTPUT_DEBUG_STRING_EVENT = 8RIP_EVENT = 9class 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 = 15class EXCEPTION_RECORD(Structure): passEXCEPTION_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 = 0x00010000CONTEXT_i386 = CONTEXT_X86CONTEXT_i486 = CONTEXT_X86CONTEXT_CONTROL = (CONTEXT_i386 | 0x0001) # SS:SP, CS:IP, FLAGS, BPCONTEXT_INTEGER = (CONTEXT_i386 | 0x0002) # AX, BX, CX, DX, SI, DICONTEXT_SEGMENTS = (CONTEXT_i386 | 0x0004) # DS, ES, FS, GSCONTEXT_FLOATING_POINT = (CONTEXT_i386 | 0x0008L) # 387 stateCONTEXT_DEBUG_REGISTERS = (CONTEXT_i386 | 0x0010L) # DB 0-3,6,7CONTEXT_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 = 80class 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 = 512class 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.CloseHandleCloseHandle.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.CreateProcessWCreateProcess.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.OpenThreadOpenThread.argtypes = [DWORD, BOOL, DWORD]OpenThread.restype = HANDLE# BOOL WINAPI ContinueDebugEvent(# __in DWORD dwProcessId,# __in DWORD dwThreadId,# __in DWORD dwContinueStatus# );ContinueDebugEvent = kernel32.ContinueDebugEventContinueDebugEvent.argtypes = [DWORD, DWORD, DWORD]ContinueDebugEvent.restype = BOOL# BOOL WINAPI DebugActiveProcess(# __in DWORD dwProcessId# );DebugActiveProcess = kernel32.DebugActiveProcessDebugActiveProcess.argtypes = [DWORD]DebugActiveProcess.restype = BOOL# BOOL WINAPI GetThreadContext(# __in HANDLE hThread,# __inout LPCONTEXT lpContext# );GetThreadContext = kernel32.GetThreadContextGetThreadContext.argtypes = [HANDLE, LPCONTEXT]GetThreadContext.restype = BOOL# BOOL WINAPI GetThreadSelectorEntry(# __in HANDLE hThread,# __in DWORD dwSelector,# __out LPLDT_ENTRY lpSelectorEntry# );GetThreadSelectorEntry = kernel32.GetThreadSelectorEntryGetThreadSelectorEntry.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.ReadProcessMemoryReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T_p]ReadProcessMemory.restype = BOOL# BOOL WINAPI SetThreadContext(# __in HANDLE hThread,# __in const CONTEXT *lpContext# );SetThreadContext = kernel32.SetThreadContextSetThreadContext.argtypes = [HANDLE, LPCONTEXT]SetThreadContext.restype = BOOL# BOOL WINAPI WaitForDebugEvent(# __out LPDEBUG_EVENT lpDebugEvent,# __in DWORD dwMilliseconds# );WaitForDebugEvent = kernel32.WaitForDebugEventWaitForDebugEvent.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.WriteProcessMemoryWriteProcessMemory.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.FlushInstructionCacheFlushInstructionCache.argtypes = [HANDLE, LPCVOID, SIZE_T]FlushInstructionCache.restype = BOOL## debugger.pyFLAG_TRACE_BIT = 0x100class DebuggerError(Exception): passclass 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: addr = getattr(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: addr = getattr(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), sizeof(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 selector entry") low, mid, high = entry.BaseLow, entry.BaseMid, entry.BaseHi fsbase = low | (mid << 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) baseaddr = self.read_process_memory(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.hthread = self._threads.get(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: raise DebuggerError("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: hthread = self._threads.pop(tid, None) if hthread is not None: rv = CloseHandle(hthread) if not rv: raise DebuggerError("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: raise DebuggerError("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.pyKINDLE_REG_KEY = \ r'Software\Classes\Amazon.KindleForPC.content\shell\open\command'class UnswindleError(Exception): passclass PC1KeyGrabber(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) addr = debugger.read_process_memory(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 resclass Unswindler(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 _is_topaz(self, path): with open(path, 'rb') as f: magic = f.read(4) if magic == 'TPZ0': return True return False 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._is_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): # darkreverser didn't protect mobidedrm's script execution to allow # importing, so we have to just run it in a subprocess 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) returnclass 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() outpath = tkFileDialog.asksaveasfilename( 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 1def 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 0if __name__ == '__main__': sys.exit(gui_main()) Quote