tools v2.2

This commit is contained in:
Apprentice Alf
2010-11-11 22:11:36 +00:00
parent 5f0671db7f
commit c386ac6e6d
100 changed files with 3157 additions and 4807 deletions

View File

@@ -1,62 +0,0 @@
Inept Epub DeDRM - ineptepub_vXX_plugin.zip
Requires Calibre version 0.6.44 or higher.
All credit given to I <3 Cabbages for the original standalone scripts.
I had the much easier job of converting them to a Calibre plugin.
This plugin is meant to decrypt Adobe Digital Edition Epubs that are protected
with Adobe's Adept encryption. It is meant to function without having to install
any dependencies... other than having Calibre installed, of course. It will still
work if you have Python and PyCrypto already installed, but they aren't necessary.
Installation:
Go to Calibre's Preferences page... click on the Plugins button. Use the file
dialog button to select the plugin's zip file (ineptepub_vXX_plugin.zip) and
click the 'Add' button. you're done.
Configuration:
When first run, the plugin will attempt to find your Adobe Digital Editions installation
(on Windows and Mac OS's). If successful, it will create an 'adeptkey.der' file and
save it in Calibre's configuration directory. It will use that file on subsequent runs.
If there are already '*.der' files in the directory, the plugin won't attempt to
find the Adobe Digital Editions installation installation.
So if you have Adobe Digital Editions installation installed on the same machine as Calibre...
you are ready to go. If not... keep reading.
If you already have keyfiles generated with I <3 Cabbages' ineptkey.pyw script,
you can put those keyfiles in Calibre's configuration directory. The easiest
way to find the correct directory is to go to Calibre's Preferences page... click
on the 'Miscellaneous' button (looks like a gear), and then click the 'Open Calibre
configuration directory' button. Paste your keyfiles in there. Just make sure that
they have different names and are saved with the '.der' extension (like the ineptkey
script produces). This directory isn't touched when upgrading Calibre, so it's quite
safe to leave them there.
Since there is no Linux version of Adobe Digital Editions, Linux users will have to
obtain a keyfile through other methods and put the file in Calibre's configuration directory.
All keyfiles with a '.der' extension found in Calibre's configuration directory will
be used to attempt to decrypt a book.
** NOTE ** There is no plugin customization data for the Inept Epub DeDRM plugin.
Troubleshooting:
If you find that it's not working for you (imported epubs still have DRM), you can
save a lot of time and trouble by trying to add the epub to Calibre with the command
line tools. This will print out a lot of helpful debugging info that can be copied into
any online help requests. I'm going to ask you to do it first, anyway, so you might
as well get used to it. ;)
Open a command prompt (terminal) and change to the directory where the ebook you're
trying to import resides. Then type the command "calibredb add your_ebook.epub".
Don't type the quotes and obviously change the 'your_ebook.epub' to whatever the
filename of your book is. Copy the resulting output and paste it into any online
help request you make.
** Note: the Mac version of Calibre doesn't install the command line tools by default.
If you go to the 'Preferences' page and click on the miscellaneous button, you'll
see the option to install the command line tools.

View File

