mirror of
https://github.com/apprenticeharper/DeDRM_tools
synced 2024-12-26 09:58:55 +01:00
First version of kindlepid and kindlefix scripts
This commit is contained in:
parent
69b8df6f72
commit
fa62e11f8c
3 changed files with 235 additions and 0 deletions
156
Kindle_Mobi_Tools/lib/kindlefix.py
Normal file
156
Kindle_Mobi_Tools/lib/kindlefix.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
import prc, sys, struct
|
||||
from binascii import hexlify
|
||||
|
||||
def strByte(s,off=0):
|
||||
return struct.unpack(">B",s[off])[0];
|
||||
|
||||
def strSWord(s,off=0):
|
||||
return struct.unpack(">h",s[off:off+2])[0];
|
||||
|
||||
def strWord(s,off=0):
|
||||
return struct.unpack(">H",s[off:off+2])[0];
|
||||
|
||||
def strDWord(s,off=0):
|
||||
return struct.unpack(">L",s[off:off+4])[0];
|
||||
|
||||
def strPutDWord(s,off,i):
|
||||
return s[:off]+struct.pack(">L",i)+s[off+4:];
|
||||
|
||||
keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
|
||||
|
||||
#implementation of Pukall Cipher 1
|
||||
def PC1(key, src, decryption=True):
|
||||
sum1 = 0;
|
||||
sum2 = 0;
|
||||
keyXorVal = 0;
|
||||
if len(key)!=16:
|
||||
print "Bad key length!"
|
||||
return None
|
||||
wkey = []
|
||||
for i in xrange(8):
|
||||
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
|
||||
|
||||
dst = ""
|
||||
for i in xrange(len(src)):
|
||||
temp1 = 0;
|
||||
byteXorVal = 0;
|
||||
for j in xrange(8):
|
||||
temp1 ^= wkey[j]
|
||||
sum2 = (sum2+j)*20021 + sum1
|
||||
sum1 = (temp1*346)&0xFFFF
|
||||
sum2 = (sum2+sum1)&0xFFFF
|
||||
temp1 = (temp1*20021+1)&0xFFFF
|
||||
byteXorVal ^= temp1 ^ sum2
|
||||
|
||||
curByte = ord(src[i])
|
||||
if not decryption:
|
||||
keyXorVal = curByte * 257;
|
||||
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
|
||||
if decryption:
|
||||
keyXorVal = curByte * 257;
|
||||
for j in xrange(8):
|
||||
wkey[j] ^= keyXorVal;
|
||||
|
||||
dst+=chr(curByte)
|
||||
|
||||
return dst
|
||||
|
||||
def find_key(rec0, pid):
|
||||
off1 = strDWord(rec0, 0xA8)
|
||||
if off1==0xFFFFFFFF or off1==0:
|
||||
print "No DRM"
|
||||
return None
|
||||
size1 = strDWord(rec0, 0xB0)
|
||||
cnt = strDWord(rec0, 0xAC)
|
||||
flag = strDWord(rec0, 0xB4)
|
||||
|
||||
temp_key = PC1(keyvec1, pid.ljust(16,'\0'), False)
|
||||
cksum = 0
|
||||
#print pid, "->", hexlify(temp_key)
|
||||
for i in xrange(len(temp_key)):
|
||||
cksum += ord(temp_key[i])
|
||||
cksum &= 0xFF
|
||||
temp_key = temp_key.ljust(16,'\0')
|
||||
#print "pid cksum: %02X"%cksum
|
||||
|
||||
#print "Key records: %02X-%02X, count: %d, flag: %02X"%(off1, off1+size1, cnt, flag)
|
||||
iOff = off1
|
||||
drm_key = None
|
||||
for i in xrange(cnt):
|
||||
dwCheck = strDWord(rec0, iOff)
|
||||
dwSize = strDWord(rec0, iOff+4)
|
||||
dwType = strDWord(rec0, iOff+8)
|
||||
nCksum = strByte(rec0, iOff+0xC)
|
||||
#print "Key record %d: check=%08X, size=%d, type=%d, cksum=%02X"%(i, dwCheck, dwSize, dwType, nCksum)
|
||||
if nCksum==cksum:
|
||||
drmInfo = PC1(temp_key, rec0[iOff+0x10:iOff+0x30])
|
||||
dw0, dw4, dw18, dw1c = struct.unpack(">II16xII", drmInfo)
|
||||
#print "Decrypted drmInfo:", "%08X, %08X, %s, %08X, %08X"%(dw0, dw4, hexlify(drmInfo[0x8:0x18]), dw18, dw1c)
|
||||
#print "Decrypted drmInfo:", hexlify(drmInfo)
|
||||
if dw0==dwCheck:
|
||||
print "Found the matching record; setting the CustomDRM flag for Kindle"
|
||||
drmInfo = strPutDWord(drmInfo,4,(dw4|0x800))
|
||||
dw0, dw4, dw18, dw1c = struct.unpack(">II16xII", drmInfo)
|
||||
#print "Updated drmInfo:", "%08X, %08X, %s, %08X, %08X"%(dw0, dw4, hexlify(drmInfo[0x8:0x18]), dw18, dw1c)
|
||||
return rec0[:iOff+0x10] + PC1(temp_key, drmInfo, False) + rec0[:iOff+0x30]
|
||||
iOff += dwSize
|
||||
return None
|
||||
|
||||
def replaceext(filename, newext):
|
||||
nameparts = filename.split(".")
|
||||
if len(nameparts)>1:
|
||||
return (".".join(nameparts[:-1]))+newext
|
||||
else:
|
||||
return nameparts[0]+newext
|
||||
|
||||
def main(fname, pid):
|
||||
if len(pid)==10 and pid[-3]=='*':
|
||||
pid = pid[:-2]
|
||||
if len(pid)!=8 or pid[-1]!='*':
|
||||
print "PID is not valid! (should be in format AAAAAAA*DD)"
|
||||
return 3
|
||||
db = prc.File(fname)
|
||||
#print dir(db)
|
||||
if db.getDBInfo()["creator"]!='MOBI':
|
||||
print "Not a Mobi file!"
|
||||
return 1
|
||||
rec0 = db.getRecord(0)[0]
|
||||
enc = strSWord(rec0, 0xC)
|
||||
print "Encryption:", enc
|
||||
if enc!=2:
|
||||
print "Unknown encryption type"
|
||||
return 1
|
||||
|
||||
if len(rec0)<0x28 or rec0[0x10:0x14] != 'MOBI':
|
||||
print "bad file format"
|
||||
return 1
|
||||
print "Mobi publication type:", strDWord(rec0, 0x18)
|
||||
formatVer = strDWord(rec0, 0x24)
|
||||
print "Mobi format version:", formatVer
|
||||
last_rec = strWord(rec0, 8)
|
||||
dwE0 = 0
|
||||
if formatVer>=4:
|
||||
new_rec0 = find_key(rec0, pid)
|
||||
if new_rec0:
|
||||
db.setRecordIdx(0,new_rec0)
|
||||
else:
|
||||
print "PID doesn't match this file"
|
||||
return 2
|
||||
else:
|
||||
print "Wrong Mobi format version"
|
||||
return 1
|
||||
|
||||
outfname = replaceext(fname, ".azw")
|
||||
if outfname==fname:
|
||||
outfname = replaceext(fname, "_fixed.azw")
|
||||
db.save(outfname)
|
||||
print "Output written to "+outfname
|
||||
return 0
|
||||
|
||||
print "The Kindleizer v0.1. Copyright (c) 2007 Igor Skochinsky"
|
||||
if len(sys.argv)<3:
|
||||
print "Fixes encrypted Mobipocket books to be readable by Kindle"
|
||||
print "Usage: kindlefix.py file.mobi PID"
|
||||
else:
|
||||
fname = sys.argv[1]
|
||||
sys.exit(main(fname, sys.argv[2]))
|
45
Kindle_Mobi_Tools/lib/kindlepid.py
Normal file
45
Kindle_Mobi_Tools/lib/kindlepid.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
import sys, binascii
|
||||
|
||||
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
|
||||
|
||||
def crc32(s):
|
||||
return (~binascii.crc32(s,-1))&0xFFFFFFFF
|
||||
|
||||
def checksumPid(s):
|
||||
crc = crc32(s)
|
||||
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]
|
||||
crc >>= 8
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def pidFromSerial(s, l):
|
||||
crc = crc32(s)
|
||||
|
||||
arr1 = [0]*l
|
||||
for i in xrange(len(s)):
|
||||
arr1[i%l] ^= ord(s[i])
|
||||
|
||||
crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
|
||||
for i in xrange(l):
|
||||
arr1[i] ^= crc_bytes[i&3]
|
||||
|
||||
pid = ""
|
||||
for i in xrange(l):
|
||||
b = arr1[i] & 0xff
|
||||
pid+=letters[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
|
||||
|
||||
return pid
|
||||
|
||||
print "Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007 Igor Skochinsky <skochinsky@mail.ru>"
|
||||
if len(sys.argv)>1:
|
||||
pid = pidFromSerial(sys.argv[1],7)+"*"
|
||||
print "Mobipocked PID for Kindle serial# "+sys.argv[1]+" is "+checksumPid(pid)
|
||||
else:
|
||||
print "Usage: kindlepid.py <Kindle Serial Number>"
|
34
Kindle_Mobi_Tools/lib/readme.txt
Normal file
34
Kindle_Mobi_Tools/lib/readme.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
Kindle Mobipocket tools 0.1
|
||||
Copyright (c) 2007 Igor Skochinsky
|
||||
|
||||
These scripts allow one to read legally purchased Secure Mobipocket books
|
||||
on Amazon Kindle.
|
||||
|
||||
* kindlepid.py
|
||||
This script generates Mobipocket PID from the Kindle serial number. That
|
||||
PID can then be added at a Mobi retailer site and used for downloading
|
||||
books locked to the Kindle.
|
||||
|
||||
Example:
|
||||
|
||||
> kindlepid.py B001BAB012345678
|
||||
Mobipocket PID calculator for Amazon Kindle. Copyright (c) 2007 Igor Skochinsky <skochinsky@mail.ru>
|
||||
Mobipocked PID for Kindle serial# B001BAB012345678 is V176CXM*FZ
|
||||
|
||||
* kindlefix.py
|
||||
This script adds a "CustomDRM" flag necessary for opening Secure
|
||||
Mobipocket books on Kindle. The book has to be enabled for Kindle's PID
|
||||
(generated by kindlepid.py). The "fixed" book is written with
|
||||
extension ".azw". That file can then be uploaded to Kindle for reading.
|
||||
|
||||
Example:
|
||||
> kindlefix.py MyBook.mobi V176CXM*FZ
|
||||
The Kindleizer v0.1. Copyright (c) 2007 Igor Skochinsky
|
||||
Encryption: 2
|
||||
Mobi publication type: 2
|
||||
Mobi format version: 4
|
||||
Found the matching record; setting the CustomDRM flag for Kindle
|
||||
Output written to MyBook.azw
|
||||
|
||||
* History
|
||||
2007-12-12 Initial release
|
Loading…
Reference in a new issue