Fix username decryption with unicode chars in Python2
This commit is contained in:
parent
78ac98fc1b
commit
cdd6402b9a
|
@ -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.")
|
||||||
|
|
|
@ -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']
|
Loading…
Reference in New Issue