"""Windows registry parsing tools.""" from __future__ import annotations import logging import struct from datetime import datetime, timedelta, timezone logger = logging.getLogger(__name__) # Suppress noisy regipy warnings (hive-type identification + binary encoding fallbacks) logging.getLogger("regipy.registry").setLevel(logging.WARNING) logging.getLogger("regipy.utils").setLevel(logging.ERROR) async def parse_registry_key(hive_path: str, key_path: str = "") -> str: """Parse a registry hive and list subkeys/values at the given path. Uses regipy for pure-Python registry parsing. """ try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed. Run: uv add regipy]" try: reg = RegistryHive(hive_path) if key_path: key = reg.get_key(key_path) else: key = reg.root_key() lines = [f"Key: {key.path}", f"Timestamp: {key.header.last_modified}", ""] # Subkeys subkeys = list(key.iter_subkeys()) if subkeys: lines.append(f"Subkeys ({len(subkeys)}):") for sk in subkeys[:50]: lines.append(f" {sk.name}") if len(subkeys) > 50: lines.append(f" ... ({len(subkeys) - 50} more)") lines.append("") # Values values = list(key.iter_values()) if values: lines.append(f"Values ({len(values)}):") for v in values[:30]: val_data = str(v.value) if len(val_data) > 200: val_data = val_data[:200] + "..." lines.append(f" {v.name} ({v.value_type}) = {val_data}") return "\n".join(lines) except Exception as e: return f"[Error parsing registry: {e}]" async def list_installed_software(hive_path: str) -> str: """List installed software from a SOFTWARE registry hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(hive_path) uninstall_path = "\\Microsoft\\Windows\\CurrentVersion\\Uninstall" key = reg.get_key(uninstall_path) programs = [] for sk in key.iter_subkeys(): name = sk.name display_name = None for v in sk.iter_values(): if v.name == "DisplayName": display_name = v.value break programs.append(display_name or name) lines = [f"Installed Software ({len(programs)} entries):", ""] for p in sorted(programs): lines.append(f" - {p}") return "\n".join(lines) except Exception as e: return f"[Error listing software: {e}]" async def get_user_activity(hive_path: str) -> str: """Extract user activity indicators from NTUSER.DAT.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(hive_path) lines = ["=== User Activity from NTUSER.DAT ===", ""] # Recent documents try: key = reg.get_key("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs") lines.append("Recent Documents:") for v in key.iter_values(): if v.name != "MRUListEx": lines.append(f" {v.name}") lines.append("") except Exception: lines.append("Recent Documents: [not found]") # Run MRU (commands typed in Run dialog) try: key = reg.get_key("\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU") lines.append("Run Dialog MRU:") for v in key.iter_values(): if v.name not in ("MRUList",): lines.append(f" {v.name}: {v.value}") lines.append("") except Exception: lines.append("Run MRU: [not found]") # Typed URLs try: key = reg.get_key("\\Software\\Microsoft\\Internet Explorer\\TypedURLs") lines.append("Typed URLs:") for v in key.iter_values(): lines.append(f" {v.value}") lines.append("") except Exception: lines.append("Typed URLs: [not found]") return "\n".join(lines) except Exception as e: return f"[Error analyzing user activity: {e}]" def _filetime_to_datetime(ft: int) -> str: """Convert a Windows FILETIME (100-nanosecond intervals since 1601-01-01) to ISO string.""" if ft <= 0: return "(not set)" try: epoch = datetime(1601, 1, 1, tzinfo=timezone.utc) dt = epoch + timedelta(microseconds=ft // 10) return dt.strftime("%Y-%m-%d %H:%M:%S UTC") except (ValueError, OverflowError): return f"(invalid FILETIME: {ft})" async def get_system_info(software_hive_path: str) -> str: """Extract OS version, install date, registered owner from SOFTWARE hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(software_hive_path) key = reg.get_key("\\Microsoft\\Windows NT\\CurrentVersion") data = {} for v in key.iter_values(): data[v.name] = v.value lines = ["=== System Information (SOFTWARE hive) ==="] lines.append(f"Product Name: {data.get('ProductName', 'N/A')}") lines.append(f"Current Version: {data.get('CurrentVersion', 'N/A')}") lines.append(f"Build Number: {data.get('CurrentBuildNumber', 'N/A')}") lines.append(f"CSD Version (Service Pack): {data.get('CSDVersion', 'None')}") lines.append(f"Registered Owner: {data.get('RegisteredOwner', 'N/A')}") lines.append(f"Registered Organization: {data.get('RegisteredOrganization', 'N/A')}") lines.append(f"Product ID: {data.get('ProductId', 'N/A')}") lines.append(f"System Root: {data.get('SystemRoot', 'N/A')}") install_epoch = data.get("InstallDate") if install_epoch and isinstance(install_epoch, int): install_dt = datetime.fromtimestamp(install_epoch, tz=timezone.utc) lines.append(f"Install Date: {install_dt.strftime('%Y-%m-%d %H:%M:%S UTC')} (epoch: {install_epoch})") else: lines.append(f"Install Date: {install_epoch}") return "\n".join(lines) except Exception as e: return f"[Error: {e}]" async def get_timezone_info(system_hive_path: str) -> str: """Extract timezone settings from SYSTEM hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(system_hive_path) key = reg.get_key("\\ControlSet001\\Control\\TimeZoneInformation") data = {} for v in key.iter_values(): data[v.name] = v.value lines = ["=== Timezone Information (SYSTEM hive) ==="] lines.append(f"Standard Name: {data.get('StandardName', 'N/A')}") lines.append(f"Daylight Name: {data.get('DaylightName', 'N/A')}") bias = data.get("Bias", "N/A") if isinstance(bias, int): hours = bias // 60 lines.append(f"Bias: {bias} minutes (UTC{-hours:+d}:00)") else: lines.append(f"Bias: {bias}") lines.append(f"Active Time Bias: {data.get('ActiveTimeBias', 'N/A')}") return "\n".join(lines) except Exception as e: return f"[Error: {e}]" async def get_computer_name(system_hive_path: str) -> str: """Extract computer name from SYSTEM hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(system_hive_path) lines = ["=== Computer Name (SYSTEM hive) ==="] for path_label, path in [ ("ComputerName", "\\ControlSet001\\Control\\ComputerName\\ComputerName"), ("ActiveComputerName", "\\ControlSet001\\Control\\ComputerName\\ActiveComputerName"), ]: try: key = reg.get_key(path) for v in key.iter_values(): if v.name == "ComputerName": lines.append(f"{path_label}: {v.value}") except Exception: pass # Also try Tcpip hostname try: key = reg.get_key("\\ControlSet001\\Services\\Tcpip\\Parameters") for v in key.iter_values(): if v.name in ("Hostname", "Domain", "NV Hostname"): lines.append(f"TCP/IP {v.name}: {v.value}") except Exception: pass return "\n".join(lines) if len(lines) > 1 else "Computer name not found in SYSTEM hive." except Exception as e: return f"[Error: {e}]" async def get_shutdown_time(system_hive_path: str) -> str: """Extract last shutdown time from SYSTEM hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(system_hive_path) lines = ["=== Shutdown Time (SYSTEM hive) ==="] try: key = reg.get_key("\\ControlSet001\\Control\\Windows") for v in key.iter_values(): if v.name == "ShutdownTime": raw = v.value if isinstance(raw, bytes) and len(raw) >= 8: ft = struct.unpack(" str: """Enumerate all user accounts from SAM hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(sam_hive_path) key = reg.get_key("\\SAM\\Domains\\Account\\Users\\Names") accounts = [] for sk in key.iter_subkeys(): accounts.append(sk.name) lines = [f"=== User Accounts (SAM hive) — {len(accounts)} total ==="] for acct in accounts: lines.append(f" - {acct}") # Try to get RIDs from the Users key try: users_key = reg.get_key("\\SAM\\Domains\\Account\\Users") rid_entries = [] for sk in users_key.iter_subkeys(): if sk.name != "Names" and sk.name.startswith("0"): rid = int(sk.name, 16) rid_entries.append(f" RID {rid} (0x{sk.name})") if rid_entries: lines.append("\nUser RIDs:") lines.extend(rid_entries) except Exception: pass return "\n".join(lines) except Exception as e: return f"[Error: {e}]" async def get_network_interfaces(system_hive_path: str) -> str: """Extract network adapter and TCP/IP configuration from SYSTEM hive.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(system_hive_path) lines = ["=== Network Interfaces (SYSTEM hive) ==="] # Try TCP/IP interfaces try: key = reg.get_key("\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces") for sk in key.iter_subkeys(): lines.append(f"\nInterface: {sk.name}") for v in sk.iter_values(): if v.name in ( "IPAddress", "SubnetMask", "DefaultGateway", "DhcpIPAddress", "DhcpSubnetMask", "DhcpDefaultGateway", "DhcpServer", "NameServer", "Domain", "EnableDHCP", ): lines.append(f" {v.name} = {v.value}") except Exception as e: lines.append(f"TCP/IP Interfaces: {e}") # Try network adapter class adapter_class = "\\ControlSet001\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}" try: key = reg.get_key(adapter_class) lines.append("\nNetwork Adapters:") for sk in key.iter_subkeys(): if sk.name == "Properties": continue desc = None for v in sk.iter_values(): if v.name == "DriverDesc": desc = v.value if desc: lines.append(f" [{sk.name}] {desc}") except Exception as e: lines.append(f"Network Adapters: {e}") # Try NetworkCards try: key = reg.get_key("\\ControlSet001\\Control\\NetworkCards") for sk in key.iter_subkeys(): for v in sk.iter_values(): if v.name == "Description": lines.append(f" NetworkCard {sk.name}: {v.value}") except Exception: pass return "\n".join(lines) if len(lines) > 1 else "No network interface data found in SYSTEM hive." except Exception as e: return f"[Error: {e}]" async def get_email_config(ntuser_hive_path: str) -> str: """Extract email account configuration (SMTP, POP3, NNTP) from NTUSER.DAT.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(ntuser_hive_path) lines = ["=== Email Account Configuration (NTUSER.DAT) ==="] try: key = reg.get_key("\\Software\\Microsoft\\Internet Account Manager\\Accounts") for sk in key.iter_subkeys(): lines.append(f"\n--- Account: {sk.name} ---") for v in sk.iter_values(): # Skip binary password hash fields (but keep "Prompt for Password" flags) if "Password" in v.name and "Prompt" not in v.name: lines.append(f" {v.name} = [present, redacted]") else: lines.append(f" {v.name} = {v.value}") except Exception as e: lines.append(f"Internet Account Manager: {e}") return "\n".join(lines) except Exception as e: return f"[Error: {e}]" async def search_registry(hive_path: str, pattern: str) -> str: """Search for a pattern in registry key names and values.""" try: from regipy.registry import RegistryHive except ImportError: return "[Error: regipy not installed]" try: reg = RegistryHive(hive_path) pattern_lower = pattern.lower() matches = [] for entry in reg.recurse_subkeys(as_json=True): path = entry.path or "" if pattern_lower in path.lower(): matches.append(f"KEY: {path}") if hasattr(entry, "values") and entry.values: for v in entry.values: name = v.get("name", "") value = str(v.get("value", "")) if pattern_lower in name.lower() or pattern_lower in value.lower(): matches.append(f" {path}\\{name} = {value[:200]}") if len(matches) >= 50: matches.append(f"[Truncated: more than 50 matches for '{pattern}']") break if not matches: return f"No registry entries matching '{pattern}' found." return "\n".join(matches) except Exception as e: return f"[Error searching registry: {e}]"