Skip to content

Commit

Permalink
MobiDeDRM fixes
Browse files Browse the repository at this point in the history
Change handling of PIDs to cope with byte arrays or strings passed in. Also fixed handling of a very old default key format.
  • Loading branch information
apprenticeharper committed Dec 26, 2020
1 parent d33f679 commit fdf0389
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
36 changes: 22 additions & 14 deletions DeDRM_plugin/mobidedrm.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,21 @@ def PC1(key, src, decryption=True):
dst+=bytes([curByte])
return dst

# accepts unicode returns unicode
def checksumPid(s):
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
crc = (~binascii.crc32(s.encode('utf-8'),-1))&0xFFFFFFFF
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l].encode('ascii')
res += letters[pos%l]
crc >>= 8
return res

# expects bytearray
def getSizeOfTrailingDataEntries(ptr, size, flags):
def getSizeOfTrailingDataEntry(ptr, size):
bitpos, result = 0, 0
Expand Down Expand Up @@ -282,7 +284,7 @@ def __init__(self, infile):
self.mobi_codepage = 1252
self.mobi_version = -1

if self.magic == 'TEXtREAd':
if self.magic == b'TEXtREAd':
print("PalmDoc format book detected.")
return

Expand All @@ -301,7 +303,7 @@ def __init__(self, infile):
# if exth region exists parse it for metadata array
try:
exth_flag, = struct.unpack('>L', self.sect[0x80:0x84])
exth = ''
exth = b''
if exth_flag & 0x40:
exth = self.sect[16 + self.mobi_length:]
if (len(exth) >= 12) and (exth[:4] == b'EXTH'):
Expand All @@ -323,12 +325,13 @@ def __init__(self, infile):
except Exception as e:
print("Cannot set meta_array: Error: {:s}".format(e.args[0]))

#returns unicode
def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = ''
title = b''
codec = 'windows-1252'
if self.magic == b'BOOKMOBI':
if 503 in self.meta_array:
Expand All @@ -339,7 +342,7 @@ def getBookTitle(self):
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '':
if title == b'':
title = self.header[:32]
title = title.split(b'\0')[0]
return title.decode(codec)
Expand All @@ -355,13 +358,15 @@ def getPIDMetaInfo(self):
# if that key exists in the meta_array, append its contents to the token
for i in range(0,len(data),5):
val, = struct.unpack('>I',data[i+1:i+5])
sval = self.meta_array.get(val,'')
sval = self.meta_array.get(val,b'')
token += sval
return rec209, token

# new must be byte array
def patch(self, off, new):
self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]

# new must be byte array
def patchSection(self, section, new, in_off = 0):
if (section + 1 == self.num_sections):
endoff = len(self.data_file)
Expand All @@ -371,12 +376,12 @@ def patchSection(self, section, new, in_off = 0):
assert off + in_off + len(new) <= endoff
self.patch(off + in_off, new)

# pids in pidlist must be unicode, returned key is byte array, pid is unicode
def parseDRM(self, data, count, pidlist):
found_key = None
keyvec1 = b'\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
for pid in pidlist:
bigpid = pid.ljust(16,b'\0')
bigpid = bigpid
bigpid = pid.encode('utf-8').ljust(16,b'\0')
temp_key = PC1(keyvec1, bigpid, False)
temp_key_sum = sum(temp_key) & 0xff
found_key = None
Expand Down Expand Up @@ -424,6 +429,7 @@ def getBookExtension(self):
return ".azw3"
return ".mobi"

# pids in pidlist may be unicode or bytearrays or bytes
def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
print("Crypto Type is: {0:d}".format(crypto_type))
Expand All @@ -445,6 +451,8 @@ def processBook(self, pidlist):
goodpids = []
# print("DEBUG ==== pidlist = ", pidlist)
for pid in pidlist:
if isinstance(pid,(bytearray,bytes)):
pid = pid.decode('utf-8')
if len(pid)==10:
if checksumPid(pid[0:-2]) != pid:
print("Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
Expand All @@ -457,8 +465,8 @@ def processBook(self, pidlist):
# print("======= DEBUG good pids = ", goodpids)

if self.crypto_type == 1:
t1_keyvec = 'QDCVEPMU675RUBSZ'
if self.magic == 'TEXtREAd':
t1_keyvec = b'QDCVEPMU675RUBSZ'
if self.magic == b'TEXtREAd':
bookkey_data = self.sect[0x0E:0x0E+16]
elif self.mobi_version < 0:
bookkey_data = self.sect[0x90:0x90+16]
Expand All @@ -473,7 +481,7 @@ def processBook(self, pidlist):
raise DrmException("Encryption not initialised. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key:
raise DrmException("No key found in {0:d} keys tried.".format(len(goodpids)))
raise DrmException("No key found in {0:d} PIDs tried.".format(len(goodpids)))
# kill the drm keys
self.patchSection(0, b'\0' * drm_size, drm_ptr)
# kill the drm pointers
Expand Down Expand Up @@ -509,6 +517,7 @@ def processBook(self, pidlist):
print("done")
return

# pids in pidlist must be unicode
def getUnencryptedBook(infile,pidlist):
if not os.path.isfile(infile):
raise DrmException("Input File Not Found.")
Expand All @@ -530,8 +539,7 @@ def cli_main():
infile = argv[1]
outfile = argv[2]
if len(argv) == 4:
# convert from unicode to bytearray before splitting.
pidlist = argv[3].encode('utf-8').split(b',')
pidlist = argv[3].split(',')
else:
pidlist = []
try:
Expand Down

0 comments on commit fdf0389

Please sign in to comment.