more clarity

This commit is contained in:
2026-07-08 16:44:12 -04:00
parent faddaba0ed
commit db8fc541df
+119 -35
View File
@@ -682,21 +682,57 @@ Function Write-UserLogonActivityOutput {
Write-PlainTextTable -Rows $Rows -Columns @('Date', 'Time', 'User', 'Activity', 'Source', 'Details')
}
Function Get-PossibleUseWindowAction {
Function Get-LocalUnlockedWindowAction {
Param(
[AllowNull()]
[string]$Activity
)
Switch ($Activity) {
'session reconnect' { Return 'reconnect' }
'session disconnect' { Return 'disconnect' }
'session reconnect' { Return 'RDP reconnect' }
'session disconnect' { Return 'session disconnect' }
'screensaver dismiss' { Return 'screensaver dismiss' }
'screensaver start' { Return 'screensaver start' }
Default { Return $Activity }
}
}
Function Format-LocalUnlockedDuration {
Param(
[Parameter(Mandatory=$true)]
[datetime]$StartTime,
[Parameter(Mandatory=$true)]
[datetime]$EndTime,
[switch]$Maximum
)
$Duration = $EndTime - $StartTime
If ($Duration.TotalSeconds -lt 0) {
$Duration = [TimeSpan]::Zero
}
If ($Duration.Days -gt 0) {
$Text = '{0}d {1}h {2}m' -f $Duration.Days, $Duration.Hours, $Duration.Minutes
}
ElseIf ($Duration.Hours -gt 0) {
$Text = '{0}h {1}m' -f $Duration.Hours, $Duration.Minutes
}
ElseIf ($Duration.Minutes -gt 0) {
$Text = '{0}m {1}s' -f $Duration.Minutes, $Duration.Seconds
}
Else {
$Text = '{0}s' -f $Duration.Seconds
}
If ($Maximum) {
Return "up to ${Text}"
}
Return $Text
}
Function Test-RedundantOpeningUnlock {
Param(
[Parameter(Mandatory=$true)]
@@ -712,7 +748,10 @@ Function Test-RedundantOpeningUnlock {
ForEach ($CandidateEvent in @($ActivityEvents)) {
If (
$CandidateEvent.Activity -in @('logon', 'session reconnect') -and
(
$CandidateEvent.Activity -eq 'logon' -or
(Test-RemoteSessionReconnect -ActivityEvent $CandidateEvent)
) -and
[Math]::Abs(($CandidateEvent.TimeCreated - $ActivityEvent.TimeCreated).TotalSeconds) -le 5
) {
Return $true
@@ -722,23 +761,56 @@ Function Test-RedundantOpeningUnlock {
Return $false
}
Function Get-PossibleUseWindow {
Function Get-ActivityEventDetailValue {
Param(
[Parameter(Mandatory=$true)]
[object]$ActivityEvent,
[Parameter(Mandatory=$true)]
[string]$Name
)
$Details = [string]$ActivityEvent.Details
If ([string]::IsNullOrWhiteSpace($Details)) { Return $null }
ForEach ($Part in ($Details -split ';')) {
$TrimmedPart = $Part.Trim()
If ($TrimmedPart -match "^$([regex]::Escape($Name))=(.*)$") {
Return $Matches[1].Trim()
}
}
Return $null
}
Function Test-RemoteSessionReconnect {
Param(
[Parameter(Mandatory=$true)]
[object]$ActivityEvent
)
If ($ActivityEvent.Activity -ne 'session reconnect') { Return $false }
$Address = Get-ActivityEventDetailValue -ActivityEvent $ActivityEvent -Name 'Address'
If ([string]::IsNullOrWhiteSpace($Address)) { Return $false }
If ($Address -eq '-' -or $Address -ieq 'LOCAL') { Return $false }
Return $true
}
Function Get-LocalUnlockedWindow {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$OpenWindow = $null
$UseWindows = New-Object System.Collections.Generic.List[object]
$OpeningActivities = @('logon', 'unlock', 'remote logon', 'session reconnect', 'screensaver dismiss')
$ImpliedClosureDescriptions = @{
'unlock' = 'lock before unlock'
'session reconnect' = 'disconnect before reconnect'
'screensaver dismiss' = 'screensaver before dismiss'
}
$ImpliedClosureNotes = @{
$UnlockedWindows = New-Object System.Collections.Generic.List[object]
$OpeningActivities = @('logon', 'unlock', 'screensaver dismiss')
$ClosingActivities = @('logoff', 'lock', 'screensaver start', 'session disconnect', 'session reconnect', 'remote logon', 'sleep', 'shutdown', 'restart')
$RepeatedOpeningClosureDescriptions = @{
'unlock' = 'lock time unknown'
'session reconnect' = 'disconnect time unknown'
'screensaver dismiss' = 'screensaver start unknown'
}
@@ -753,16 +825,26 @@ Function Get-PossibleUseWindow {
Continue
}
If ($Activity -in @('logoff', 'lock', 'session disconnect', 'sleep', 'shutdown', 'restart')) {
If ($Activity -eq 'session reconnect' -and -not (Test-RemoteSessionReconnect -ActivityEvent $ActivityEvent)) {
Continue
}
If ($Activity -in $ClosingActivities) {
If ($null -ne $OpenWindow) {
$UseWindows.Add([PSCustomObject]@{
$Note = ''
If ($Activity -eq 'session reconnect' -or $Activity -eq 'remote logon') {
$Note = 'local console unavailable during remote session'
}
$UnlockedWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = $ActivityEvent.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
Duration = Format-LocalUnlockedDuration -StartTime $OpenWindow.StartTime -EndTime $ActivityEvent.TimeCreated
User = $OpenWindow.User
Opened = $OpenWindow.Opened
Closed = Get-PossibleUseWindowAction -Activity $Activity
Note = ''
Closed = Get-LocalUnlockedWindowAction -Activity $Activity
Note = $Note
}) | Out-Null
$OpenWindow = $null
}
@@ -775,15 +857,16 @@ Function Get-PossibleUseWindow {
}
If ($Activity -in $OpeningActivities) {
If ($null -ne $OpenWindow -and $ImpliedClosureDescriptions.ContainsKey($Activity)) {
$UseWindows.Add([PSCustomObject]@{
If ($null -ne $OpenWindow -and $RepeatedOpeningClosureDescriptions.ContainsKey($Activity)) {
$UnlockedWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = "before $($ActivityEvent.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'))"
Duration = Format-LocalUnlockedDuration -StartTime $OpenWindow.StartTime -EndTime $ActivityEvent.TimeCreated -Maximum
User = $OpenWindow.User
Opened = $OpenWindow.Opened
Closed = $ImpliedClosureDescriptions[$Activity]
Note = $ImpliedClosureNotes[$Activity]
Closed = $RepeatedOpeningClosureDescriptions[$Activity]
Note = 'latest possible duration'
}) | Out-Null
$OpenWindow = $null
}
@@ -792,17 +875,18 @@ Function Get-PossibleUseWindow {
$OpenWindow = [PSCustomObject]@{
StartTime = $ActivityEvent.TimeCreated
User = $User
Opened = Get-PossibleUseWindowAction -Activity $Activity
Opened = Get-LocalUnlockedWindowAction -Activity $Activity
}
}
}
}
If ($null -ne $OpenWindow) {
$UseWindows.Add([PSCustomObject]@{
$UnlockedWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = 'open through returned events'
Duration = 'open'
User = $OpenWindow.User
Opened = $OpenWindow.Opened
Closed = 'not returned'
@@ -810,25 +894,25 @@ Function Get-PossibleUseWindow {
}) | Out-Null
}
Return $UseWindows
Return $UnlockedWindows
}
Function Write-PossibleUseWindowOutput {
Function Write-LocalUnlockedWindowOutput {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$UseWindows = @(Get-PossibleUseWindow -ActivityEvents $ActivityEvents | Sort-Object -Property StartTime)
$UnlockedWindows = @(Get-LocalUnlockedWindow -ActivityEvents $ActivityEvents | Sort-Object -Property StartTime)
If ($UseWindows.Count -eq 0) {
Write-Output 'Possible use windows inferred from returned events: none'
If ($UnlockedWindows.Count -eq 0) {
Write-Output 'Local unlocked windows inferred from returned events: none'
Return
}
$Rows = @($UseWindows | Select-Object Start,End,User,Opened,Closed,Note)
Write-Output 'Possible use windows inferred from returned events:'
Write-PlainTextTable -Rows $Rows -Columns @('Start', 'End', 'User', 'Opened', 'Closed', 'Note')
$Rows = @($UnlockedWindows | Select-Object Start,End,Duration,User,Opened,Closed,Note)
Write-Output 'Local unlocked windows inferred from returned events:'
Write-PlainTextTable -Rows $Rows -Columns @('Start', 'End', 'Duration', 'User', 'Opened', 'Closed', 'Note')
}
# Winlogon 7001/7002 tracks user session logon/logoff more reliably than Security 4624/4634.
@@ -945,9 +1029,9 @@ ForEach ($PowerFilter in $PowerFilters) {
$OutputEvents = @(Get-UniqueUserLogonActivity -ActivityEvents $ActivityEvents | Sort-Object -Property TimeCreated)
Write-UserLogonActivityOutput -ActivityEvents $OutputEvents -StartDateTime $StartDateTime -EndDateTime $EndDateTime
Try {
Write-PossibleUseWindowOutput -ActivityEvents $OutputEvents
Write-LocalUnlockedWindowOutput -ActivityEvents $OutputEvents
}
Catch {
$PossibleUseWindowError = Get-UserLogonActivityErrorMessage -ErrorRecord $_ -FallbackMessage 'Unable to infer possible use windows.'
Write-Output "Possible use windows inferred from returned events: unavailable. ${PossibleUseWindowError}"
$LocalUnlockedWindowError = Get-UserLogonActivityErrorMessage -ErrorRecord $_ -FallbackMessage 'Unable to infer local unlocked windows.'
Write-Output "Local unlocked windows inferred from returned events: unavailable. ${LocalUnlockedWindowError}"
}