Guest User

Untitled

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