#/** #@file RegistryLib.py #@brief Librería o clase Registry #@class Boot #@brief Funciones para gestión del registro de Windows. #@warning License: GNU GPLv3+ #*/ import subprocess import os import re import shutil import tempfile import ogGlobals import SystemLib import FileLib # Función ficticia para lanzar chntpw con timeout de 5 s., evitando cuelgues del programa. chntpw_exe = shutil.which ('drbl-chntpw') or shutil.which ('chntpw') def chntpw (hivefile, input_file): with open (input_file, 'r') as fd: input_contents = fd.read() return subprocess.run ([chntpw_exe, '-e', hivefile], timeout=5, input=input_contents, 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 #@brief Añade una nueva clave al registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_keyname nombre de la clave #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## def ogAddRegistryKey (mntpt, hive, k): hivefile = ogGetHivePath (mntpt, hive) if not hivefile: return k_dirname, k_basename = _split_k (k) with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'nk {k_basename}\n') f.write ('q\ny\n') f.close() chntpw (hivefile, f.name) os.remove (f.name) #/** # ogAddRegistryValue path_mountpoint str_hive str_valuename [str_valuetype] #@brief Añade un nuevo valor al registro de Windows, indicando su tipo de datos. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_valuename nombre del valor #@param str_valuetype tipo de datos del valor (opcional) #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { DEFAULT, SAM, SECURITY, SOFTWARE, SYSTEM, COMPONENTS } #@note valuetype = { STRING, BINARY, DWORD }, por defecto: STRING #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## #ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') ## type STRING by default #ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'STRING') #ogAddRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1', 'BINARY') #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 'STRING' == vtype.upper(): TYPE = 1 elif 'BINARY' == vtype.upper(): TYPE = 3 elif 'DWORD' == vtype.upper(): TYPE = 4 else: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, vtype) return with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'nv {TYPE} {k_basename}\n') f.write ('q\ny\n') f.close() chntpw (hivefile, f.name) os.remove (f.name) #/** # ogDeleteRegistryKey path_mountpoint str_hive str_keyname #@brief Elimina una clave del registro de Windows con todo su contenido. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_keyname nombre de la clave #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #@warning La clave debe estar vacía para poder ser borrada. #*/ ## def ogDeleteRegistryKey (mntpt, hive, k): hivefile = ogGetHivePath (mntpt, hive) if not hivefile: return k_dirname, k_basename = _split_k (k) with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'dk {k_basename}\n') f.write ('q\ny\n') f.close() chntpw (hivefile, f.name) os.remove (f.name) #/** # ogDeleteRegistryValue path_mountpoint str_hive str_valuename #@brief Elimina un valor del registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_valuename nombre del valor #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## #ogDeleteRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Microsoft\NewKey\Value1') def ogDeleteRegistryValue (mntpt, hive, k): hivefile = ogGetHivePath (mntpt, hive) if not hivefile: return k_dirname, k_basename = _split_k (k) with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'dv {k_basename}\n') f.write ('q\ny\n') f.close() chntpw (hivefile, f.name) os.remove(f.name) #/** # ogGetHivePath path_mountpoint [str_hive|str_user] #@brief Función básica que devuelve el camino del fichero con una sección del registro. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@return str_path - camino del fichero de registro #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { DEFAULT, SAM, SECURITY, SOFTWARE, SYSTEM, COMPONENTS, NombreDeUsuario } #@warning El sistema de archivos de Windows debe estar montada previamente. #*/ ## #ogGetHivePath ('/mnt/sda1', 'user1') => /mnt/sda1/Users/user1/NTUSER.DAT #ogGetHivePath ('/mnt/sda1', 'SYSTEM') => //mnt/sda1/Windows/System32/config/SYSTEM #ogGetHivePath ('/mnt/sda1', 'IEUser') => //mnt/sda1/Users/IEUser/NTUSER.DAT def ogGetHivePath(mntpt, hive): # Camino del fichero de registro de usuario o de sistema (de menor a mayor prioridad). 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'{mntpt} {hive}') return None ## simulate 'grep --after-context 1' def _grep_A1 (strings, search_term): results = [] for i in range (len (strings)): if search_term in strings[i]: results.append (strings[i]) if i + 1 < len(strings): results.append (strings[i + 1]) return results #/** # ogGetRegistryValue path_mountpoint str_hive str_valuename #@brief Devuelve el dato de un valor del registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_valuename nombre del valor #@return str_valuedata - datos del valor. #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## def ogGetRegistryValue (mntpt, hive, k): hivefile = ogGetHivePath(mntpt, hive) if not hivefile: return k_dirname, k_basename = _split_k (k) with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'cat {k_basename}\n') f.write ('q\n') f.close() chntpw_out = chntpw (hivefile, f.name) os.remove (f.name) lines = chntpw_out.splitlines() lines = _grep_A1 (lines, '> Value') if 2 != len (lines): return None ret = None if 'REG_BINARY' in lines[0]: if re.search ('^:[0-9A-F]+ ', lines[1]): ret = lines[1][8:56] else: ret = lines[1] return ret #/** # ogListRegistryKeys path_mountpoint str_hive str_key #@brief Lista los nombres de subclaves de una determinada clave del registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_key clave de registro #@return str_subkey ... - lista de subclaves #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## #ogListRegistryKeys ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') def ogListRegistryKeys (mntpt, hive, k): hivefile = ogGetHivePath(mntpt, hive) if not hivefile: return with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'ls {k}\n') f.write ('q\n') f.close() chntpw_out = chntpw (hivefile, f.name) os.remove (f.name) 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 #/** # ogListRegistryValues path_mountpoint str_hive str_key #@brief Lista los nombres de valores de una determinada clave del registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_key clave de registro #@return str_value ... - lista de valores #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw, awk #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## #ogListRegistryValues ('/mnt/sda1', 'SOFTWARE', '\Microsoft\Windows\CurrentVersion') def ogListRegistryValues (mntpt, hive, k): hivefile = ogGetHivePath(mntpt, hive) if not hivefile: return with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'ls {k}\n') f.write ('q\n') f.close() chntpw_out = chntpw (hivefile, f.name) os.remove (f.name) 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 def _format_hex (hex_string): result = [] offset = 0 hex_values = hex_string.strip().split() result.append (str (len (hex_values))) while hex_values: chunk = hex_values[:16] hex_values = hex_values[16:] offset_str = f':{offset:05x} ' hex_line = ' '.join (chunk) result.append (offset_str + hex_line) offset += 16 return '\n'.join (result) #/** # ogSetRegistryValue path_mountpoint str_hive str_valuename str_valuedata #@brief Establece el dato asociado a un valor del registro de Windows. #@param path_mountpoint directorio donde está montado el sistema Windows #@param str_hive sección del registro #@param str_valuename nombre del valor de registro #@param str_valuedata dato del valor de registro #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND Fichero de registro no encontrado. #@note hive = { default, sam, security, software, system, components } #@warning Requisitos: chntpw #@warning El sistema de archivos de Windows debe estar montado previamente. #*/ ## #ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\StringValue', 'Abcde Fghij') #ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\DwordValue', 1) #ogSetRegistryValue ('/mnt/sda1', 'SOFTWARE', '\Key\SubKey\BinaryValue', '04 08 0C 10') def ogSetRegistryValue (mntpt, hive, k, v): hivefile = ogGetHivePath (mntpt, hive) if not hivefile: return k_dirname, k_basename = _split_k (k) with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f"ls {k_dirname}\n") f.write ('q\n') f.close() chntpw_out = chntpw (hivefile, f.name) os.remove(f.name) 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 if not re.match (r'^([0-9A-F]{2} )*$', v.upper()): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'"{v}"') return formatted = _format_hex (v.upper()) formatted += '\ns' else: formatted = v with tempfile.NamedTemporaryFile (delete_on_close=False, prefix='chntpw-', mode='w') as f: f.write (f'cd {k_dirname}\n') f.write (f'ed {k_basename}\n') f.write (f'{formatted}\n') f.write ('q\ny\n') f.close() chntpw (hivefile, f.name) os.remove(f.name)