Fix username decryption with unicode chars in Python2

This commit is contained in:
NoDRM 2021-12-20 21:07:44 +01:00
parent 78ac98fc1b
commit cdd6402b9a
2 changed files with 293 additions and 7 deletions

View File

@ -30,13 +30,14 @@
# 6.0 - Work if TkInter is missing # 6.0 - Work if TkInter is missing
# 7.0 - Python 3 for calibre 5 # 7.0 - Python 3 for calibre 5
# 7.1 - Fix "failed to decrypt user key key" error (read username from registry) # 7.1 - Fix "failed to decrypt user key key" error (read username from registry)
# 7.2 - Fix decryption error on Python2 if there's unicode in the username
""" """
Retrieve Adobe ADEPT user key. Retrieve Adobe ADEPT user key.
""" """
__license__ = 'GPL v3' __license__ = 'GPL v3'
__version__ = '7.1' __version__ = '7.2'
import sys, os, struct, getopt import sys, os, struct, getopt
from base64 import b64decode from base64 import b64decode
@ -240,14 +241,21 @@ if iswindows:
def GetUserName2(): def GetUserName2():
try: try:
import winreg from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER
except ImportError: except ImportError:
import _winreg as winreg # We're on Python 2
try:
# The default _winreg on Python2 isn't unicode-safe.
# Check if we have winreg_unicode, a unicode-safe alternative.
# Without winreg_unicode, this will fail with Unicode chars in the username.
from adobekey_winreg_unicode import OpenKey, QueryValueEx, HKEY_CURRENT_USER
except:
from _winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER
try: try:
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device' DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH) regkey = OpenKey(HKEY_CURRENT_USER, DEVICE_KEY_PATH)
userREG = winreg.QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2] userREG = QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2]
return userREG return userREG
except: except:
return None return None
@ -398,11 +406,16 @@ if iswindows:
plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH) plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
except WindowsError: except WindowsError:
raise ADEPTError("Could not locate ADE activation") raise ADEPTError("Could not locate ADE activation")
for i in range(0, 16):
i = -1
while True:
i = i + 1 # start with 0
try: try:
plkparent = winreg.OpenKey(plkroot, "%04d" % (i,)) plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
except WindowsError: except:
# No more keys
break break
ktype = winreg.QueryValueEx(plkparent, None)[0] ktype = winreg.QueryValueEx(plkparent, None)[0]
if ktype != 'credentials': if ktype != 'credentials':
continue continue
@ -476,6 +489,8 @@ elif isosx:
return None return None
def adeptkeys(): def adeptkeys():
# TODO: All the code to support extracting multiple activation keys
# TODO: seems to be Windows-only currently, still needs to be added for Mac.
actpath = findActivationDat() actpath = findActivationDat()
if actpath is None: if actpath is None:
raise ADEPTError("Could not find ADE activation.dat file.") raise ADEPTError("Could not find ADE activation.dat file.")

View File

