tools v1.8

This commit is contained in:
Apprentice Alf
2010-07-24 12:55:24 +01:00
parent 92dafd94b2
commit d427f758f6
18 changed files with 1081 additions and 495 deletions

View File

@@ -1,18 +1,29 @@
#! /usr/bin/python
# -*- coding: utf-8 -*-
# ineptepub.pyw, version 4.1
# ineptepub.pyw, version 5.2
# Copyright © 2009-2010 i♥cabbages
# To run this program install Python 2.6 from http://www.python.org/download/
# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# (make sure to install the version for Python 2.6). Save this script file as
# ineptepub.pyw and double-click on it to run it.
# Released under the terms of the GNU General Public Licence, version 3 or
# later. <http://www.gnu.org/licenses/>
# Windows users: Before running this program, you must first install Python 2.6
# from <http://www.python.org/download/> and PyCrypto from
# <http://www.voidspace.org.uk/python/modules.shtml#pycrypto> (make sure to
# install the version for Python 2.6). Save this script file as
# ineptepub.pyw and double-click on it to run it.
#
# Mac OS X users: Save this script file as ineptepub.pyw. You can run this
# program from the command line (pythonw ineptepub.pyw) or by double-clicking
# it when it has been associated with PythonLauncher.
# Revision history:
# 1 - Initial release
# 2 - Rename to INEPT, fix exit code
# 3 - Add cmd or gui choosing
# 4 - support for 1.7.2 support (anon)
# 4.1 - backward compatibility for 1.7.1 and old adeptkey.der
# 5 - Version bump to avoid (?) confusion;
# Improve OS X support by using OpenSSL when available
# 5.1 - Improve OpenSSL error checking
# 5.2 - Fix ctypes error causing segfaults on some systems
"""
Decrypt Adobe ADEPT-encrypted EPUB books.
@@ -34,117 +45,224 @@ import Tkconstants
import tkFileDialog
import tkMessageBox
try:
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
except ImportError:
AES = None
RSA = None
class ADEPTError(Exception):
pass
def _load_crypto_libcrypto():
from ctypes import CDLL, POINTER, c_void_p, c_char_p, c_int, c_long, \
Structure, c_ulong, create_string_buffer, cast
from ctypes.util import find_library
libcrypto = find_library('crypto')
if libcrypto is None:
raise ADEPTError('libcrypto not found')
libcrypto = CDLL(libcrypto)
RSA_NO_PADDING = 3
AES_MAXNR = 14
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
class RSA(Structure):
pass
RSA_p = POINTER(RSA)
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
def F(restype, name, argtypes):
func = getattr(libcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
d2i_RSAPrivateKey = F(RSA_p, 'd2i_RSAPrivateKey',
[RSA_p, c_char_pp, c_long])
RSA_size = F(c_int, 'RSA_size', [RSA_p])
RSA_private_decrypt = F(c_int, 'RSA_private_decrypt',
[c_int, c_char_p, c_char_p, RSA_p, c_int])
RSA_free = F(None, 'RSA_free', [RSA_p])
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 RSA(object):
def __init__(self, der):
buf = create_string_buffer(der)
pp = c_char_pp(cast(buf, c_char_p))
rsa = self._rsa = d2i_RSAPrivateKey(None, pp, len(der))
if rsa is None:
raise ADEPTError('Error parsing ADEPT user key DER')
def decrypt(self, from_):
rsa = self._rsa
to = create_string_buffer(RSA_size(rsa))
dlen = RSA_private_decrypt(len(from_), from_, to, rsa,
RSA_NO_PADDING)
if dlen < 0:
raise ADEPTError('RSA decryption failed')
return to[:dlen]
def __del__(self):
if self._rsa is not None:
RSA_free(self._rsa)
self._rsa = None
class AES(object):
def __init__(self, userkey):
self._blocksize = len(userkey)
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, RSA)
def _load_crypto_pycrypto():
from Crypto.Cipher import AES as _AES
from Crypto.PublicKey import RSA as _RSA
# ASN.1 parsing code from tlslite
class ASN1Error(Exception):
pass
class ASN1Parser(object):
class Parser(object):
def __init__(self, bytes):
self.bytes = bytes
self.index = 0
def get(self, length):
if self.index + length > len(self.bytes):
raise ASN1Error("Error decoding ASN.1")
x = 0
for count in range(length):
x <<= 8
x |= self.bytes[self.index]
self.index += 1
return x
def getFixBytes(self, lengthBytes):
bytes = self.bytes[self.index : self.index+lengthBytes]
self.index += lengthBytes
return bytes
def getVarBytes(self, lengthLength):
lengthBytes = self.get(lengthLength)
return self.getFixBytes(lengthBytes)
def getFixList(self, length, lengthList):
l = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
def getVarList(self, length, lengthLength):
lengthList = self.get(lengthLength)
if lengthList % length != 0:
raise ASN1Error("Error decoding ASN.1")
lengthList = int(lengthList/length)
l = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
def startLengthCheck(self, lengthLength):
self.lengthCheck = self.get(lengthLength)
self.indexCheck = self.index
def setLengthCheck(self, length):
self.lengthCheck = length
self.indexCheck = self.index
def stopLengthCheck(self):
if (self.index - self.indexCheck) != self.lengthCheck:
raise ASN1Error("Error decoding ASN.1")
def atLengthCheck(self):
if (self.index - self.indexCheck) < self.lengthCheck:
return False
elif (self.index - self.indexCheck) == self.lengthCheck:
return True
else:
raise ASN1Error("Error decoding ASN.1")
def __init__(self, bytes):
p = self.Parser(bytes)
p.get(1)
self.length = self._getASN1Length(p)
self.value = p.getFixBytes(self.length)
def getChild(self, which):
p = self.Parser(self.value)
for x in range(which+1):
markIndex = p.index
p.get(1)
length = self._getASN1Length(p)
p.getFixBytes(length)
return ASN1Parser(p.bytes[markIndex:p.index])
def _getASN1Length(self, p):
firstLength = p.get(1)
if firstLength<=127:
return firstLength
else:
lengthLength = firstLength & 0x7F
return p.get(lengthLength)
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC)
def decrypt(self, data):
return self._aes.decrypt(data)
class RSA(object):
def __init__(self, der):
key = ASN1Parser([ord(x) for x in der])
key = [key.getChild(x).value for x in xrange(1, 4)]
key = [self.bytesToNumber(v) for v in key]
self._rsa = _RSA.construct(key)
def bytesToNumber(self, bytes):
total = 0L
for byte in bytes:
total = (total << 8) + byte
return total
def decrypt(self, data):
return self._rsa.decrypt(data)
return (AES, RSA)
def _load_crypto():
AES = RSA = None
for loader in (_load_crypto_libcrypto, _load_crypto_pycrypto):
try:
AES, RSA = loader()
break
except (ImportError, ADEPTError):
pass
return (AES, RSA)
AES, RSA = _load_crypto()
META_NAMES = ('mimetype', 'META-INF/rights.xml', 'META-INF/encryption.xml')
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
# ASN.1 parsing code from tlslite
def bytesToNumber(bytes):
total = 0L
multiplier = 1L
for count in range(len(bytes)-1, -1, -1):
byte = bytes[count]
total += multiplier * byte
multiplier *= 256
return total
class ASN1Error(Exception):
pass
class ASN1Parser(object):
class Parser(object):
def __init__(self, bytes):
self.bytes = bytes
self.index = 0
def get(self, length):
if self.index + length > len(self.bytes):
raise ASN1Error("Error decoding ASN.1")
x = 0
for count in range(length):
x <<= 8
x |= self.bytes[self.index]
self.index += 1
return x
def getFixBytes(self, lengthBytes):
bytes = self.bytes[self.index : self.index+lengthBytes]
self.index += lengthBytes
return bytes
def getVarBytes(self, lengthLength):
lengthBytes = self.get(lengthLength)
return self.getFixBytes(lengthBytes)
def getFixList(self, length, lengthList):
l = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
def getVarList(self, length, lengthLength):
lengthList = self.get(lengthLength)
if lengthList % length != 0:
raise ASN1Error("Error decoding ASN.1")
lengthList = int(lengthList/length)
l = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
def startLengthCheck(self, lengthLength):
self.lengthCheck = self.get(lengthLength)
self.indexCheck = self.index
def setLengthCheck(self, length):
self.lengthCheck = length
self.indexCheck = self.index
def stopLengthCheck(self):
if (self.index - self.indexCheck) != self.lengthCheck:
raise ASN1Error("Error decoding ASN.1")
def atLengthCheck(self):
if (self.index - self.indexCheck) < self.lengthCheck:
return False
elif (self.index - self.indexCheck) == self.lengthCheck:
return True
else:
raise ASN1Error("Error decoding ASN.1")
def __init__(self, bytes):
p = self.Parser(bytes)
p.get(1)
self.length = self._getASN1Length(p)
self.value = p.getFixBytes(self.length)
def getChild(self, which):
p = self.Parser(self.value)
for x in range(which+1):
markIndex = p.index
p.get(1)
length = self._getASN1Length(p)
p.getFixBytes(length)
return ASN1Parser(p.bytes[markIndex:p.index])
def _getASN1Length(self, p):
firstLength = p.get(1)
if firstLength<=127:
return firstLength
else:
lengthLength = firstLength & 0x7F
return p.get(lengthLength)
class ZipInfo(zipfile.ZipInfo):
def __init__(self, *args, **kwargs):
if 'compress_type' in kwargs:
@@ -152,11 +270,10 @@ class ZipInfo(zipfile.ZipInfo):
super(ZipInfo, self).__init__(*args, **kwargs)
self.compress_type = compress_type
class Decryptor(object):
def __init__(self, bookkey, encryption):
enc = lambda tag: '{%s}%s' % (NSMAP['enc'], tag)
self._aes = AES.new(bookkey, AES.MODE_CBC)
self._aes = AES(bookkey)
encryption = etree.fromstring(encryption)
self._encrypted = encrypted = set()
expr = './%s/%s/%s' % (enc('EncryptedData'), enc('CipherData'),
@@ -165,7 +282,7 @@ class Decryptor(object):
path = elem.get('URI', None)
if path is not None:
encrypted.add(path)
def decompress(self, bytes):
dc = zlib.decompressobj(-15)
bytes = dc.decompress(bytes)
@@ -173,7 +290,7 @@ class Decryptor(object):
if ex:
bytes = bytes + ex
return bytes
def decrypt(self, path, data):
if path in self._encrypted:
data = self._aes.decrypt(data)[16:]
@@ -181,16 +298,12 @@ class Decryptor(object):
data = self.decompress(data)
return data
class ADEPTError(Exception):
pass
def cli_main(argv=sys.argv):
progname = os.path.basename(argv[0])
if AES is None:
print "%s: This script requires PyCrypto, which must be installed " \
"separately. Read the top-of-script comment for details." % \
(progname,)
print "%s: This script requires OpenSSL or PyCrypto, which must be" \
" installed separately. Read the top-of-script comment for" \
" details." % (progname,)
return 1
if len(argv) != 4:
print "usage: %s KEYFILE INBOOK OUTBOOK" % (progname,)
@@ -198,9 +311,7 @@ def cli_main(argv=sys.argv):
keypath, inpath, outpath = argv[1:]
with open(keypath, 'rb') as f:
keyder = f.read()
key = ASN1Parser([ord(x) for x in keyder])
key = [bytesToNumber(key.getChild(x).value) for x in xrange(1, 4)]
rsa = RSA.construct(key)
rsa = RSA(keyder)
with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = set(inf.namelist())
if 'META-INF/rights.xml' not in namelist or \
@@ -227,7 +338,6 @@ def cli_main(argv=sys.argv):
outf.writestr(path, decryptor.decrypt(path, data))
return 0
class DecryptionDialog(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root, border=5)
@@ -328,8 +438,9 @@ def gui_main():
root.withdraw()
tkMessageBox.showerror(
"INEPT EPUB Decrypter",
"This script requires PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
"This script requires OpenSSL or PyCrypto, which must be"
" installed separately. Read the top-of-script comment for"
" details.")
return 1
root.title('INEPT EPUB Decrypter')
root.resizable(True, False)

View File

@@ -1,25 +1,38 @@
#! /usr/bin/python
# -*- coding: utf-8 -*-
# ineptkey.pyw, version 4.4
# ineptkeyv44
# ineptkey.pyw, version 5
# Copyright © 2009-2010 i♥cabbages
# To run this program install Python 2.6 from http://www.python.org/download/
# and PyCrypto from http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# (make sure to install the version for Python 2.6). Save this script file as
# ineptkey.pyw and double-click on it to run it. It will create a file named
# adeptkey.der in the same directory. These are your ADEPT user keys.
# Released under the terms of the GNU General Public Licence, version 3 or
# later. <http://www.gnu.org/licenses/>
# Windows users: Before running this program, you must first install Python 2.6
# from <http://www.python.org/download/> and PyCrypto from
# <http://www.voidspace.org.uk/python/modules.shtml#pycrypto> (make certain
# to install the version for Python 2.6). Then save this script file as
# ineptkey.pyw and double-click on it to run it. It will create a file named
# adeptkey.der in the same directory. This is your ADEPT user key.
#
# Mac OS X users: Save this script file as ineptkey.pyw. You can run this
# program from the command line (pythonw ineptkey.pyw) or by double-clicking
# it when it has been associated with PythonLauncher. It will create a file
# named adeptkey.der in the same directory. This is your ADEPT user key.
# Revision history:
# 1 - Initial release, for Adobe Digital Editions 1.7
# 2 - Better algorithm for finding pLK; improved error handling
# 3 - Rename to INEPT
# 4 - Series of changes by joblack (and others?) --
# 4.1 - quick beta fix for ADE 1.7.2 (anon)
# 4.2 - added old 1.7.1 processing
# 4.3 - better key search
# 4.4 - Make it working on 64-bit Python
# 5 - Clean up and improve 4.x changes;
# Clean up and merge OS X support by unknown
"""
Retrieve Adobe ADEPT user key under Windows.
Retrieve Adobe ADEPT user key.
"""
from __future__ import with_statement
@@ -28,246 +41,305 @@ __license__ = 'GPL v3'
import sys
import os
from struct import pack
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_ulonglong, \
sizeof, c_void_p, c_size_t
import _winreg as winreg
import struct
import Tkinter
import Tkconstants
import tkMessageBox
import traceback
import hashlib
import pickle
try:
from Crypto.Cipher import AES
except ImportError:
AES = None
DEVICE_KEY = 'Software\\Adobe\\Adept\\Device'
PRIVATE_LICENCE_KEY = 'Software\\Adobe\\Adept\\Activation\\%04d'
PRIVATE_LICENCE_KEY_KEY = 'Software\\Adobe\\Adept\\Activation\\%04d\\%04d'
ACTIVATION = 'Software\\Adobe\\Adept\\Activation\\'
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
class ADEPTError(Exception):
pass
if sys.platform.startswith('win'):
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
from ctypes.wintypes import LPVOID, DWORD, BOOL
import _winreg as winreg
try:
from Crypto.Cipher import AES
except ImportError:
AES = None
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]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(
path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
if sizeof(c_void_p) == 4:
## 32-bit Python
CPUID0_INSNS = create_string_buffer("\x53\x31\xc0\x0f\xa2\x8b\x44\x24\x08\x89"
"\x18\x89\x50\x04\x89\x48\x08\x5b\xc3")
def cpuid0():
buffer = create_string_buffer(12)
cpuid0__ = CFUNCTYPE(c_char_p)(addressof(CPUID0_INSNS))
def cpuid0():
cpuid0__(buffer)
return buffer.raw
return cpuid0
cpuid0 = cpuid0()
CPUID1_INSNS = create_string_buffer("\x53\x31\xc0\x40\x0f\xa2\x5b\xc3")
cpuid1 = CFUNCTYPE(c_uint)(addressof(CPUID1_INSNS))
else:
## 64 bit Python
# In 64-bit we cannot execute instructions stored in a string because
# the O.S. prevents that to defend against buffer overrun attacks.
# Therefore we have to allocate a block of memory with the execute
# permission and copy our code into it.
NULL = c_void_p(0)
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
VirtualAlloc = windll.kernel32.VirtualAlloc
VirtualAlloc.restype = c_void_p
VirtualAlloc.argtypes = (c_void_p, c_size_t, c_uint, c_uint)
def VirtualAlloc():
_VirtualAlloc = kernel32.VirtualAlloc
_VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
_VirtualAlloc.restype = LPVOID
def VirtualAlloc(addr, size, alloctype=(MEM_COMMIT | MEM_RESERVE),
protect=PAGE_EXECUTE_READWRITE):
return _VirtualAlloc(addr, size, alloctype, protect)
return VirtualAlloc
VirtualAlloc = VirtualAlloc()
from ctypes import memmove
memmove.restype = c_void_p
memmove.argtypes = (c_void_p, c_void_p, c_size_t)
MEM_RELEASE = 0x8000
CPUID0_INSNS = (b"\x55" # push %rbp
"\x48\x89\xe5" # mov %rsp,%rbp
"\x48\x89\x4d\xf8" # mov %rcx,-0x8(%rbp)
"\x31\xc0" # xor %eax,%eax
"\x0f\xa2" # cpuid
"\x48\x8b\x45\xf8" # mov -0x8(%rbp),%rax
"\x89\x18" # mov %ebx,(%rax)
"\x89\x50\x04" # mov %edx,0x4(%rax)
"\x89\x48\x08" # mov %ecx,0x8(%rax)
"\x48\x8b\x45\xf8" # mov -0x8(%rbp),%rax
"\xc9" # leave
"\xc3" # ret
)
def VirtualFree():
_VirtualFree = kernel32.VirtualFree
_VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
_VirtualFree.restype = BOOL
def VirtualFree(addr, size=0, freetype=MEM_RELEASE):
return _VirtualFree(addr, size, freetype)
return VirtualFree
VirtualFree = VirtualFree()
CPUID1_INSNS = (b"\x31\xc0" # xor %eax,%eax
"\xff\xc0" # inc %eax
"\x0f\xa2" # cpuid
"\xc3" # ret
)
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)
insnlen0 = len(CPUID0_INSNS)
insnlen1 = len(CPUID1_INSNS)
insnlen = insnlen0 + insnlen1
def __call__(self, *args):
return self._native(*args)
code_addr = (VirtualAlloc(NULL, insnlen,
MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE))
def __del__(self):
if self._buf is not None:
VirtualFree(self._buf)
self._buf = None
if code_addr is None:
raise ADEPTError("Failed to allocate memory")
memmove(code_addr, CPUID0_INSNS + CPUID1_INSNS, insnlen)
if struct.calcsize("P") == 4:
CPUID0_INSNS = (
"\x53" # push %ebx
"\x31\xc0" # xor %eax,%eax
"\x0f\xa2" # cpuid
"\x8b\x44\x24\x08" # mov 0x8(%esp),%eax
"\x89\x18" # mov %ebx,0x0(%eax)
"\x89\x50\x04" # mov %edx,0x4(%eax)
"\x89\x48\x08" # mov %ecx,0x8(%eax)
"\x5b" # pop %ebx
"\xc3" # ret
)
CPUID1_INSNS = (
"\x53" # push %ebx
"\x31\xc0" # xor %eax,%eax
"\x40" # inc %eax
"\x0f\xa2" # cpuid
"\x5b" # pop %ebx
"\xc3" # ret
)
else:
CPUID0_INSNS = (
"\x49\x89\xd8" # mov %rbx,%r8
"\x49\x89\xc9" # mov %rcx,%r9
"\x48\x31\xc0" # xor %rax,%rax
"\x0f\xa2" # cpuid
"\x4c\x89\xc8" # mov %r9,%rax
"\x89\x18" # mov %ebx,0x0(%rax)
"\x89\x50\x04" # mov %edx,0x4(%rax)
"\x89\x48\x08" # mov %ecx,0x8(%rax)
"\x4c\x89\xc3" # mov %r8,%rbx
"\xc3" # retq
)
CPUID1_INSNS = (
"\x53" # push %rbx
"\x48\x31\xc0" # xor %rax,%rax
"\x48\xff\xc0" # inc %rax
"\x0f\xa2" # cpuid
"\x5b" # pop %rbx
"\xc3" # retq
)
def cpuid0():
buffer = create_string_buffer(12)
cpuid0__ = CFUNCTYPE(c_ulonglong, c_char_p)(code_addr)
_cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
buf = create_string_buffer(12)
def cpuid0():
cpuid0__(buffer)
return buffer.raw
_cpuid0(buf)
return buf.raw
return cpuid0
cpuid0 = cpuid0()
cpuid1 = CFUNCTYPE(c_ulonglong)(code_addr + insnlen0)
cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
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,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
raise ADEPTError("Failed to decrypt user key key (sic)")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
raise ADEPTError("Failed to decrypt user key key (sic)")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def retrieve_key(keypath):
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
signature = pack('>I', cpuid1())[1:]
user = GetUserName()
entropy = pack('>I12s3s13s', serial, vendor, signature, user)
cuser = winreg.HKEY_CURRENT_USER
try:
regkey = winreg.OpenKey(cuser, DEVICE_KEY)
except WindowsError:
raise ADEPTError("Adobe Digital Editions not activated")
device = winreg.QueryValueEx(regkey, 'key')[0]
keykey = CryptUnprotectData(device, entropy)
userkey = None
pkcs = None
keys = {}
counter = 0
for i in xrange(0, 16):
skey = PRIVATE_LICENCE_KEY % i
def retrieve_key(keypath):
if AES is None:
tkMessageBox.showerror(
"ADEPT Key",
"This script requires PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
return False
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
signature = struct.pack('>I', cpuid1())[1:]
user = GetUserName()
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
cuser = winreg.HKEY_CURRENT_USER
try:
regkey = winreg.OpenKey(cuser, skey)
regkey = winreg.OpenKey(cuser, DEVICE_KEY_PATH)
except WindowsError:
break
type = winreg.QueryValueEx(regkey, None)[0]
# obfuscation technique
if type != 'credentials':
continue
for j in xrange(0, 16):
plkkey = PRIVATE_LICENCE_KEY_KEY % (i, j)
raise ADEPTError("Adobe Digital Editions not activated")
device = winreg.QueryValueEx(regkey, 'key')[0]
keykey = CryptUnprotectData(device, entropy)
userkey = None
try:
plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
except WindowsError:
raise ADEPTError("Could not locate ADE activation")
for i in xrange(0, 16):
try:
regkey = winreg.OpenKey(cuser, plkkey)
plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
except WindowsError:
break
type = winreg.QueryValueEx(regkey, None)[0]
if type != 'privateLicenseKey':
ktype = winreg.QueryValueEx(plkparent, None)[0]
if ktype != 'credentials':
continue
userkey = winreg.QueryValueEx(regkey, 'value')[0]
break
for j in xrange(0, 16):
plkkey = PRIVATE_LICENCE_KEY_KEY % (i, j)
try:
pkcs = winreg.OpenKey(cuser, plkkey)
except WindowsError:
for j in xrange(0, 16):
try:
plkkey = winreg.OpenKey(plkparent, "%04d" % (j,))
except WindowsError:
break
ktype = winreg.QueryValueEx(plkkey, None)[0]
if ktype != 'privateLicenseKey':
continue
userkey = winreg.QueryValueEx(plkkey, 'value')[0]
break
type = winreg.QueryValueEx(pkcs, None)[0]
if type != 'pkcs12':
if userkey is not None:
break
if userkey is None:
raise ADEPTError('Could not locate privateLicenseKey')
userkey = userkey.decode('base64')
userkey = AES.new(keykey, AES.MODE_CBC).decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
with open(keypath, 'wb') as f:
f.write(userkey)
return True
elif sys.platform.startswith('darwin'):
import xml.etree.ElementTree as etree
import Carbon.File
import Carbon.Folder
import Carbon.Folders
import MacOS
ACTIVATION_PATH = 'Adobe/Digital Editions/activation.dat'
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
def find_folder(domain, dtype):
try:
fsref = Carbon.Folder.FSFindFolder(domain, dtype, False)
return Carbon.File.pathname(fsref)
except MacOS.Error:
return None
def find_app_support_file(subpath):
dtype = Carbon.Folders.kApplicationSupportFolderType
for domain in Carbon.Folders.kUserDomain, Carbon.Folders.kLocalDomain:
path = find_folder(domain, dtype)
if path is None:
continue
pkcs = winreg.QueryValueEx(pkcs, 'value')[0]
break
if pkcs is None:
raise ADEPTError('Could not locate PKCS specification')
if userkey is None:
raise ADEPTError('Could not locate privateLicenseKey')
userkey = userkey.decode('base64')
userkey = AES.new(keykey, AES.MODE_CBC).decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1])]
with open(keypath, 'wb') as f:
f.write(userkey)
return
path = os.path.join(path, subpath)
if os.path.isfile(path):
return path
return None
def retrieve_key(keypath):
actpath = find_app_support_file(ACTIVATION_PATH)
if actpath is None:
raise ADEPTError("Could not locate ADE activation")
tree = etree.parse(actpath)
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = '//%s/%s' % (adept('credentials'), adept('privateLicenseKey'))
userkey = tree.findtext(expr)
userkey = userkey.decode('base64')
userkey = userkey[26:]
with open(keypath, 'wb') as f:
f.write(userkey)
return True
elif sys.platform.startswith('cygwin'):
def retrieve_key(keypath):
tkMessageBox.showerror(
"ADEPT Key",
"This script requires a Windows-native Python, and cannot be run "
"under Cygwin. Please install a Windows-native Python and/or "
"check your file associations.")
return False
else:
def retrieve_key(keypath):
tkMessageBox.showerror(
"ADEPT Key",
"This script only supports Windows and Mac OS X. For Linux "
"you should be able to run ADE and this script under Wine (with "
"an appropriate version of Windows Python installed).")
return False
class ExceptionDialog(Tkinter.Frame):
def __init__(self, root, text):
@@ -279,33 +351,27 @@ class ExceptionDialog(Tkinter.Frame):
self.text.pack(fill=Tkconstants.BOTH, expand=1)
self.text.insert(Tkconstants.END, text)
def main(argv=sys.argv):
root = Tkinter.Tk()
root.withdraw()
progname = os.path.basename(argv[0])
if AES is None:
tkMessageBox.showerror(
"ADEPT Key",
"This script requires PyCrypto, which must be installed "
"separately. Read the top-of-script comment for details.")
return 1
keypath = 'adeptkey.der'
success = False
try:
retrieve_key(keypath)
success = retrieve_key(keypath)
except ADEPTError, e:
tkMessageBox.showerror("ADEPT Key", "Error: " + str(e))
return 1
except Exception:
root.wm_state('normal')
root.title('ADEPT Key')
text = traceback.format_exc()
ExceptionDialog(root, text).pack(fill=Tkconstants.BOTH, expand=1)
root.mainloop()
if not success:
return 1
tkMessageBox.showinfo(
"ADEPT Key", "Key successfully retrieved to %s" % (keypath))
return 0
if __name__ == '__main__':
sys.exit(main())
sys.exit(main())

View File

@@ -1,123 +0,0 @@
#! /usr/bin/env python
# ineptkeymac.py, version 1
# This program runs on Mac OS X, version 10.6.2 and probably several other
# versions. It uses Python 2.6, but it probably also runs on all versions
# 2.x with x >= 5.
# This program extracts the private RSA key for your ADE account in a
# standard binary form (DER format) in a file of your choosing. Its purpose
# is to make a backup of that key so that your legally bought ADE encoded
# ebooks can be salvaged in case they would no longer be supported by ADE
# software. No other usages are intended.
# It has been tested with the key storage structure of ADE 1.7.1 and 1.7.2
# and Sony Reader Library.
# This software does not contain any encryption code. Its only use of
# external encryption software is the use of openssl for the conversion of
# the private key from pem to der format. It doesn't use encryption or
# decryption, however.
# You can run this program from the command line (python ineptkeymac.py
# filename), or by doubleclicking when it has been associated with
# Pythonlauncher. When no filename is given it will show a dialog to obtain one.
from __future__ import with_statement
__license__ = 'GPL v3'
import sys
import os
import xml.etree.ElementTree as etree
from contextlib import closing
import Tkinter
import Tkconstants
import tkFileDialog
from tkMessageBox import showerror
from subprocess import Popen, PIPE
import textwrap
NS = 'http://ns.adobe.com/adept'
ACTFILE = '~/Library/Application Support/Adobe/Digital Editions/activation.dat'
HEADER = '-----BEGIN PRIVATE KEY-----\n'
FOOTER = '\n-----END PRIVATE KEY-----\n'
Gui = False
def get_key():
'''Returns the private key as a binary string (DER format)'''
try:
filename = os.path.expanduser(ACTFILE)
tree = etree.parse(filename)
xpath = '//{%s}credentials/{%s}privateLicenseKey' % (NS, NS)
b64key = tree.findtext(xpath)
pemkey = HEADER + textwrap.fill(b64key, 64) + FOOTER
cmd = ['openssl', 'rsa', '-outform', 'der']
proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = proc.communicate(pemkey)
if proc.returncode != 0:
error("openssl error: " + stderr)
return None
return stdout
except IOError:
error("Can find keyfile. Maybe you should activate your Adobe ID.")
sys.exit(1)
def store_key(key, keypath):
'''Store the key in the file given as keypath. If no keypath is given a
dialog will ask for one.'''
try:
if keypath is None:
keypath = get_keypath()
if not keypath: # Cancelled
return
with closing(open(keypath, 'wb')) as outf:
outf.write(key)
except IOError, e:
error("Can write keyfile: " + str(e))
def get_keypath():
keypath = tkFileDialog.asksaveasfilename(
parent = None, title = 'Select file to store ADEPT key',
initialdir = os.path.expanduser('~/Desktop'),
initialfile = 'adeptkey.der',
defaultextension = '.der', filetypes = [('DER-encoded files', '.der'),
('All Files', '.*')])
if keypath:
keypath = os.path.normpath(keypath)
return keypath
def error(text):
print text
if Gui: showerror('Error!', text)
def gui_main():
root = Tkinter.Tk()
root.iconify()
global Gui
Gui = True
store_key(get_key(), None)
return 0
def main(argv=sys.argv):
progname = os.path.basename(argv[0])
if len(argv) == 1: # assume GUI if no argument given
return gui_main()
if len(argv) != 2:
print "usage: %s KEYFILE" % (progname,)
return 1
store_key(get_key(), argv[1])
if __name__ == '__main__':
sys.exit(main())