]> xmof Git - DeDRM.git/commitdiff
Fix username decryption with unicode chars in Python2
authorNoDRM <no_drm123@protonmail.com>
Mon, 20 Dec 2021 20:07:44 +0000 (21:07 +0100)
committerNoDRM <no_drm123@protonmail.com>
Mon, 20 Dec 2021 20:07:44 +0000 (21:07 +0100)
DeDRM_plugin/adobekey.py
DeDRM_plugin/adobekey_winreg_unicode.py [new file with mode: 0644]

index f957ed1f84ae56b1df43705aa40e2268b6907b61..9871edafb793919436a236b90968ac034d8cd1ac 100644 (file)
 #   6.0 - Work if TkInter is missing
 #   7.0 - Python 3 for calibre 5
 #   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.
 """
 
 __license__ = 'GPL v3'
-__version__ = '7.1'
+__version__ = '7.2'
 
 import sys, os, struct, getopt
 from base64 import b64decode
@@ -240,14 +241,21 @@ if iswindows:
 
     def GetUserName2():
         try:
-            import winreg
+            from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER
         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: 
             DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
-            regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH)
-            userREG = winreg.QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2]
+            regkey = OpenKey(HKEY_CURRENT_USER, DEVICE_KEY_PATH)
+            userREG = QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2]
             return userREG
         except: 
             return None
@@ -398,11 +406,16 @@ if iswindows:
             plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
         except WindowsError:
             raise ADEPTError("Could not locate ADE activation")
-        for i in range(0, 16):
+
+        i = -1
+        while True:
+            i = i + 1   # start with 0
             try:
                 plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
-            except WindowsError:
+            except:
+                # No more keys
                 break
+                
             ktype = winreg.QueryValueEx(plkparent, None)[0]
             if ktype != 'credentials':
                 continue
@@ -476,6 +489,8 @@ elif isosx:
         return None
 
     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()
         if actpath is None:
             raise ADEPTError("Could not find ADE activation.dat file.")
diff --git a/DeDRM_plugin/adobekey_winreg_unicode.py b/DeDRM_plugin/adobekey_winreg_unicode.py
new file mode 100644 (file)
index 0000000..6c719c4
--- /dev/null
@@ -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']
\ No newline at end of file