Guest User

Alf

a guest
Dec 16th, 2009
350
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×