Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
function Close-ProcessesBySid {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidatePattern("^S-\d-\d+-(\d+-){1,14}\d+$")]
[string]$Sid,

# sihost.exe, svchost.exe, WidgetService.exe, dllhost.exe, ctfmon.exe, svchost.exe
[Parameter()]
[string[]]$Blacklist = @(
"ShellExperienceHost.exe"
),

[Parameter()]
[switch]$Force
)

$summary = [ordered]@{
Total = 0
Blocked = 0
Closed = 0
FailedClose = 0
}

Write-ToLog -Message "Close-ProcessesBySid start: SID=$Sid Force=$Force Blacklist=$($Blacklist -join ',')" -Level Verbose -Step "Close-ProcessesBySid"

$resultList = New-Object System.Collections.ArrayList
$processes = Get-CimInstance Win32_Process
if (-not $processes -or $processes.Count -eq 0) {
Write-ToLog -Message "No processes running on the system" -Level Verbose -Step "Close-ProcessesBySid"
return $resultList
}

foreach ($proc in $processes) {
if (-not $proc.ProcessId -or -not $proc.Name) {
continue
}

try {
$ownerSid = (Invoke-CimMethod -InputObject $proc -MethodName GetOwnerSid -ErrorAction Stop).Sid
} catch {
continue
}

if ($ownerSid -ne $Sid) {
continue
}

$summary.Total++

$blockedFound = $false
$closedResult = $false
$blockedNames = @()

if ($Blacklist -contains $proc.Name) {
$blockedFound = $true
$blockedNames += $proc.Name
$summary.Blocked++
Write-ToLog -Message "Blocked (blacklist): $($proc.Name) pid=$($proc.ProcessId)" -Level Verbose -Step "Close-ProcessesBySid"
} else {
try {
$defaultArgs = @('/PID', $proc.ProcessId.ToString(), '/T')
if ($Force) { $defaultArgs += '/F' }

$process = Start-Process -FilePath 'taskkill.exe' `
-ArgumentList $defaultArgs `
-NoNewWindow `
-PassThru `
-Wait `
-ErrorAction Stop

$closedResult = ($process.ExitCode -eq 0)

# Re-check to confirm the process is actually gone
try {
$stillRunning = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
} catch {
$stillRunning = $null
}
if ($stillRunning) {
$closedResult = $false
}
if ($closedResult) {
$summary.Closed++
Write-ToLog -Message "Closed: $($proc.Name) pid=$($proc.ProcessId)" -Level Verbose -Step "Close-ProcessesBySid"
} else {
$summary.FailedClose++
Write-ToLog -Message "Close failed (exit $($process.ExitCode)): $($proc.Name) pid=$($proc.ProcessId)" -Level Warning -Step "Close-ProcessesBySid"
}
} catch {
$closedResult = $false
$summary.FailedClose++
Write-ToLog -Message "Close threw: $($proc.Name) pid=$($proc.ProcessId) error=$($_.Exception.Message)" -Level Warning -Step "Close-ProcessesBySid"
}
}

$resultList.Add(
[PSCustomObject]@{
ProcessName = $proc.Name
ProcessID = $proc.ProcessId
Closed = if ($blockedFound) { $false } else { $closedResult }
WasBlockedByBlacklist = $blockedFound
BlacklistedProcessesFound = if ($blockedFound) { $blockedNames -join ',' } else { '' }
}
) | Out-Null
}

Write-ToLog -Message "Close-ProcessesBySid summary: total=$($summary.Total) blocked=$($summary.Blocked) closed=$($summary.Closed) failed=$($summary.FailedClose)" -Level Verbose -Step "Close-ProcessesBySid"

if ($summary.Total -eq 0) {
Write-ToLog -Message "No processes running for SID: $Sid" -Level Verbose -Step "Close-ProcessesBySid"
}

return $resultList
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@ Function Backup-RegistryHive {
Copy-Item -Path "$profileImagePath\NTUSER.DAT" -Destination "$profileImagePath\NTUSER.DAT.BAK" -ErrorAction Stop
Copy-Item -Path "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat" -Destination "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" -ErrorAction Stop
} catch {
$processList = Get-ProcessByOwner -username $domainUsername
if ($processList) {
Show-ProcessListResult -ProcessList $processList -domainUsername $domainUsername
# $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess
$closeResults = Close-ProcessesBySid -Sid $SID -Force
if ($closeResults) {
$closedCount = ($closeResults | Where-Object { $_.Closed } | Measure-Object).Count
$blockedCount = ($closeResults | Where-Object { $_.WasBlockedByBlacklist } | Measure-Object).Count
$totalCount = ($closeResults | Measure-Object).Count
Write-ToLog -Message "Closed processes: $closedCount, blocked: $blockedCount, total scanned: $totalCount" -Level Verbose -Step "Backup-RegistryHive"
}

try {
Set-RegistryExe -op Unload -hive root -UserSid $SID -ProfilePath $profileImagePath -ThrowOnFailure | Out-Null
} catch {
Write-ToLog -Message "Unload root failed after process close: $($_.Exception.Message)" -Level Warning -Step "Backup-RegistryHive"
}
try {
Set-RegistryExe -op Unload -hive classes -UserSid $SID -ProfilePath $profileImagePath -ThrowOnFailure | Out-Null
} catch {
Write-ToLog -Message "Unload classes failed after process close: $($_.Exception.Message)" -Level Warning -Step "Backup-RegistryHive"
}

try {
Write-ToLog -Message("Initial backup was not successful, trying again...") -Level Verbose -Step "Backup-RegistryHive"
Write-ToLog $CloseResults -Level Verbose -Step "Backup-RegistryHive"
Start-Sleep 1
# retry:
Copy-Item -Path "$profileImagePath\NTUSER.DAT" -Destination "$profileImagePath\NTUSER.DAT.BAK" -ErrorAction Stop
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ function Set-RegistryExe {
# User Security Identifier
[Parameter(Mandatory = $true)]
[ValidatePattern("^S-\d-\d+-(\d+-){1,14}\d+$")]
[System.String]$UserSid
[System.String]$UserSid,
[Parameter()]
[switch]$ThrowOnFailure
)
begin {
switch ($hive) {
Expand All @@ -38,6 +40,13 @@ function Set-RegistryExe {
}
}
$status = Get-RegistryExeStatus $results

if (-not $status -and $ThrowOnFailure.IsPresent) {
$resultText = if ($results) { ($results | Out-String).Trim() } else { "No output" }
$errorMessage = "Set-RegistryExe $op $key failed. Details: $resultText"
Write-ToLog -Message $errorMessage -Level Warning -Step "Set-RegistryExe"
throw [System.InvalidOperationException]::new($errorMessage)
}
}
end {
# Status here will be either true or false depending on whether or not the tool was able to perform the registry action requested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ function Set-UserRegistryLoadState {
if ($results) {
Write-ToLog "Load Successful: $results" -Level Verbose -Step "Set-UserRegistryLoadState"
} else {
$processList = Get-ProcessByOwner -username $username
if ($processList) {
Show-ProcessListResult -ProcessList $processList -domainUsername $username
# $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess
$closeResults = Close-ProcessesBySid -Sid $UserSid -Force
if ($closeResults) {
if ($closeResults | Where-Object { $_.WasBlockedByBlacklist }) {
throw "Registry Load $key blocked by active session for $UserSid"
}
}
Set-UserRegistryLoadState -op Load -ProfilePath $ProfilePath -UserSid $UserSid -counter $counter -hive root
}
Expand All @@ -62,10 +63,11 @@ function Set-UserRegistryLoadState {
if ($results) {
Write-ToLog "Load Successful: $results" -Level Verbose -Step "Set-UserRegistryLoadState"
} else {
$processList = Get-ProcessByOwner -username $username
if ($processList) {
Show-ProcessListResult -ProcessList $processList -domainUsername $username
# $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess
$closeResults = Close-ProcessesBySid -Sid $UserSid -Force
if ($closeResults) {
if ($closeResults | Where-Object { $_.WasBlockedByBlacklist }) {
throw "Registry Load $key blocked by active session for $UserSid"
}
}
Set-UserRegistryLoadState -op Load -ProfilePath $ProfilePath -UserSid $UserSid -counter $counter -hive classes
}
Expand All @@ -84,10 +86,11 @@ function Set-UserRegistryLoadState {
Write-ToLog "Unload Successful: $results" -Level Verbose -Step "Set-UserRegistryLoadState"

} else {
$processList = Get-ProcessByOwner -username $username
if ($processList) {
Show-ProcessListResult -ProcessList $processList -domainUsername $username
# $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess
$closeResults = Close-ProcessesBySid -Sid $UserSid -Force
if ($closeResults) {
if ($closeResults | Where-Object { $_.WasBlockedByBlacklist }) {
throw "Registry Unload $key blocked by active session for $UserSid"
}
}
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid -counter $counter -hive root
}
Expand All @@ -100,10 +103,11 @@ function Set-UserRegistryLoadState {
Write-ToLog "Unload Successful: $results" -Level Verbose -Step "Set-UserRegistryLoadState"

} else {
$processList = Get-ProcessByOwner -username $username
if ($processList) {
Show-ProcessListResult -ProcessList $processList -domainUsername $username
# $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess
$closeResults = Close-ProcessesBySid -Sid $UserSid -Force
if ($closeResults) {
if ($closeResults | Where-Object { $_.WasBlockedByBlacklist }) {
throw "Registry Unload $key blocked by active session for $UserSid"
}
}
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid -counter $counter -hive classes
}
Expand Down
Loading
Loading