Advertisement
Guest User

Alf

a guest
Dec 16th, 2009
492
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.14 KB | None | 0 0
  1. # This is a python script. You need a Python interpreter to run it.
  2. # For example, ActiveState Python, which exists for windows.
  3. #
  4. # Changelog
  5. #  0.01 - Initial version
  6. #  0.02 - Huffdic compressed books were not properly decrypted
  7. #  0.03 - Wasn't checking MOBI header length
  8. #  0.04 - Wasn't sanity checking size of data record
  9. #  0.05 - It seems that the extra data flags take two bytes not four
  10. #  0.06 - And that low bit does mean something after all :-)
  11. #  0.07 - The extra data flags aren't present in MOBI header < 0xE8
  12.  
  13. import sys,struct,binascii
  14.  
  15. class DrmException(Exception):
  16.     pass
  17.  
  18. #implementation of Pukall Cipher 1
  19. def PC1(key, src, decryption=True):
  20.     sum1 = 0;
  21.     sum2 = 0;
  22.     keyXorVal = 0;
  23.     if len(key)!=16:
  24.         print "Bad key length!"
  25.         return None
  26.     wkey = []
  27.     for i in xrange(8):
  28.         wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
  29.  
  30.     dst = ""
  31.     for i in xrange(len(src)):
  32.         temp1 = 0;
  33.         byteXorVal = 0;
  34.         for j in xrange(8):
  35.             temp1 ^= wkey[j]
  36.             sum2  = (sum2+j)*20021 + sum1
  37.             sum1  = (temp1*346)&0xFFFF
  38.             sum2  = (sum2+sum1)&0xFFFF
  39.             temp1 = (temp1*20021+1)&0xFFFF
  40.             byteXorVal ^= temp1 ^ sum2
  41.         curByte = ord(src[i])
  42.         if not decryption:
  43.             keyXorVal = curByte * 257;
  44.         curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
  45.         if decryption:
  46.             keyXorVal = curByte * 257;
  47.         for j in xrange(8):
  48.             wkey[j] ^= keyXorVal;
  49.         dst+=chr(curByte)
  50.     return dst
  51.  
  52. def checksumPid(s):
  53.     letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
  54.     crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
  55.     crc = crc ^ (crc >> 16)
  56.     res = s
  57.     l = len(letters)
  58.     for i in (0,1):
  59.         b = crc & 0xff
  60.         pos = (b // l) ^ (b % l)
  61.         res += letters[pos%l]
  62.         crc >>= 8
  63.     return res
  64.  
  65. def getSizeOfTrailingDataEntries(ptr, size, flags):
  66.     def getSizeOfTrailingDataEntry(ptr, size):
  67.         bitpos, result = 0, 0
  68.         if size <= 0:
  69.             return result
  70.         while True:
  71.             v = ord(ptr[size-1])
  72.             result |= (v & 0x7F) << bitpos
  73.             bitpos += 7
  74.             size -= 1
  75.             if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
  76.                 return result
  77.     num = 0
  78.     testflags = flags >> 1
  79.     while testflags:
  80.         if testflags & 1:
  81.             num += getSizeOfTrailingDataEntry(ptr, size - num)
  82.         testflags >>= 1
  83.     if flags & 1:
  84.         num += (ord(ptr[size - num - 1]) & 0x3) + 1
  85.     return num
  86.  
  87. class DrmStripper:
  88.     def loadSection(self, section):
  89.         if (section + 1 == self.num_sections):
  90.             endoff = len(self.data_file)
  91.         else:
  92.             endoff = self.sections[section + 1][0]
  93.         off = self.sections[section][0]
  94.         return self.data_file[off:endoff]
  95.  
  96.     def patch(self, off, new)
  97.         self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
  98.  
  99.     def patchSection(self, section, new, in_off = 0):
  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.         assert off + in_off + len(new) <= endoff
  106.         self.patch(off + in_off, new)
  107.  
  108.     def parseDRM(self, data, count, pid):
  109.         pid = pid.ljust(16,'\0')
  110.         keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
  111.         temp_key = PC1(keyvec1, pid, False)
  112.         temp_key_sum = sum(map(ord,temp_key)) & 0xff
  113.         found_key = None
  114.         for i in xrange(count):
  115.             verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
  116.             cookie = PC1(temp_key, cookie)
  117.             ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
  118.             if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
  119.                 found_key = finalkey
  120.                 break
  121.         return found_key       
  122.  
  123.  
  124.     def __init__(self, data_file, pid):
  125.  
  126.         if checksumPid(pid[0:-2]) != pid:
  127.             raise DrmException("invalid PID checksum")
  128.         pid = pid[0:-2]
  129.        
  130.         self.data_file = data_file
  131.         header = data_file[0:72]
  132.         if header[0x3C:0x3C+8] != 'BOOKMOBI':
  133.             raise DrmException("invalid file format")
  134.         self.num_sections, = struct.unpack('>H', data_file[76:78])
  135.  
  136.         self.sections = []
  137.         for i in xrange(self.num_sections):
  138.             offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
  139.             flags, val = a1, a2<<16|a3<<8|a4
  140.             self.sections.append( (offset, flags, val) )
  141.  
  142.         sect = self.loadSection(0)
  143.         records, = struct.unpack('>H', sect[0x8:0x8+2])
  144.         mobi_length, = struct.unpack('>L',sect[0x14:0x18])
  145.         extra_data_flags = 0
  146.         if mobi_length >= 0xE8:
  147.             extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
  148.  
  149.  
  150.         crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
  151.         if crypto_type == 0:
  152.             raise DrmException("it seems that this book isn't encrypted")
  153.         if crypto_type == 1:
  154.             raise DrmException("cannot decode Mobipocket encryption type 1")
  155.         if crypto_type != 2:
  156.             raise DrmException("unknown encryption type: %d" % crypto_type)
  157.  
  158.         # calculate the keys
  159.         drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
  160.         if drm_count == 0:
  161.             raise DrmException("no PIDs found in this file")
  162.         found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
  163.         if not found_key:
  164.             raise DrmException("no key found. maybe the PID is incorrect")
  165.  
  166.         # kill the drm keys
  167.         self.patchSection(0, "\0" * drm_size, drm_ptr)
  168.         # kill the drm pointers
  169.         self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
  170.         # clear the crypto type
  171.         self.patchSection(0, "\0" * 2, 0xC)
  172.  
  173.         # decrypt sections
  174.         print "Decrypting. Please wait...",
  175.         for i in xrange(1, records+1):
  176.             data = self.loadSection(i)
  177.             extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
  178.             self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
  179.         print "done"
  180.     def getResult(self):
  181.         return self.data_file
  182.  
  183. print "MobiDeDrm v0.07. Copyright (c) 2008 The Dark Reverser"
  184. if len(sys.argv)<4:
  185.     print "Removes protection from Mobipocket books"
  186.     print "Usage:"
  187.     print "  mobidedrm infile.mobi outfile.mobi PID"
  188. else:  
  189.     infile = sys.argv[1]
  190.     outfile = sys.argv[2]
  191.     pid = sys.argv[3]
  192.     data_file = file(infile, 'rb').read()
  193.     try:
  194.         file(outfile, 'wb').write(DrmStripper(data_file, pid).getResult())
  195.     except DrmException, e:
  196.         print "Error: %s" % e
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement