From 7dccc621cbd9de6fe6d6e302107b3d2242bcbddc Mon Sep 17 00:00:00 2001 From: Natalia Serrano Date: Thu, 5 Dec 2024 14:55:28 +0100 Subject: [PATCH] refs #1226 finish and refactor RegistryLib --- client/lib/engine/bin/RegistryLib.py | 229 +++++++++++++++------------ 1 file changed, 125 insertions(+), 104 deletions(-) diff --git a/client/lib/engine/bin/RegistryLib.py b/client/lib/engine/bin/RegistryLib.py index f35f61d..c924bbd 100755 --- a/client/lib/engine/bin/RegistryLib.py +++ b/client/lib/engine/bin/RegistryLib.py @@ -17,11 +17,18 @@ import SystemLib import FileLib # Función ficticia para lanzar chntpw con timeout de 5 s., evitando cuelgues del programa. -def chntpw (file, input): - exe = shutil.which ('drbl-chntpw') or shutil.which ('chntpw') - - return subprocess.run ([exe, '-e', file], timeout=5, input=input, capture_output=True, text=True).stdout +chntpw_exe = shutil.which ('drbl-chntpw') or shutil.which ('chntpw') +def chntpw (hivefile, input_file): + return subprocess.run ([chntpw_exe, '-e', hivefile], timeout=5, input=open(input_file, 'r'), capture_output=True, text=True).stdout +## en el codigo bash aparecen "${3%\\*}" y "${3##*\\}" varias veces +## ${3%\\*} es el "dirname" de una key del registro +## ${3##*\\} es el "basename" +def _split_k (k): + k_elems = k.split ('\\') + k_dirname = '\\'.join (k_elems[0:-1]) + k_basename = k_elems[-1] + return k_dirname, k_basename #/** # ogAddRegistryKey path_mountpoint str_hive str_keyname @@ -36,18 +43,19 @@ def chntpw (file, input): #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## -def ogAddRegistryKey(path_mountpoint, str_hive, str_key): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +def ogAddRegistryKey (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return - # Añadir nueva clave. - chntpw(FILE, 'cd', str_key.rsplit('\\', 1)[0]) - chntpw(FILE, 'nk', str_key.rsplit('\\', 1)[-1]) - chntpw(FILE, 'q') - chntpw(FILE, 'y') + k_dirname, k_basename = _split_k (k) + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open (tmpfile, 'w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'nk {k_basename}\n') + f.write ('q\ny\n') + chntpw (hivefile, tmpfile) + os.remove (tmpfile) #/** # ogAddRegistryValue path_mountpoint str_hive str_valuename [str_valuetype] @@ -64,32 +72,29 @@ def ogAddRegistryKey(path_mountpoint, str_hive, str_key): #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## -def ogAddRegistryValue(path_mountpoint, str_hive, str_key, str_valuename, str_valuetype=''): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') +#ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'DWORD') +def ogAddRegistryValue (mntpt, hive, k, vtype='STRING'): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return + + k_dirname, k_basename = _split_k (k) # Determine the value type. - if str_valuetype.upper() == 'STRING' or str_valuetype == '': - TYPE = 1 - elif str_valuetype.upper() == 'BINARY': - TYPE = 3 - elif str_valuetype.upper() == 'DWORD': - TYPE = 4 + if 'STRING' == vtype.upper(): TYPE = 1 + elif 'BINARY' == vtype.upper(): TYPE = 3 + elif 'DWORD' == vtype.upper(): TYPE = 4 else: - SystemLib.ogRaiseError( - "session", - ogGlobals.OG_ERR_OUTOFLIMIT, - f"Error: {str_valuetype}", - ) + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, vtype) return - # Add the registry value. - chntpw(FILE, 'cd', str_key.rsplit('\\', 1)[0]) - chntpw(FILE, 'nv', str_valuename.rsplit('\\', 1)[-1], TYPE) - chntpw(FILE, 'q') - chntpw(FILE, 'y') + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open (tmpfile, 'w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'nv {TYPE} {k_basename}\n') + f.write ('q\ny\n') + chntpw (hivefile, tmpfile) + os.remove (tmpfile) #/** @@ -106,14 +111,19 @@ def ogAddRegistryValue(path_mountpoint, str_hive, str_key, str_valuename, str_va #@warning El sistema de archivos de Windows debe estar montada previamente. #@warning La clave debe estar vacía para poder ser borrada. #*/ ## -def ogDeleteRegistryKey(path_mountpoint, str_hive, str_key): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +def ogDeleteRegistryKey (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return - # Delete the registry key. - subprocess.run(['chntpw', FILE], input=f"cd {str_key.rsplit('\\', 1)[0]}\ndk {str_key.rsplit('\\', 1)[-1]}\nq\ny\n".encode(), timeout=5) + k_dirname, k_basename = _split_k (k) + + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open (tmpfile, 'w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'dk {k_basename}\n') + f.write ('q\ny\n') + chntpw (hivefile, tmpfile) + os.remove (tmpfile) #/** @@ -129,17 +139,20 @@ def ogDeleteRegistryKey(path_mountpoint, str_hive, str_key): #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## -def ogDeleteRegistryValue(path_mountpoint, str_hive, str_valuename): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +#ogDeleteRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') +def ogDeleteRegistryValue (mntpt, hive, k): + hivefile = ogGetHivePath (mntpt, hive) + if not hivefile: return - # Delete the registry value. - chntpw(FILE, 'cd', str_valuename.rsplit('\\', 1)[0]) - chntpw(FILE, 'dv', str_valuename.rsplit('\\', 1)[-1]) - chntpw(FILE, 'q') - chntpw(FILE, 'y') + k_dirname, k_basename = _split_k (k) + + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open(tmpfile, 'w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'dv {k_basename}\n') + f.write ('q\ny\n') + chntpw (hivefile, tmpfile) + os.remove(tmpfile) #/** @@ -153,21 +166,20 @@ def ogDeleteRegistryValue(path_mountpoint, str_hive, str_valuename): #@note hive = { DEFAULT, SAM, SECURITY, SOFTWARE, SYSTEM, COMPONENTS, NombreDeUsuario } #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## -#ogGetHivePath ('/mnt/sda1', 'SOFTWARE') => /mnt/sda1/WINDOWS/System32/config/SOFTWARE #ogGetHivePath ('/mnt/sda1', 'user1') => /mnt/sda1/Users/user1/NTUSER.DAT -def ogGetHivePath(path_mountpoint, str_hive): +def ogGetHivePath(mntpt, hive): # Camino del fichero de registro de usuario o de sistema (de menor a mayor prioridad). - FILE = FileLib.ogGetPath(file=f"/{path_mountpoint}/Windows/System32/config/{str_hive}") - if not FILE: FILE = FileLib.ogGetPath(file=f"/{path_mountpoint}/Users/{str_hive}/NTUSER.DAT") - if not FILE: FILE = FileLib.ogGetPath(file=f"/{path_mountpoint}/winnt/system32/config/{str_hive}") - if not FILE: FILE = FileLib.ogGetPath(file=f"/{path_mountpoint}/Documents and Settings/{str_hive}/NTUSER.DAT") + FILE = FileLib.ogGetPath(file=f"/{mntpt}/Windows/System32/config/{hive}") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/Users/{hive}/NTUSER.DAT") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/winnt/system32/config/{hive}") + if not FILE: FILE = FileLib.ogGetPath(file=f"/{mntpt}/Documents and Settings/{hive}/NTUSER.DAT") if FILE and os.path.isfile(FILE): return FILE else: SystemLib.ogRaiseError( [], ogGlobals.OG_ERR_NOTFOUND, - f"{path_mountpoint} {str_hive}" + f"{mntpt} {hive}" ) return None @@ -185,20 +197,20 @@ def ogGetHivePath(path_mountpoint, str_hive): #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## -def ogGetRegistryValue(path_mountpoint, hive, k): - FILE = ogGetHivePath(path_mountpoint, hive) +def ogGetRegistryValue (mntpt, hive, k): + FILE = ogGetHivePath(mntpt, hive) if not FILE: return - k_elems = k.split ('\\') - k_dirname = '\\'.join (k_elems[0:-1]) - k_basename = k_elems[-1] + k_dirname, k_basename = _split_k (k) - input = '\n'.join ([ - f'cd {k_dirname}', - f'cat {k_basename}', - 'q', - ]) - chntpw_out = chntpw (file, input) + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open(tmpfile, 'w') as f: + f.write (f'cd {k_dirname}\n') + f.write (f'cat {k_basename}\n') + f.write ('q\n') + + chntpw_out = chntpw (hivefile, tmpfile) + os.remove (tmpfile) lines = chntpw_out.splitlines() if 2 != len (lines): return None @@ -220,20 +232,26 @@ def ogGetRegistryValue(path_mountpoint, hive, k): #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## -def ogListRegistryKeys(path_mountpoint, str_hive, str_key): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +#ogListRegistryKeys ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') +def ogListRegistryKeys (mntpt, hive, k): + FILE = ogGetHivePath(mntpt, hive) + if not FILE: return - # Devolver la lista de claves de registro. - chntpw_cmd = f''' - chntpw "{FILE}" << EOT 2> /dev/null | awk 'BEGIN {{FS="[<>]"}} $1~/^ $/ {{print $2}}' - ls {str_key} - q - EOT - ''' - subprocess.run(chntpw_cmd, shell=True, timeout=5) + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open(tmpfile, 'w') as f: + f.write (f'ls {k}\n') + f.write ('q\n') + chntpw_out = chntpw (hivefile, tmpfile) + os.remove (tmpfile) + lines = chntpw_out.splitlines() + + ret = [] + for l in lines: + elems = re.split ('[<>]', l) + if len(elems) < 2: continue + if ' ' == elems[0]: + ret.append (elems[1]) + return ret #/** @@ -249,20 +267,27 @@ def ogListRegistryKeys(path_mountpoint, str_hive, str_key): #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## -def ogListRegistryValues(path_mountpoint, str_hive, str_key): - # Variables locales. - FILE = ogGetHivePath(path_mountpoint, str_hive) - if not FILE: - return +#ogListRegistryValues ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') +def ogListRegistryValues (mntpt, hive, k): + FILE = ogGetHivePath(mntpt, hive) + if not FILE: return + + tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') + with open(tmpfile, 'w') as f: + f.write (f'ls {k}\n') + f.write ('q\n') + chntpw_out = chntpw (hivefile, tmpfile) + os.remove (tmpfile) + lines = chntpw_out.splitlines() + + ret = [] + for l in lines: + elems = re.split ('[<>]', l) + if len(elems) < 2: continue + if 'REG_' in elems[0]: + ret.append (elems[1]) + return ret - # Devolver la lista de valores de registro. - chntpw_cmd = f''' - chntpw "{FILE}" << EOT 2> /dev/null | awk 'BEGIN {{FS="[<>]"}} $1~/REG_/ {{print $2}}' - ls {str_key} - q - EOT - ''' - subprocess.run(chntpw_cmd, shell=True, timeout=5) def _format_hex (hex_string): result = [] @@ -304,11 +329,7 @@ def ogSetRegistryValue (mntpt, hive, k, v): hivefile = ogGetHivePath (mntpt, hive) if not hivefile: return -# ${3%\\*} es dirname -# ${3##*\\} es basename - k_elems = k.split ('\\') - k_dirname = '\\'.join (k_elems[0:-1]) - k_basename = k_elems[-1] + k_dirname, k_basename = _split_k (k) tmpfile = tempfile.TemporaryFile (prefix='chntpw-', mode='w') try: @@ -316,8 +337,8 @@ def ogSetRegistryValue (mntpt, hive, k, v): f.write (f"ls {k_dirname}\n") f.write ('q\n') - output = subprocess.run (['chntpw', hivefile], input=open(tmpfile, 'r'), capture_output=True, text=True).stdout - if re.search (f"BINARY.*<{k_basename}>", output): + chntpw_out = chntpw (hivefile, tmpfile) + if re.search (f"BINARY.*<{k_basename}>", chntpw_out): ## the entry in the registry is binary. Our input should be a sequence of bytes if ' ' != v[-1]: v += ' ' ## the regex below requires a trailing space @@ -337,6 +358,6 @@ def ogSetRegistryValue (mntpt, hive, k, v): f.write ('q\ny\n') # Aplicar cambios. - subprocess.run (['chntpw', hivefile], input=open(tmpfile, 'r')) + chntpw (hivefile, tmpfile) finally: os.remove(tmpfile)