SHARE
TWEET

DarkReverser

a guest Sep 24th, 2008 1,540 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.  
  8. import sys,struct,binascii
  9.  
  10. class DrmException(Exception):
  11.         pass
  12.  
  13. #implementation of Pukall Cipher 1
  14. def PC1(key, src, decryption=True):
  15.     sum1 = 0;
  16.     sum2 = 0;
  17.     keyXorVal = 0;
  18.     if len(key)!=16:
  19.         print "Bad key length!"
  20.         return None
  21.     wkey = []
  22.     for i in xrange(8):
  23.         wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
  24.  
  25.     dst = ""
  26.     for i in xrange(len(src)):
  27.         temp1 = 0;
  28.         byteXorVal = 0;
  29.         for j in xrange(8):
  30.             temp1 ^= wkey[j]
  31.             sum2  = (sum2+j)*20021 + sum1
  32.             sum1  = (temp1*346)&0xFFFF
  33.             sum2  = (sum2+sum1)&0xFFFF
  34.             temp1 = (temp1*20021+1)&0xFFFF
  35.             byteXorVal ^= temp1 ^ sum2
  36.         curByte = ord(src[i])
  37.         if not decryption:
  38.             keyXorVal = curByte * 257;
  39.         curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
  40.         if decryption:
  41.             keyXorVal = curByte * 257;
  42.         for j in xrange(8):
  43.             wkey[j] ^= keyXorVal;
  44.         dst+=chr(curByte)
  45.     return dst
  46.  
  47. def checksumPid(s):
  48.         letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
  49.         crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
  50.         crc = crc ^ (crc >> 16)
  51.         res = s
  52.         l = len(letters)
  53.         for i in (0,1):
  54.                 b = crc & 0xff
  55.                 pos = (b // l) ^ (b % l)
  56.                 res += letters[pos%l]
  57.                 crc >>= 8
  58.         return res
  59.  
  60. def getSizeOfTrailingDataEntries(ptr, size, flags):
  61.         def getSizeOfTrailingDataEntry(ptr, size):
  62.                 bitpos, result = 0, 0
  63.                 while True:
  64.                         v = ord(ptr[size-1])
  65.                         result |= (v & 0x7F) << bitpos
  66.                         bitpos += 7
  67.                         size -= 1
  68.                         if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
  69.                                 return result
  70.         num = 0
  71.         flags >>= 1
  72.         while flags:
  73.                 if flags & 1:
  74.                         num += getSizeOfTrailingDataEntry(ptr, size - num)
  75.                 flags >>= 1            
  76.         return num
  77.  
  78.  
  79. class DrmStripper:
  80.         def loadSection(self, section):
  81.                 if (section + 1 == self.num_sections):
  82.                         endoff = len(self.data_file)
  83.                 else:
  84.                         endoff = self.sections[section + 1][0]
  85.                 off = self.sections[section][0]
  86.                 return self.data_file[off:endoff]
  87.  
  88.         def patch(self, off, new):     
  89.                 self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
  90.  
  91.         def patchSection(self, section, new, in_off = 0):
  92.                 if (section + 1 == self.num_sections):
  93.                         endoff = len(self.data_file)
  94.                 else:
  95.                         endoff = self.sections[section + 1][0]
  96.                 off = self.sections[section][0]
  97.                 assert off + in_off + len(new) <= endoff
  98.                 self.patch(off + in_off, new)
  99.  
  100.         def parseDRM(self, data, count, pid):
  101.                 pid = pid.ljust(16,'\0')
  102.                 keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
  103.                 temp_key = PC1(keyvec1, pid, False)
  104.                 temp_key_sum = sum(map(ord,temp_key)) & 0xff
  105.                 found_key = None
  106.                 for i in xrange(count):
  107.                         verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
  108.                         cookie = PC1(temp_key, cookie)
  109.                         ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
  110.                         if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
  111.                                 found_key = finalkey
  112.                                 break
  113.                 return found_key               
  114.  
  115.  
  116.         def __init__(self, data_file, pid):
  117.  
  118.                 if checksumPid(pid[0:-2]) != pid:
  119.                         raise DrmException("invalid PID checksum")
  120.                 pid = pid[0:-2]
  121.                
  122.                 self.data_file = data_file
  123.                 header = data_file[0:72]
  124.                 if header[0x3C:0x3C+8] != 'BOOKMOBI':
  125.                         raise DrmException("invalid file format")
  126.                 self.num_sections, = struct.unpack('>H', data_file[76:78])
  127.  
  128.                 self.sections = []
  129.                 for i in xrange(self.num_sections):
  130.                         offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
  131.                         flags, val = a1, a2<<16|a3<<8|a4
  132.                         self.sections.append( (offset, flags, val) )
  133.  
  134.                 sect = self.loadSection(0)
  135.                 records, = struct.unpack('>H', sect[0x8:0x8+2])
  136.                 extra_data_flags, = struct.unpack('>L', sect[0xF0:0xF4])
  137.  
  138.                 crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
  139.                 if crypto_type != 2:
  140.                         raise DrmException("invalid encryption type: %d" % crypto_type)
  141.  
  142.                 # calculate the keys
  143.                 drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
  144.                 found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
  145.                 if not found_key:
  146.                         raise DrmException("no key found. maybe the PID is incorrect")
  147.  
  148.                 # kill the drm keys
  149.                 self.patchSection(0, "\0" * drm_size, drm_ptr)
  150.                 # kill the drm pointers
  151.                 self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
  152.                 # clear the crypto type
  153.                 self.patchSection(0, "\0" * 2, 0xC)
  154.  
  155.                 # decrypt sections
  156.                 print "Decrypting. Please wait...",
  157.                 for i in xrange(1, records+1):
  158.                         data = self.loadSection(i)
  159.                         extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
  160.                         self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
  161.                 print "done"
  162.         def getResult(self):
  163.                 return self.data_file
  164.  
  165. print "MobiDeDrm v0.02. Copyright (c) 2008 The Dark Reverser"
  166. if len(sys.argv)<4:
  167.         print "Removes protection from Mobipocket books"
  168.         print "Usage:"
  169.         print "  mobidedrm infile.mobi outfile.mobi PID"
  170. else:  
  171.         infile = sys.argv[1]
  172.         outfile = sys.argv[2]
  173.         pid = sys.argv[3]
  174.         data_file = file(infile, 'rb').read()
  175.         try:
  176.                 file(outfile, 'wb').write(DrmStripper(data_file, pid).getResult())
  177.         except DrmException, e:
  178.                 print "Error: %s" % e
RAW Paste Data
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
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top