G2A Many GEOs
SHARE
TWEET

Alf

a guest Jan 7th, 2010 3,437 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/python
  2. #
  3. # This is a python script. You need a Python interpreter to run it.
  4. # For example, ActiveState Python, which exists for windows.
  5. #
  6. # It can run standalone to convert files, or it can be installed as a
  7. # plugin for Calibre (http://calibre-ebook.com/about) so that
  8. # importing files with DRM 'Just Works'.
  9. #
  10. # To create a Calibre plugin, rename this file so that the filename
  11. # ends in '_plugin.py', put it into a ZIP file and import that Calibre
  12. # using its plugin configuration GUI.
  13. #
  14. # Changelog
  15. #  0.01 - Initial version
  16. #  0.02 - Huffdic compressed books were not properly decrypted
  17. #  0.03 - Wasn't checking MOBI header length
  18. #  0.04 - Wasn't sanity checking size of data record
  19. #  0.05 - It seems that the extra data flags take two bytes not four
  20. #  0.06 - And that low bit does mean something after all :-)
  21. #  0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
  22. #  0.08 - ...and also not in Mobi header version < 6
  23. #  0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
  24.  
  25. import sys,struct,binascii
  26.  
  27. class DrmException(Exception):
  28.         pass
  29.  
  30. #implementation of Pukall Cipher 1
  31. def PC1(key, src, decryption=True):
  32.     sum1 = 0;
  33.     sum2 = 0;
  34.     keyXorVal = 0;
  35.     if len(key)!=16:
  36.         print "Bad key length!"
  37.         return None
  38.     wkey = []
  39.     for i in xrange(8):
  40.         wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
  41.  
  42.     dst = ""
  43.     for i in xrange(len(src)):
  44.         temp1 = 0;
  45.         byteXorVal = 0;
  46.         for j in xrange(8):
  47.             temp1 ^= wkey[j]
  48.             sum2  = (sum2+j)*20021 + sum1
  49.             sum1  = (temp1*346)&0xFFFF
  50.             sum2  = (sum2+sum1)&0xFFFF
  51.             temp1 = (temp1*20021+1)&0xFFFF
  52.             byteXorVal ^= temp1 ^ sum2
  53.         curByte = ord(src[i])
  54.         if not decryption:
  55.             keyXorVal = curByte * 257;
  56.         curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
  57.         if decryption:
  58.             keyXorVal = curByte * 257;
  59.         for j in xrange(8):
  60.             wkey[j] ^= keyXorVal;
  61.         dst+=chr(curByte)
  62.     return dst
  63.  
  64. def checksumPid(s):
  65.         letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
  66.         crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
  67.         crc = crc ^ (crc >> 16)
  68.         res = s
  69.         l = len(letters)
  70.         for i in (0,1):
  71.                 b = crc & 0xff
  72.                 pos = (b // l) ^ (b % l)
  73.                 res += letters[pos%l]
  74.                 crc >>= 8
  75.         return res
  76.  
  77. def getSizeOfTrailingDataEntries(ptr, size, flags):
  78.         def getSizeOfTrailingDataEntry(ptr, size):
  79.                 bitpos, result = 0, 0
  80.                 if size <= 0:
  81.                         return result
  82.                 while True:
  83.                         v = ord(ptr[size-1])
  84.                         result |= (v & 0x7F) << bitpos
  85.                         bitpos += 7
  86.                         size -= 1
  87.                         if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
  88.                                 return result
  89.         num = 0
  90.         testflags = flags >> 1
  91.         while testflags:
  92.                 if testflags & 1:
  93.                         num += getSizeOfTrailingDataEntry(ptr, size - num)
  94.                 testflags >>= 1
  95.         if flags & 1:
  96.                 num += (ord(ptr[size - num - 1]) & 0x3) + 1
  97.         return num
  98.  
  99. class DrmStripper:
  100.         def loadSection(self, section):
  101.                 if (section + 1 == self.num_sections):
  102.                         endoff = len(self.data_file)
  103.                 else:
  104.                         endoff = self.sections[section + 1][0]
  105.                 off = self.sections[section][0]
  106.                 return self.data_file[off:endoff]
  107.  
  108.         def patch(self, off, new):     
  109.                 self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
  110.  
  111.         def patchSection(self, section, new, in_off = 0):
  112.                 if (section + 1 == self.num_sections):
  113.                         endoff = len(self.data_file)
  114.                 else:
  115.                         endoff = self.sections[section + 1][0]
  116.                 off = self.sections[section][0]
  117.                 assert off + in_off + len(new) <= endoff
  118.                 self.patch(off + in_off, new)
  119.  
  120.         def parseDRM(self, data, count, pid):
  121.                 pid = pid.ljust(16,'\0')
  122.                 keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
  123.                 temp_key = PC1(keyvec1, pid, False)
  124.                 temp_key_sum = sum(map(ord,temp_key)) & 0xff
  125.                 found_key = None
  126.                 for i in xrange(count):
  127.                         verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
  128.                         cookie = PC1(temp_key, cookie)
  129.                         ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
  130.                         if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
  131.                                 found_key = finalkey
  132.                                 break
  133.                 return found_key               
  134.  
  135.  
  136.         def __init__(self, data_file, pid):
  137.  
  138.                 if checksumPid(pid[0:-2]) != pid:
  139.                         raise DrmException("invalid PID checksum")
  140.                 pid = pid[0:-2]
  141.                
  142.                 self.data_file = data_file
  143.                 header = data_file[0:72]
  144.                 if header[0x3C:0x3C+8] != 'BOOKMOBI':
  145.                         raise DrmException("invalid file format")
  146.                 self.num_sections, = struct.unpack('>H', data_file[76:78])
  147.  
  148.                 self.sections = []
  149.                 for i in xrange(self.num_sections):
  150.                         offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
  151.                         flags, val = a1, a2<<16|a3<<8|a4
  152.                         self.sections.append( (offset, flags, val) )
  153.  
  154.                 sect = self.loadSection(0)
  155.                 records, = struct.unpack('>H', sect[0x8:0x8+2])
  156.                 mobi_length, = struct.unpack('>L',sect[0x14:0x18])
  157.                 mobi_version, = struct.unpack('>L',sect[0x68:0x6C])
  158.                 extra_data_flags = 0
  159.                 print "MOBI header length = %d" %mobi_length
  160.                 print "MOBI header version = %d" %mobi_version
  161.                 if (mobi_length >= 0xE4) and (mobi_version > 5):
  162.                         extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
  163.                         print "Extra Data Flags = %d" %extra_data_flags
  164.  
  165.  
  166.                 crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
  167.                 if crypto_type == 0:
  168.                         raise DrmException("it seems that this book isn't encrypted")
  169.                 if crypto_type == 1:
  170.                         raise DrmException("cannot decode Mobipocket encryption type 1")
  171.                 if crypto_type != 2:
  172.                         raise DrmException("unknown encryption type: %d" % crypto_type)
  173.  
  174.                 # calculate the keys
  175.                 drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
  176.                 if drm_count == 0:
  177.                         raise DrmException("no PIDs found in this file")
  178.                 found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
  179.                 if not found_key:
  180.                         raise DrmException("no key found. maybe the PID is incorrect")
  181.  
  182.                 # kill the drm keys
  183.                 self.patchSection(0, "\0" * drm_size, drm_ptr)
  184.                 # kill the drm pointers
  185.                 self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
  186.                 # clear the crypto type
  187.                 self.patchSection(0, "\0" * 2, 0xC)
  188.  
  189.                 # decrypt sections
  190.                 print "Decrypting. Please wait...",
  191.                 for i in xrange(1, records+1):
  192.                         data = self.loadSection(i)
  193.                         extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
  194.                         # print "record %d, extra_size %d" %(i,extra_size)
  195.                         self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
  196.                 print "done"
  197.         def getResult(self):
  198.                 return self.data_file
  199.  
  200. if not __name__ == "__main__":
  201.         from calibre.customize import FileTypePlugin
  202.  
  203.         class MobiDeDRM(FileTypePlugin):
  204.  
  205.                 name                = 'MobiDeDRM' # Name of the plugin
  206.                 description         = 'Removes DRM from secure Mobi files'
  207.                 supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
  208.                 author              = 'The Dark Reverser' # The author of this plugin
  209.                 version             = (0, 0, 9)   # The version number of this plugin
  210.                 file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
  211.                 on_import           = True # Run this plugin during the import
  212.  
  213.        
  214.                 def run(self, path_to_ebook):
  215.                         of = self.temporary_file('.mobi')
  216.                         PID = self.site_customization
  217.                         data_file = file(path_to_ebook, 'rb').read()
  218.                         ar = PID.split(',')
  219.                         for i in ar:
  220.                                 try:
  221.                                         file(of.name, 'wb').write(DrmStripper(data_file, i).getResult())
  222.                                 except DrmException:
  223.                                         # Hm, we should display an error dialog here.
  224.                                         # Dunno how though.
  225.                                         # Ignore the dirty hack behind the curtain.
  226. #                                       strexcept = 'echo exception: %s > /dev/tty' % e
  227. #                                       subprocess.call(strexcept,shell=True)
  228.                                         print i + ": not PID for book"
  229.                                 else:
  230.                                         return of.name
  231.  
  232.                 def customization_help(self, gui=False):
  233.                         return 'Enter PID (separate multiple PIDs with comma)'
  234.  
  235. if __name__ == "__main__":
  236.         print "MobiDeDrm v0.09. Copyright (c) 2008 The Dark Reverser"
  237.         if len(sys.argv)<4:
  238.                 print "Removes protection from Mobipocket books"
  239.                 print "Usage:"
  240.                 print "  mobidedrm infile.mobi outfile.mobi PID"
  241.         else:  
  242.                 infile = sys.argv[1]
  243.                 outfile = sys.argv[2]
  244.                 pid = sys.argv[3]
  245.                 data_file = file(infile, 'rb').read()
  246.                 try:
  247.                         file(outfile, 'wb').write(DrmStripper(data_file, pid).getResult())
  248.                 except DrmException, e:
  249.                         print "Error: %s" % e
RAW Paste Data
Ledger Nano X - The secure hardware wallet
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top