G2A Many GEOs
SHARE
TWEET

Alf

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