@@ -19,24 +19,86 @@ class ADEPTError(Exception):
if iswindows:
from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
string_at, Structure, c_void_p, cast, c_size_t, memmove
string_at, Structure, c_void_p, cast, c_size_t, memmove, CDLL, c_int, \
c_long, c_ulong
from ctypes.wintypes import LPVOID, DWORD, BOOL
import _winreg as winreg
def _load_crypto_libcrypto():
from ctypes.util import find_library
libcrypto = find_library('libeay32')
if libcrypto is None:
raise ADEPTError('libcrypto not found')
libcrypto = CDLL(libcrypto)
AES_MAXNR = 14
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
try:
from Crypto.Cipher import AES as _aes
except ImportError:
_aes = None
def F(restype, name, argtypes):
func = getattr(libcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
[c_char_p, c_int, AES_KEY_p])
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
c_int])
class AES(object):
def __init__(self, userkey):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise ADEPTError('AES improper key used')
key = self._key = AES_KEY()
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
if rv < 0:
raise ADEPTError('Failed to initialize AES key')
def decrypt(self, data):
out = create_string_buffer(len(data))
iv = ("\x00" * self._blocksize)
rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
if rv == 0:
raise ADEPTError('AES decryption failed')
return out.raw
return AES
def _load_crypto_pycrypto():
from Crypto.Cipher import AES as _AES
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC)
def decrypt(self, data):
return self._aes.decrypt(data)
return AES
def _load_crypto():
AES = None
for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto):
try:
AES = loader()
break
except (ImportError, ADEPTError):
pass
return AES
AES = _load_crypto()
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
def GetSystemDirectory():
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
@@ -47,7 +109,7 @@ if iswindows:
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
@@ -61,7 +123,7 @@ if iswindows:
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
@@ -75,11 +137,11 @@ if iswindows:
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
def VirtualAlloc():
_VirtualAlloc = kernel32.VirtualAlloc
_VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
@@ -89,9 +151,9 @@ if iswindows:
return _VirtualAlloc(addr, size, alloctype, protect)
return VirtualAlloc
VirtualAlloc = VirtualAlloc()
MEM_RELEASE = 0x8000
def VirtualFree():
_VirtualFree = kernel32.VirtualFree
_VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
@@ -100,22 +162,22 @@ if iswindows:
return _VirtualFree(addr, size, freetype)
return VirtualFree
VirtualFree = VirtualFree()
class NativeFunction(object):
def __init__(self, restype, argtypes, insns):
self._buf = buf = VirtualAlloc(None, len(insns))
memmove(buf, insns, len(insns))
ftype = CFUNCTYPE(restype, *argtypes)
self._native = ftype(buf)
def __call__(self, *args):
return self._native(*args)
def __del__(self):
if self._buf is not None:
VirtualFree(self._buf)
self._buf = None
if struct.calcsize("P") == 4:
CPUID0_INSNS = (
"\x53" # push %ebx
@@ -157,7 +219,7 @@ if iswindows:
"\x5b" # pop %rbx
"\xc3" # retq
)
def cpuid0():
_cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
buf = create_string_buffer(12)
@@ -166,14 +228,14 @@ if iswindows:
return buf.raw
return cpuid0
cpuid0 = cpuid0()
cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
@@ -191,10 +253,14 @@ if iswindows:
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def retrieve_key():
if _aes is None:
raise ADEPTError("Couldn\'t load PyCrypto")
if AES is None:
tkMessageBox.showerror(
"ADEPT Key",
"This script requires PyCrypto or OpenSSL which must be installed "
"separately. Read the top-of-script comment for details.")
return False
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
@@ -236,7 +302,8 @@ if iswindows:
if userkey is None:
raise ADEPTError('Could not locate privateLicenseKey')
userkey = userkey.decode('base64')
userkey = _aes.new(keykey, _aes.MODE_CBC).decrypt(userkey)
aes = AES(keykey)
userkey = aes.decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
return userkey

View File

