2021-11-20 14:14:59 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Most of the code in this file has been taken from adobekey.pyw written by i♥cabbages
|
|
|
|
# adobekey.pyw, version 7.0
|
|
|
|
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
|
|
|
|
# Released under the terms of the GNU General Public Licence, version 3
|
|
|
|
# <http://www.gnu.org/licenses/>
|
|
|
|
|
|
|
|
try:
|
|
|
|
from ctypes import windll
|
|
|
|
except ImportError:
|
|
|
|
import os
|
|
|
|
if os.name != 'nt':
|
|
|
|
print("This script is for Windows!")
|
|
|
|
exit()
|
|
|
|
else:
|
|
|
|
raise
|
|
|
|
|
2022-01-16 17:43:29 +01:00
|
|
|
#@@CALIBRE_COMPAT_CODE@@
|
|
|
|
|
2021-11-20 14:14:59 +01:00
|
|
|
def GetSystemDirectory():
|
|
|
|
from ctypes import windll, c_wchar_p, c_uint, create_unicode_buffer
|
|
|
|
MAX_PATH = 255
|
|
|
|
|
|
|
|
kernel32 = windll.kernel32
|
|
|
|
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
|
|
|
|
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
|
|
|
|
GetSystemDirectoryW.restype = c_uint
|
2021-11-25 09:15:37 +01:00
|
|
|
|
|
|
|
buffer = create_unicode_buffer(MAX_PATH + 1)
|
|
|
|
GetSystemDirectoryW(buffer, len(buffer))
|
|
|
|
return buffer.value
|
|
|
|
|
|
|
|
|
|
|
|
def GetVolumeSerialNumber(path):
|
2021-11-20 14:14:59 +01:00
|
|
|
from ctypes import windll, c_wchar_p, c_uint, POINTER, byref
|
|
|
|
|
|
|
|
kernel32 = windll.kernel32
|
|
|
|
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
|
2021-11-25 09:15:37 +01:00
|
|
|
vsn = c_uint(0)
|
|
|
|
GetVolumeInformationW(
|
|
|
|
path, None, 0, byref(vsn), None, None, None, 0)
|
|
|
|
return vsn.value
|
|
|
|
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
def GetUserNameWINAPI():
|
|
|
|
from ctypes import windll, c_wchar_p, c_uint, POINTER, byref, create_unicode_buffer
|
|
|
|
advapi32 = windll.advapi32
|
|
|
|
GetUserNameW = advapi32.GetUserNameW
|
|
|
|
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
|
|
|
|
GetUserNameW.restype = c_uint
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
# Yes, it's actually implemented like that. Encode in UTF16 but only take the lowest byte of each character.
|
|
|
|
return buffer.value.encode('utf-16-le')[::2]
|
|
|
|
|
|
|
|
def GetUserNameREG():
|
|
|
|
try:
|
|
|
|
import winreg
|
|
|
|
except ImportError:
|
|
|
|
import _winreg as winreg
|
|
|
|
|
|
|
|
try:
|
|
|
|
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
|
|
|
|
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH)
|
|
|
|
# Yes, it's actually implemented like that. Encode in UTF16 but only take the lowest byte of each character.
|
|
|
|
userREG = winreg.QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2]
|
|
|
|
return userREG
|
|
|
|
except:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
from ctypes import Structure, c_uint, c_void_p, POINTER
|
|
|
|
class DataBlob(Structure):
|
|
|
|
_fields_ = [('cbData', c_uint),
|
|
|
|
('pbData', c_void_p)]
|
|
|
|
DataBlob_p = POINTER(DataBlob)
|
|
|
|
|
|
|
|
def CryptUnprotectData(indata, entropy):
|
|
|
|
from ctypes import windll, c_wchar_p, c_uint, byref, cast, create_string_buffer, string_at
|
|
|
|
|
|
|
|
crypt32 = windll.crypt32
|
|
|
|
_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
|
|
|
|
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)):
|
|
|
|
return None
|
|
|
|
return string_at(outdata.pbData, outdata.cbData)
|
|
|
|
|
|
|
|
def GetMasterKey():
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
if os.name != 'nt':
|
|
|
|
print("This script is for Windows!")
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
verbose_logging = False
|
|
|
|
try:
|
|
|
|
import calibre_plugins.deacsm.prefs as prefs
|
|
|
|
deacsmprefs = prefs.DeACSM_Prefs()
|
|
|
|
verbose_logging = deacsmprefs["detailed_logging"]
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2021-11-20 14:14:59 +01:00
|
|
|
# Get serial number of root drive
|
|
|
|
root = GetSystemDirectory().split('\\')[0] + '\\'
|
|
|
|
serial = GetVolumeSerialNumber(root)
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
print("Serial: " + str(serial))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
# Get CPU vendor:
|
2021-11-25 09:15:37 +01:00
|
|
|
|
2022-01-16 17:43:29 +01:00
|
|
|
import cpuid
|
2021-11-25 09:15:37 +01:00
|
|
|
import struct
|
2021-11-20 14:14:59 +01:00
|
|
|
cpu = cpuid.CPUID()
|
|
|
|
_, b, c, d = cpu(0)
|
|
|
|
vendor = struct.pack("III", b, d, c)
|
2021-11-25 09:15:37 +01:00
|
|
|
|
|
|
|
if verbose_logging:
|
|
|
|
print("Vendor: " + vendor.decode("utf-8"))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
signature, _, _, _ = cpu(1)
|
|
|
|
signature = struct.pack('>I', signature)[1:]
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
print("Signature: " + str(signature.hex()))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
# Search for the username in the registry:
|
|
|
|
user = None
|
|
|
|
|
|
|
|
user_from_registry = GetUserNameREG()
|
|
|
|
current_user_name = GetUserNameWINAPI()
|
|
|
|
|
|
|
|
if (user_from_registry is not None):
|
|
|
|
# Found entry
|
|
|
|
user = user_from_registry
|
|
|
|
else:
|
|
|
|
user = current_user_name
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
if (user_from_registry is not None and user_from_registry != current_user_name):
|
|
|
|
print("Username: {0}/{1} mismatch, using {0}".format(str(user_from_registry), str(current_user_name)))
|
|
|
|
elif (user_from_registry is not None):
|
|
|
|
print("Username: {0} (Registry)".format(str(user_from_registry)))
|
|
|
|
else:
|
|
|
|
print("Username: {0} (WinAPI)".format(str(current_user_name)))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Find the value we want to decrypt from the registry:
|
|
|
|
try:
|
|
|
|
import winreg
|
|
|
|
except ImportError:
|
|
|
|
import _winreg as winreg
|
|
|
|
|
|
|
|
try:
|
|
|
|
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
|
|
|
|
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH)
|
|
|
|
device = winreg.QueryValueEx(regkey, 'key')[0]
|
|
|
|
except:
|
|
|
|
print("Can't find encrypted device key.")
|
2021-11-25 09:15:37 +01:00
|
|
|
return None
|
2021-11-20 14:14:59 +01:00
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
print("Encrypted key: " + str(device))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
# These three must all be bytes.
|
|
|
|
#print(type(vendor))
|
|
|
|
#print(type(signature))
|
|
|
|
#print(type(user))
|
|
|
|
|
|
|
|
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
print("Entropy: " + str(entropy))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
keykey = CryptUnprotectData(device, entropy)
|
|
|
|
if (keykey is None):
|
|
|
|
print("Couldn't decrypt key!")
|
|
|
|
return None
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
if verbose_logging:
|
|
|
|
print("Decrypted key: " + str(keykey))
|
2021-11-20 14:14:59 +01:00
|
|
|
|
|
|
|
return keykey
|
|
|
|
|
2021-11-25 09:15:37 +01:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
GetMasterKey()
|