From b4a462d7ec72a4116be4a5542987f2188fc72163 Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:26:01 +0100 Subject: [PATCH] Add NTDS BitLocker recovery information --- dissect/database/ese/ntds/objects/__init__.py | 2 + dissect/database/ese/ntds/objects/computer.py | 7 +++ .../ntds/objects/msfve_recoveryinformation.py | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 dissect/database/ese/ntds/objects/msfve_recoveryinformation.py diff --git a/dissect/database/ese/ntds/objects/__init__.py b/dissect/database/ese/ntds/objects/__init__.py index 566ae34..0acaf9c 100644 --- a/dissect/database/ese/ntds/objects/__init__.py +++ b/dissect/database/ese/ntds/objects/__init__.py @@ -68,6 +68,7 @@ from dissect.database.ese.ntds.objects.msds_resourcepropertylist import MSDSResourcePropertyList from dissect.database.ese.ntds.objects.msds_shadowprincipalcontainer import MSDSShadowPrincipalContainer from dissect.database.ese.ntds.objects.msds_valuetype import MSDSValueType +from dissect.database.ese.ntds.objects.msfve_recoveryinformation import MSFVERecoveryInformation from dissect.database.ese.ntds.objects.msimaging_psps import MSImagingPSPs from dissect.database.ese.ntds.objects.mskds_provserverconfiguration import MSKDSProvServerConfiguration from dissect.database.ese.ntds.objects.msmqenterprisesettings import MSMQEnterpriseSettings @@ -174,6 +175,7 @@ "MSDSResourcePropertyList", "MSDSShadowPrincipalContainer", "MSDSValueType", + "MSFVERecoveryInformation", "MSImagingPSPs", "MSKDSProvServerConfiguration", "MSMQEnterpriseSettings", diff --git a/dissect/database/ese/ntds/objects/computer.py b/dissect/database/ese/ntds/objects/computer.py index 28d500e..c267620 100644 --- a/dissect/database/ese/ntds/objects/computer.py +++ b/dissect/database/ese/ntds/objects/computer.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING +from dissect.database.ese.ntds.objects.msfve_recoveryinformation import MSFVERecoveryInformation from dissect.database.ese.ntds.objects.user import User if TYPE_CHECKING: @@ -22,6 +23,12 @@ class Computer(User): def __repr_body__(self) -> str: return f"name={self.name!r}" + def fve_recovery_information(self) -> Iterator[MSFVERecoveryInformation]: + """Return the BitLocker recovery information objects associated with this computer.""" + for child in self.children(): + if isinstance(child, MSFVERecoveryInformation): + yield child + def managed_by(self) -> Iterator[Object]: """Return the objects that manage this computer.""" self._assert_local() diff --git a/dissect/database/ese/ntds/objects/msfve_recoveryinformation.py b/dissect/database/ese/ntds/objects/msfve_recoveryinformation.py new file mode 100644 index 0000000..28a2019 --- /dev/null +++ b/dissect/database/ese/ntds/objects/msfve_recoveryinformation.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from uuid import UUID + +from dissect.database.ese.ntds.objects.top import Top + +if TYPE_CHECKING: + from dissect.database.ese.ntds.objects import Computer + + +class MSFVERecoveryInformation(Top): + """Represents a msFVE-RecoveryInformation object in the Active Directory. + + References: + - https://learn.microsoft.com/en-us/windows/win32/adschema/c-msfve-recoveryinformation + """ + + __object_class__ = "msFVE-RecoveryInformation" + + @property + def volume_guid(self) -> UUID: + """Return the volume GUID associated with this recovery information.""" + return UUID(bytes_le=self.get("msFVE-VolumeGuid")) + + @property + def recovery_guid(self) -> UUID: + """Return the recovery GUID associated with this recovery information.""" + return UUID(bytes_le=self.get("msFVE-RecoveryGuid")) + + @property + def recovery_password(self) -> str: + """Return the recovery password associated with this recovery information.""" + return self.get("msFVE-RecoveryPassword") + + @property + def key_package(self) -> bytes | None: + """Return the key package associated with this recovery information, if any.""" + return self.get("msFVE-KeyPackage") + + def computer(self) -> Computer: + """Return the computer object associated with this recovery information.""" + if (parent := self.parent()) is None: + raise ValueError("msFVE-RecoveryInformation object has no parent computer") + return parent