First version of kindlepid and kindlefix scripts

This commit is contained in:
Igor Skochinsky 2007-12-12 17:38:30 +00:00 committed by Apprentice Alf
parent 69b8df6f72
commit fa62e11f8c
3 changed files with 235 additions and 0 deletions

View 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]))

View 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>"

View 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