Improve auditpol parsing for RU logon/logoff category output

This commit is contained in:
Луценко Андрей Анатольевич
2026-04-11 22:14:31 +10:00
parent 0c9faece92
commit 65469cf1d2
+118 -47
View File
@@ -192,8 +192,7 @@ function Enable-SecurityAudit {
} }
} }
function Test-RussianAuditPolUi { function Test-RussianUiPreferred {
# Эвристика: если auditpol локализован, не дергаем английские subcategory имена (они дают код 87 и шумят в логах).
try { try {
if ((Get-Culture).TwoLetterISOLanguageName -eq 'ru') { return $true } if ((Get-Culture).TwoLetterISOLanguageName -eq 'ru') { return $true }
} catch { } } catch { }
@@ -205,66 +204,138 @@ function Enable-SecurityAudit {
return ($r.Text -match 'Вход/выход') return ($r.Text -match 'Вход/выход')
} }
function Test-LogonAuditEnabled { function Test-SuccessAndFailureText {
# На русской Windows подкатегория называется "Вход в систему", а не "Logon". param([string]$Line)
# В выводе auditpol обычно встречаются слова "успех" и "отказ" (или Success/Failure). if ([string]::IsNullOrWhiteSpace($Line)) { return $false }
$isRu = Test-RussianAuditPolUi # RU: "Успех и сбой" (часто в выводе категории)
$candidates = @('/get /subcategory:"Вход в систему"') if ($Line -match '(?i)успех\s+и\s+сбой') { return $true }
if (-not $isRu) { # Иногда встречается без пробелов вокруг "и" из-за форматирования/переносов
$candidates += '/get /subcategory:"Logon"' if ($Line -match '(?i)успех\s*и\s*сбой') { return $true }
} # EN fallback
if (($Line -match '(?i)Success') -and ($Line -match '(?i)Failure')) { return $true }
foreach ($args in $candidates) {
$r = Invoke-AuditPol -Arguments $args
if ($r.ExitCode -ne 0) {
Write-Log ("Не удалось прочитать auditpol (код {0}) для: {1}. Вывод:`n{2}" -f $r.ExitCode, $args.Trim(), $r.Text)
continue
}
$t = $r.Text
if (($t -match '(?i)Success') -and ($t -match '(?i)Failure')) { return $true }
if (($t -match '(?i)успех') -and ($t -match '(?i)отказ')) { return $true }
}
return $false return $false
} }
if (Test-LogonAuditEnabled) { function Get-CategorySettingLine {
Write-Log "Аудит входа уже настроен (Success+Failure)" param(
return [Parameter(Mandatory = $true)][string]$CategoryName,
[Parameter(Mandatory = $true)][string]$SubcategoryLabel
)
$r = Invoke-AuditPol -Arguments ('/get /category:"{0}"' -f $CategoryName)
if ($r.ExitCode -ne 0) {
return [pscustomobject]@{ Ok = $false; ExitCode = $r.ExitCode; Text = $r.Text; Line = $null }
} }
Write-Log "Аудит входа не настроен полностью. Пытаюсь включить..." $lines = $r.Text -split "`r?`n"
foreach ($ln in $lines) {
$t = ($ln -replace '\s+', ' ').Trim()
if ([string]::IsNullOrWhiteSpace($t)) { continue }
# Порядок попыток: от самого "каноничного" к более совместимому. # Ищем по подстроке без жёсткой привязки к количеству пробелов
$isRu = Test-RussianAuditPolUi if ($t -notlike ('*{0}*' -f $SubcategoryLabel)) { continue }
$attempts = New-Object System.Collections.Generic.List[string] return [pscustomobject]@{ Ok = $true; ExitCode = 0; Text = $r.Text; Line = $t }
# RU Windows (как в вашем выводе auditpol /list /subcategory:*)
$attempts.Add('/set /subcategory:"Вход в систему" /success:enable /failure:enable')
$attempts.Add('/set /category:"Вход/выход" /success:enable /failure:enable')
if (-not $isRu) {
# EN Windows
$attempts.Add('/set /subcategory:"Logon" /success:enable /failure:enable')
$attempts.Add('/set /category:"Logon/Logoff" /success:enable /failure:enable')
} }
# GUID категории (как было у вас изначально)
$attempts.Add('/set /category:"{69979849-797A-11D9-BED3-505054503030}" /success:enable /failure:enable')
foreach ($a in $attempts) { return [pscustomobject]@{ Ok = $true; ExitCode = 0; Text = $r.Text; Line = $null }
$r = Invoke-AuditPol -Arguments $a }
if ($r.ExitCode -eq 0) {
Write-Log ("auditpol OK: {0}" -f $a.Trim()) function Ensure-RuLogonLogoffSubcategories {
# Как вы описали: смотрим категорию "Вход/выход" и добиваем две ключевые подкатегории.
$category = "Вход/выход"
$targets = @(
"Вход в систему",
"Выход из системы"
)
foreach ($sub in $targets) {
$cur = Get-CategorySettingLine -CategoryName $category -SubcategoryLabel $sub
if (-not $cur.Ok) {
Write-Log ("Не удалось прочитать auditpol /get /category для '{0}' (код {1}). Вывод:`n{2}" -f $category, $cur.ExitCode, $cur.Text)
return $false
}
if ($null -eq $cur.Line) {
$frag = $cur.Text
if ($frag.Length -gt 4000) { $frag = $frag.Substring(0, 4000) + "`n... (truncated)" }
Write-Log ("В выводе категории '{0}' не найдена строка подкатегории '{1}'. Вывод (фрагмент):`n{2}" -f $category, $sub, $frag)
return $false
}
if (Test-SuccessAndFailureText -Line $cur.Line) {
Write-Log ("Аудит уже 'Успех и сбой' для: {0} :: {1}" -f $sub, $cur.Line)
continue
}
Write-Log ("Требуется включить Success+Failure для подкатегории: {0}. Текущая строка: {1}" -f $sub, $cur.Line)
$setArgs = ('/set /subcategory:"{0}" /success:enable /failure:enable' -f $sub)
$set = Invoke-AuditPol -Arguments $setArgs
if ($set.ExitCode -ne 0) {
Write-Log ("auditpol SET FAIL (код {0}): {1}`n{2}" -f $set.ExitCode, $setArgs, $set.Text)
return $false
}
$after = Get-CategorySettingLine -CategoryName $category -SubcategoryLabel $sub
if ($null -ne $after.Line -and (Test-SuccessAndFailureText -Line $after.Line)) {
Write-Log ("OK: подкатегория '{0}' приведена к 'Успех и сбой'. Строка: {1}" -f $sub, $after.Line)
} else { } else {
Write-Log ("auditpol FAIL (код {0}): {1}`n{2}" -f $r.ExitCode, $a.Trim(), $r.Text) Write-Log ("ПОСЛЕ SET строка для '{0}' всё ещё не похожа на 'Успех и сбой': {1}" -f $sub, $after.Line)
return $false
}
} }
if (Test-LogonAuditEnabled) { return $true
Write-Log "Аудит входа успешно включен (Success+Failure)" }
function Ensure-EnLogonLogoffSubcategories {
$category = "Logon/Logoff"
$targets = @("Logon", "Logoff")
foreach ($sub in $targets) {
$cur = Get-CategorySettingLine -CategoryName $category -SubcategoryLabel $sub
if (-not $cur.Ok) {
Write-Log ("Не удалось прочитать auditpol /get /category для '{0}' (код {1}). Вывод:`n{2}" -f $category, $cur.ExitCode, $cur.Text)
return $false
}
if ($null -eq $cur.Line) {
Write-Log ("В выводе категории '{0}' не найдена строка подкатегории '{1}'." -f $category, $sub)
return $false
}
if (Test-SuccessAndFailureText -Line $cur.Line) { continue }
$setArgs = ('/set /subcategory:"{0}" /success:enable /failure:enable' -f $sub)
$set = Invoke-AuditPol -Arguments $setArgs
if ($set.ExitCode -ne 0) {
Write-Log ("auditpol SET FAIL (код {0}): {1}`n{2}" -f $set.ExitCode, $setArgs, $set.Text)
return $false
}
}
return $true
}
$preferRu = Test-RussianUiPreferred
if ($preferRu) {
if (Ensure-RuLogonLogoffSubcategories) {
Write-Log "Проверка аудита (RU): OK для 'Вход в систему' и 'Выход из системы'"
return return
} }
Write-Log "ВНИМАНИЕ (RU): не удалось автоматически привести аудит подкатегорий 'Вход/выход' к 'Успех и сбой' через auditpol. Скрипт продолжит работу, но часть событий может отсутствовать. Проверьте доменную/локальную GPO (часто мешает централизованный Advanced Audit Policy)."
return
} }
Write-Log "ВНИМАНИЕ: не удалось автоматически включить аудит входа через auditpol. Скрипт продолжит работу, но часть событий может отсутствовать. Проверьте политику аудита вручную (локальная/доменная GPO)." if (Ensure-EnLogonLogoffSubcategories) {
Write-Log "Проверка аудита (EN): OK для Logon/Logoff"
return
}
# Fallback: старый GUID категории Logon/Logoff (Microsoft). Это не "UID пользователя", а GUID категории политики аудита.
Write-Log "Пробую fallback через GUID категории Logon/Logoff..."
$guidSet = Invoke-AuditPol -Arguments '/set /category:"{69979849-797A-11D9-BED3-505054503030}" /success:enable /failure:enable'
if ($guidSet.ExitCode -ne 0) {
Write-Log ("auditpol GUID SET FAIL (код {0}):`n{1}" -f $guidSet.ExitCode, $guidSet.Text)
}
Write-Log "ВНИМАНИЕ: не удалось автоматически настроить аудит входа/выхода через auditpol. Скрипт продолжит работу, но часть событий может отсутствовать. Проверьте политику аудита вручную (локальная/доменная GPO)."
} }
function Send-Heartbeat { function Send-Heartbeat {