Public/Mount-VBUserHive.ps1
|
function Mount-VBUserHive { <# .SYNOPSIS Mount a user's NTUSER.DAT hive to the registry for offline editing. .DESCRIPTION Resolves a user profile by SID, Username, or ProfilePath and mounts the associated NTUSER.DAT hive to HKEY_USERS. Works locally or remotely via Invoke-Command. Returns detailed status including whether the hive was newly mounted or already loaded. .PARAMETER SID User security identifier (e.g., S-1-5-21-...). .PARAMETER Username Username to resolve (domain\user or local user format). .PARAMETER ProfilePath Direct path to the user profile (overrides auto-resolution). .PARAMETER ComputerName Target computer name. Default is local machine. .PARAMETER Credential Credentials for remote operations on $ComputerName. .EXAMPLE Mount-VBUserHive -Username 'contoso\jdoe' .EXAMPLE Get-ADUser jdoe | Mount-VBUserHive -ComputerName SERVER01 .VERSION v1.0 - 2026-04-16 #> [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName)] [string]$SID, [Parameter(ValueFromPipelineByPropertyName)] [string]$Username, [Parameter(ValueFromPipelineByPropertyName)] [string]$ProfilePath, [Parameter(ValueFromPipelineByPropertyName)] [string]$ComputerName = $env:COMPUTERNAME, [PSCredential]$Credential ) process { try { #region --- Resolve Profile --- $allProfiles = @{ Local = { Get-CimInstance -ClassName Win32_UserProfile -Filter "Special = 'False'" -ErrorAction Stop } Remote = { Get-CimInstance -ClassName Win32_UserProfile -Filter "Special = 'False'" -ComputerName $ComputerName -Credential $Credential -ErrorAction Stop } } $profiles = if ($ComputerName -eq $env:COMPUTERNAME) { & $allProfiles.Local } else { & $allProfiles.Remote } $targetProfile = $null if ($SID) { $targetProfile = $profiles | Where-Object { $_.SID -eq $SID } } elseif ($Username) { foreach ($profile in $profiles) { try { $fullAccount = (New-Object System.Security.Principal.SecurityIdentifier($profile.SID)).Translate([System.Security.Principal.NTAccount]).Value $profileUsername = if ($fullAccount -match '\\') { ($fullAccount -split '\\')[1] } else { $fullAccount } if ($profileUsername -eq $Username) { $targetProfile = $profile break } } catch { continue } } } if (-not $targetProfile) { $notFoundMsg = if ($SID) { "SID: $SID" } else { "Username: $Username" } throw "Profile not found for $notFoundMsg" } $resolvedSID = $targetProfile.SID $resolvedProfilePath = if ($ProfilePath) { $ProfilePath } else { $targetProfile.LocalPath } # Resolve username if not provided $resolvedUsername = $Username if (-not $resolvedUsername) { try { $fullAccount = (New-Object System.Security.Principal.SecurityIdentifier($resolvedSID)).Translate([System.Security.Principal.NTAccount]).Value $resolvedUsername = if ($fullAccount -match '\\') { ($fullAccount -split '\\')[1] } else { $fullAccount } } catch { $resolvedUsername = "Unknown" } } #endregion #region --- Mount Hive (Local or Remote) --- $scriptBlock = { param($SID, $ProfilePath) $ntuserPath = Join-Path $ProfilePath 'NTUSER.DAT' if (-not (Test-Path $ntuserPath)) { throw "NTUSER.DAT not found at $ntuserPath" } $loadedSIDs = Get-ChildItem "Registry::HKEY_USERS" | Select-Object -ExpandProperty PSChildName if ($loadedSIDs -contains $SID) { return @{ AlreadyLoaded = $true; HiveMounted = $false } } $regResult = reg.exe load "HKU\$SID" "$ntuserPath" 2>&1 if ($LASTEXITCODE -ne 0) { throw "Registry load failed: $regResult" } return @{ AlreadyLoaded = $false; HiveMounted = $true } } $mountResult = if ($ComputerName -eq $env:COMPUTERNAME) { & $scriptBlock $resolvedSID $resolvedProfilePath } else { Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $resolvedSID, $resolvedProfilePath } #endregion # Return success [PSCustomObject]@{ ComputerName = $ComputerName SID = $resolvedSID Username = $resolvedUsername ProfilePath = $resolvedProfilePath HiveMounted = $mountResult.HiveMounted AlreadyLoaded = $mountResult.AlreadyLoaded Status = 'Success' } } catch { [PSCustomObject]@{ ComputerName = $ComputerName SID = $SID Username = $Username ProfilePath = $ProfilePath HiveMounted = $false AlreadyLoaded = $false Error = $_.Exception.Message Status = 'Failed' } } } } |