@@ -1,6 +1,6 @@
#! /usr/bin/python
# ineptepub_v01_plugin.py
# ineptepub_plugin.py
# Released under the terms of the GNU General Public Licence, version 3 or
# later. <http://www.gnu.org/licenses/>
#
@@ -41,6 +41,8 @@
#
# Revision history:
# 0.1 - Initial release
# 0.1.1 - Allow Windows users to make use of openssl if they have it installed.
# - Incorporated SomeUpdates zipfix routine.
"""
@@ -76,7 +78,10 @@ def _load_crypto_libcrypto():
Structure, c_ulong, create_string_buffer, cast
from ctypes.util import find_library
libcrypto = find_library('crypto')
if sys.platform.startswith('win'):
libcrypto = find_library('libeay32')
else:
libcrypto = find_library('crypto')
if libcrypto is None:
raise ADEPTError('libcrypto not found')
libcrypto = CDLL(libcrypto)
@@ -358,7 +363,7 @@ class IneptDeDRM(FileTypePlugin):
Credit given to I <3 Cabbages for the original stand-alone scripts.'
supported_platforms = ['linux', 'osx', 'windows']
author = 'DiapDealer'
version = (0, 1, 0)
version = (0, 1, 1)
minimum_calibre_version = (0, 6, 44) # Compiled python libraries cannot be imported in earlier versions.
file_types = set(['epub'])
on_import = True
@@ -376,7 +381,6 @@ class IneptDeDRM(FileTypePlugin):
# Add the included Carbon import directory for Mac users.
pdir = 'windows' if iswindows else 'osx' if isosx else 'linux'
ppath = os.path.join(self.sys_insertion_path, pdir)
#sys.path.insert(0, ppath)
sys.path.append(ppath)
AES, RSA = _load_crypto()
@@ -433,10 +437,19 @@ class IneptDeDRM(FileTypePlugin):
# Attempt to decrypt epub with each encryption key found.
for userkey in userkeys:
# Create a TemporaryPersistent file to work with.
# Check original epub archive for zip errors.
import zipfix
inf = self.temporary_file('.epub')
try:
fr = zipfix.fixZip(path_to_ebook, inf.name)
fr.fix()
except Exception, e:
raise Exception(e)
return
of = self.temporary_file('.epub')
# Give the user key, ebook and TemporaryPersistent file to the plugin_main function.
result = plugin_main(userkey, path_to_ebook, of.name)
result = plugin_main(userkey, inf.name, of.name)
# Ebook is not an Adobe Adept epub... do nothing and pass it on.
# This allows a non-encrypted epub to be imported without error messages.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,136 @@
#!/usr/bin/env python
import sys
import zlib
import zipfile
import os
import os.path
import getopt
from struct import unpack
_FILENAME_LEN_OFFSET = 26
_EXTRA_LEN_OFFSET = 28
_FILENAME_OFFSET = 30
_MAX_SIZE = 64 * 1024
class fixZip:
def __init__(self, zinput, zoutput):
self.inzip = zipfile.ZipFile(zinput,'r')
self.outzip = zipfile.ZipFile(zoutput,'w')
# open the input zip for reading only as a raw file
self.bzf = file(zinput,'rb')
def getlocalname(self, zi):
local_header_offset = zi.header_offset
self.bzf.seek(local_header_offset + _FILENAME_LEN_OFFSET)
leninfo = self.bzf.read(2)
local_name_length, = unpack('<H', leninfo)
self.bzf.seek(local_header_offset + _FILENAME_OFFSET)
local_name = self.bzf.read(local_name_length)
return local_name
def uncompress(self, cmpdata):
dc = zlib.decompressobj(-15)
data = ''
while len(cmpdata) > 0:
if len(cmpdata) > _MAX_SIZE :
newdata = cmpdata[0:_MAX_SIZE]
cmpdata = cmpdata[_MAX_SIZE:]
else:
newdata = cmpdata
cmpdata = ''
newdata = dc.decompress(newdata)
unprocessed = dc.unconsumed_tail
if len(unprocessed) == 0:
newdata += dc.flush()
data += newdata
cmpdata += unprocessed
unprocessed = ''
return data
def getfiledata(self, zi):
# get file name length and exta data length to find start of file data
local_header_offset = zi.header_offset
self.bzf.seek(local_header_offset + _FILENAME_LEN_OFFSET)
leninfo = self.bzf.read(2)
local_name_length, = unpack('<H', leninfo)
self.bzf.seek(local_header_offset + _EXTRA_LEN_OFFSET)
exinfo = self.bzf.read(2)
extra_field_length, = unpack('<H', exinfo)
self.bzf.seek(local_header_offset + _FILENAME_OFFSET + local_name_length + extra_field_length)
data = None
# if not compressed we are good to go
if zi.compress_type == zipfile.ZIP_STORED:
data = self.bzf.read(zi.file_size)
# if compressed we must decompress it using zlib
if zi.compress_type == zipfile.ZIP_DEFLATED:
cmpdata = self.bzf.read(zi.compress_size)
data = self.uncompress(cmpdata)
return data
def fix(self):
# get the zipinfo for each member of the input archive
# and copy member over to output archive
# if problems exist with local vs central filename, fix them
for i, zinfo in enumerate(self.inzip.infolist()):
data = None
nzinfo = zinfo
try:
data = self.inzip.read(zinfo)
except zipfile.BadZipfile or zipfile.error:
local_name = self.getlocalname(zinfo)
data = self.getfiledata(zinfo)
nzinfo.filename = local_name
nzinfo.date_time = zinfo.date_time
nzinfo.compress_type = zinfo.compress_type
nzinfo.flag_bits = 0
nzinfo.internal_attr = 0
self.outzip.writestr(nzinfo,data)
self.bzf.close()
self.inzip.close()
self.outzip.close()
def usage():
print """usage: zipfix.py inputzip outputzip
inputzip is the source zipfile to fix
outputzip is the fixed zip archive
"""
def main(argv=sys.argv):
if len(argv)!=3:
usage()
return 1
infile = None
outfile = None
infile = argv[1]
outfile = argv[2]
if not os.path.exists(infile):
print "Error: Input Zip File does not exist"
return 1
try:
fr = fixZip(infile, outfile)
fr.fix()
return 0
except Exception, e:
print "Error Occurred ", e
return 2
if __name__ == '__main__' :
sys.exit(main())