@ -0,0 +1,271 @@
# This is based on https://github.com/DanielStutzbach/winreg_unicode
# The original _winreg in Python2 doesn't support unicode.
# This causes issues if there's unicode chars in the username needed to decrypt the key.
'''
Copyright 2010 Stutzbach Enterprises, LLC (daniel@stutzbachenterprises.com)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
'''
import ctypes, ctypes.wintypes
ERROR_SUCCESS = 0
ERROR_MORE_DATA = 234
KEY_READ = 0x20019
REG_NONE = 0
REG_SZ = 1
REG_EXPAND_SZ = 2
REG_BINARY = 3
REG_DWORD = 4
REG_DWORD_BIG_ENDIAN = 5
REG_DWORD_LITTLE_ENDIAN = 4
REG_LINK = 6
REG_MULTI_SZ = 7
REG_RESOURCE_LIST = 8
REG_FULL_RESOURCE_DESCRIPTOR = 9
REG_RESOURCE_REQUIREMENTS_LIST = 10
c_HKEY = ctypes.c_void_p
DWORD = ctypes.wintypes.DWORD
BYTE = ctypes.wintypes.BYTE
LPDWORD = ctypes.POINTER(DWORD)
LPBYTE = ctypes.POINTER(BYTE)
advapi32 = ctypes.windll.advapi32
class FILETIME(ctypes.Structure):
_fields_ = [("dwLowDateTime", DWORD),
("dwHighDateTime", DWORD)]
RegCloseKey = advapi32.RegCloseKey
RegCloseKey.restype = ctypes.c_long
RegCloseKey.argtypes = [c_HKEY]
RegOpenKeyEx = advapi32.RegOpenKeyExW
RegOpenKeyEx.restype = ctypes.c_long
RegOpenKeyEx.argtypes = [c_HKEY, ctypes.c_wchar_p, ctypes.c_ulong,
ctypes.c_ulong, ctypes.POINTER(c_HKEY)]
RegQueryInfoKey = advapi32.RegQueryInfoKeyW
RegQueryInfoKey.restype = ctypes.c_long
RegQueryInfoKey.argtypes = [c_HKEY, ctypes.c_wchar_p, LPDWORD, LPDWORD,
LPDWORD, LPDWORD, LPDWORD, LPDWORD,
LPDWORD, LPDWORD, LPDWORD,
ctypes.POINTER(FILETIME)]
RegEnumValue = advapi32.RegEnumValueW
RegEnumValue.restype = ctypes.c_long
RegEnumValue.argtypes = [c_HKEY, DWORD, ctypes.c_wchar_p, LPDWORD,
LPDWORD, LPDWORD, LPBYTE, LPDWORD]
RegEnumKeyEx = advapi32.RegEnumKeyExW
RegEnumKeyEx.restype = ctypes.c_long
RegEnumKeyEx.argtypes = [c_HKEY, DWORD, ctypes.c_wchar_p, LPDWORD,
LPDWORD, ctypes.c_wchar_p, LPDWORD,
ctypes.POINTER(FILETIME)]
RegQueryValueEx = advapi32.RegQueryValueExW
RegQueryValueEx.restype = ctypes.c_long
RegQueryValueEx.argtypes = [c_HKEY, ctypes.c_wchar_p, LPDWORD, LPDWORD,
LPBYTE, LPDWORD]
def check_code(code):
if code == ERROR_SUCCESS:
return
raise ctypes.WinError(2)
class HKEY(object):
def __init__(self):
self.hkey = c_HKEY()
def __enter__(self):
return self
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
self.Close()
return False
def Detach(self):
rv = self.cast(self.hkey, self.c_ulong).value
self.hkey = c_HKEY()
return rv
def __nonzero__(self):
return bool(self.hkey)
def Close(self):
if not self.hkey:
return
if RegCloseKey is None or check_code is None or c_HKEY is None:
return # globals become None during exit
rc = RegCloseKey(self.hkey)
self.hkey = c_HKEY()
check_code(rc)
def __del__(self):
self.Close()
class RootHKEY(ctypes.Structure):
def __init__(self, value):
self.hkey = c_HKEY(value)
def Close(self):
pass
HKEY_CLASSES_ROOT = RootHKEY(0x80000000)
HKEY_CURRENT_USER = RootHKEY(0x80000001)
HKEY_LOCAL_MACHINE = RootHKEY(0x80000002)
HKEY_USERS = RootHKEY(0x80000003)
HKEY_PERFORMANCE_DATA = RootHKEY(0x80000004)
HKEY_CURRENT_CONFIG = RootHKEY(0x80000005)
HKEY_DYN_DATA = RootHKEY(0x80000006)
def OpenKey(key, sub_key):
new_key = HKEY()
rc = RegOpenKeyEx(key.hkey, sub_key, 0, KEY_READ,
ctypes.cast(ctypes.byref(new_key.hkey),
ctypes.POINTER(c_HKEY)))
check_code(rc)
return new_key
def QueryInfoKey(key):
null = LPDWORD()
num_sub_keys = DWORD()
num_values = DWORD()
ft = FILETIME()
rc = RegQueryInfoKey(key.hkey, ctypes.c_wchar_p(), null, null,
ctypes.byref(num_sub_keys), null, null,
ctypes.byref(num_values), null, null, null,
ctypes.byref(ft))
check_code(rc)
return (num_sub_keys.value, num_values.value,
ft.dwLowDateTime | (ft.dwHighDateTime << 32))
def EnumValue(key, index):
null = LPDWORD()
value_size = DWORD()
data_size = DWORD()
rc = RegQueryInfoKey(key.hkey, ctypes.c_wchar_p(), null, null, null,
null, null, null,
ctypes.byref(value_size), ctypes.byref(data_size),
null, ctypes.POINTER(FILETIME)())
check_code(rc)
value_size.value += 1
data_size.value += 1
value = ctypes.create_unicode_buffer(value_size.value)
while True:
data = ctypes.create_string_buffer(data_size.value)
tmp_value_size = DWORD(value_size.value)
tmp_data_size = DWORD(data_size.value)
typ = DWORD()
rc = RegEnumValue(key.hkey, index,
ctypes.cast(value, ctypes.c_wchar_p),
ctypes.byref(tmp_value_size), null,
ctypes.byref(typ),
ctypes.cast(data, LPBYTE),
ctypes.byref(tmp_data_size))
if rc != ERROR_MORE_DATA:
break
data_size.value *= 2
check_code(rc)
return (value.value, Reg2Py(data, tmp_data_size.value, typ.value),
typ.value)
def split_multi_sz(data, size):
if size == 0:
return []
Q = size
P = 0
rv = []
while P < Q and data[P].value != u'\0':
rv.append[P]
while P < Q and data[P].value != u'\0':
P += 1
P += 1
rv.append(size)
return [ctypes.wstring_at(ctypes.pointer(data[rv[i]]),
rv[i+1] - rv[i]).rstrip(u'\x00')
for i in range(len(rv)-1)]
def Reg2Py(data, size, typ):
if typ == REG_DWORD:
if size == 0:
return 0
return ctypes.cast(data, ctypes.POINTER(ctypes.c_int)).contents.value
elif typ == REG_SZ or typ == REG_EXPAND_SZ:
return ctypes.wstring_at(data, size // 2).rstrip(u'\x00')
elif typ == REG_MULTI_SZ:
return split_multi_sz(ctypes.cast(data, ctypes.c_wchar_p), size // 2)
else:
if size == 0:
return None
return ctypes.string_at(data, size)
def EnumKey(key, index):
tmpbuf = ctypes.create_unicode_buffer(257)
length = DWORD(257)
rc = RegEnumKeyEx(key.hkey, index,
ctypes.cast(tmpbuf, ctypes.c_wchar_p),
ctypes.byref(length),
LPDWORD(), ctypes.c_wchar_p(), LPDWORD(),
ctypes.POINTER(FILETIME)())
check_code(rc)
return ctypes.wstring_at(tmpbuf, length.value).rstrip(u'\x00')
def QueryValueEx(key, value_name):
size = 256
typ = DWORD()
while True:
tmp_size = DWORD(size)
buf = ctypes.create_string_buffer(size)
rc = RegQueryValueEx(key.hkey, value_name, LPDWORD(),
ctypes.byref(typ),
ctypes.cast(buf, LPBYTE), ctypes.byref(tmp_size))
if rc != ERROR_MORE_DATA:
break
size *= 2
check_code(rc)
return (Reg2Py(buf, tmp_size.value, typ.value), typ.value)
__all__ = ['OpenKey', 'QueryInfoKey', 'EnumValue', 'EnumKey', 'QueryValueEx',
'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE',
'HKEY_USERS', 'HKEY_PERFORMANCE_DATA', 'HKEY_CURRENT_CONFIG',
'HKEY_DYN_DATA', 'REG_NONE', 'REG_SZ', 'REG_EXPAND_SZ',
'REG_BINARY', 'REG_DWORD', 'REG_DWORD_BIG_ENDIAN',
'REG_DWORD_LITTLE_ENDIAN', 'REG_LINK', 'REG_MULTI_SZ',
'REG_RESOURCE_LIST', 'REG_FULL_RESOURCE_DESCRIPTOR',
'REG_RESOURCE_REQUIREMENTS_LIST']