Загрузка данных


#requires -version 2.0
<#
.SYNOPSIS
    Аудит отключения SMBv1 (клиент + сервер), фактическое состояние.
.NOTES
    Запуск: startup-скрипт GPO (SYSTEM) или logon-скрипт с правами админа.
    Принудительный повтор без учёта маркера: задать переменную среды SMB1_AUDIT_FORCE=1
    Пропуск задержки: SMB1_AUDIT_NODELAY=1
#>

# ============ CONFIG ============
$ResultDirectory      = "\\192.168.6.7\report\smbv1\"
$SummaryCsv           = Join-Path $ResultDirectory "_summary.csv"
$LocalLogDirectory    = "C:\smb_v1_check"
$MinDelaySeconds      = 60
$MaxDelaySeconds      = 300   # НЕ выше 540: startup-скрипт GPO живёт 600 сек
$MarkerExpireDays     = 30    # через столько дней аудит повторяется
# ================================

$ComputerName = $env:COMPUTERNAME
$Results = New-Object System.Collections.ArrayList
$FailCount = 0; $WarnCount = 0; $PassCount = 0

function Add-Result([string]$Status, [string]$Name, [string]$Details) {
    [void]$Results.Add(("[{0}] {1}: {2}" -f $Status, $Name, $Details))
    switch ($Status) {
        "PASS" { $script:PassCount++ }
        "FAIL" { $script:FailCount++ }
        "WARN" { $script:WarnCount++ }
    }
}

function Add-Section([string]$Title) {
    [void]$Results.Add("")
    [void]$Results.Add(("-" * 60))
    [void]$Results.Add($Title)
    [void]$Results.Add(("-" * 60))
}

function Get-RegistryValueSafe([string]$Path, [string]$Name) {
    try {
        $p = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop
        return $p.$Name
    } catch { return $null }
}

function Test-IsAdmin {
    $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $pr = New-Object System.Security.Principal.WindowsPrincipal($id)
    return $pr.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Test-PendingReboot {
    if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") { return $true }
    if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") { return $true }
    $v = Get-RegistryValueSafe "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" "PendingFileRenameOperations"
    return ($null -ne $v)
}

# Состояние компонента SMB1Protocol через DISM (Win8+). Возврат: строка State или $null
function Get-DismFeatureState {
    try {
        $out = & dism.exe /online /get-featureinfo /featurename:SMB1Protocol /format:list 2>$null
        foreach ($line in $out) {
            if ($line -match "^\s*State\s*:\s*(.+)$") { return $Matches[1].Trim() }
        }
    } catch {}
    return $null
}

# ============ OS INFO ============
$OS = Get-WmiObject Win32_OperatingSystem
$CS = Get-WmiObject Win32_ComputerSystem
$DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$OSCaption = $OS.Caption
$OSBuild = [int]$OS.BuildNumber
$IsWin7 = ($OS.Version -like "6.1*")
$IsWin8Plus = ([version]$OS.Version -ge [version]"6.2")

$Arch = $OS.OSArchitecture
if ([string]::IsNullOrEmpty($Arch)) { $Arch = $(if ([IntPtr]::Size -eq 8) {"64-bit"} else {"32-bit"}) }

# ============ SHARE AVAILABILITY ============
$ShareAvailable = Test-Path $ResultDirectory

# Догрузка локальных отчётов прошлых запусков
if ($ShareAvailable -and (Test-Path $LocalLogDirectory)) {
    Get-ChildItem $LocalLogDirectory -Filter "*.txt" -ErrorAction SilentlyContinue | ForEach-Object {
        try { Copy-Item $_.FullName (Join-Path $ResultDirectory $_.Name) -Force; Remove-Item $_.FullName -Force } catch {}
    }
}

# ============ MARKER ============
if ($ShareAvailable -and $env:SMB1_AUDIT_FORCE -ne "1") {
    $marker = Get-ChildItem $ResultDirectory -Filter ($ComputerName + "_*.txt") -ErrorAction SilentlyContinue |
              Sort-Object LastWriteTime -Descending | Select-Object -First 1
    if ($null -ne $marker) {
        $age = (New-TimeSpan -Start $marker.LastWriteTime -End (Get-Date)).Days
        if ($age -lt $MarkerExpireDays) {
            Write-Host "Audit already completed $($age)d ago: $($marker.FullName). Exit."
            exit 0
        }
        Remove-Item $marker.FullName -Force -ErrorAction SilentlyContinue
    }
}

# ============ DELAY ============
if ($env:SMB1_AUDIT_NODELAY -ne "1") {
    $Delay = (New-Object System.Random).Next($MinDelaySeconds, $MaxDelaySeconds)
    Write-Host "Waiting $Delay seconds..."
    Start-Sleep -Seconds $Delay
}

# ============ HEADER ============
[void]$Results.Add("=" * 60)
[void]$Results.Add("SMB v1 DISABLE AUDIT v2")
[void]$Results.Add("=" * 60)
[void]$Results.Add("Computer     : $ComputerName")
[void]$Results.Add("Domain       : $($CS.Domain)")
[void]$Results.Add("OS           : $OSCaption (build $OSBuild, $Arch)")
[void]$Results.Add("Date         : $DateTime")
[void]$Results.Add("Elevated     : $(Test-IsAdmin)")

# ============ SERVER SIDE ============
Add-Section "1. SERVER SIDE (входящие SMBv1)"

$ServerStatus = "UNKNOWN"

if ($IsWin8Plus) {
    # Авторитетный источник: Get-SmbServerConfiguration
    $usedCmdlet = $false
    try {
        $cfg = Get-SmbServerConfiguration -ErrorAction Stop
        $usedCmdlet = $true
        if ($cfg.EnableSMB1Protocol) {
            $ServerStatus = "FAIL"
            Add-Result FAIL "Server effective" "Get-SmbServerConfiguration: EnableSMB1Protocol = True. SMBv1-сервер АКТИВЕН."
        } else {
            $ServerStatus = "PASS"
            Add-Result PASS "Server effective" "Get-SmbServerConfiguration: EnableSMB1Protocol = False."
        }
    } catch {}

    if (-not $usedCmdlet) {
        $v = Get-RegistryValueSafe "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" "SMB1"
        if ($null -ne $v) {
            if ([int]$v -eq 0) { $ServerStatus = "PASS"; Add-Result PASS "Server registry" "LanmanServer\SMB1 = 0." }
            else { $ServerStatus = "FAIL"; Add-Result FAIL "Server registry" "LanmanServer\SMB1 = $v. SMBv1 включён." }
        } else {
            # Параметр отсутствует: на <1709 это ВКЛЮЧЕНО, на >=1709 — смотрим компонент
            if ($OSBuild -lt 16299) {
                $ServerStatus = "FAIL"
                Add-Result FAIL "Server registry" "Параметр SMB1 отсутствует, билд $OSBuild (< 1709): SMBv1-сервер включён по умолчанию. Задать SMB1 = 0."
            } else {
                $ServerStatus = "WARN"
                Add-Result WARN "Server registry" "Параметр SMB1 отсутствует. Состояние определяется компонентом (см. ниже)."
            }
        }
    }

    # Состояние компонента (установлен/вычищен)
    $dism = Get-DismFeatureState
    if ($null -ne $dism) {
        Add-Result PASS "Server feature" "Компонент SMB1Protocol: $dism" | Out-Null
        # переоформляем как инфо-строку
        $Results[$Results.Count - 1] = "[INFO] Server feature: компонент SMB1Protocol = $dism"
        $script:PassCount--
        if ($dism -match "^Enabled" -and $ServerStatus -eq "PASS") {
            $ServerStatus = "WARN"
            Add-Result WARN "Server feature" "Компонент SMB1Protocol = $dism: протокол выключен, но бинарники установлены. Рекомендуется Disable-WindowsOptionalFeature -FeatureName SMB1Protocol."
        }
    }
}
elseif ($IsWin7) {
    $v = Get-RegistryValueSafe "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" "SMB1"
    if ($null -eq $v) {
        $ServerStatus = "FAIL"
        Add-Result FAIL "Server registry" "Параметр SMB1 отсутствует: на Windows 7 это ВКЛЮЧЕНО. Задать SMB1 = 0 (DWORD)."
    } elseif ([int]$v -eq 0) {
        $ServerStatus = "PASS"
        Add-Result PASS "Server registry" "LanmanServer\SMB1 = 0. Серверная часть отключена."
    } else {
        $ServerStatus = "FAIL"
        Add-Result FAIL "Server registry" "LanmanServer\SMB1 = $v. SMBv1-сервер включён."
    }
}
else {
    $ServerStatus = "WARN"
    Add-Result WARN "Server" "ОС не поддерживается скриптом (ниже Win7): $($OS.Version). Проверьте вручную."
}

# ============ CLIENT SIDE ============
Add-Section "2. CLIENT SIDE (исходящие SMBv1)"

$ClientStatus = "UNKNOWN"
$drvPath = "HKLM:\SYSTEM\CurrentControlSet\Services\mrxsmb10"
$drvStart = Get-RegistryValueSafe $drvPath "Start"
$drvKeyExists = Test-Path $drvPath

# Фактическое состояние драйвера
$drvRunning = $null
try {
    $svc = Get-Service -Name "mrxsmb10" -ErrorAction Stop
    $drvRunning = ($svc.Status -eq "Running")
} catch { $drvRunning = $false }

if (-not $drvKeyExists) {
    $ClientStatus = "PASS"
    Add-Result PASS "Client driver" "Ключ mrxsmb10 отсутствует: компонент удалён, исходящие SMBv1 невозможны."
}
elseif ([int]$drvStart -eq 4) {
    if ($drvRunning) {
        $ClientStatus = "FAIL"
        Add-Result FAIL "Client driver" "Start = 4, но драйвер ЗАГРУЖЕН: отключение применится только после перезагрузки. SMBv1-клиент активен прямо сейчас."
    } else {
        $ClientStatus = "PASS"
        Add-Result PASS "Client driver" "Start = 4 (Disabled), драйвер не загружен."
    }
}
else {
    $desc = switch ([int]$drvStart) { 2 {"Automatic"} 3 {"Manual"} default {"Start=$drvStart"} }
    if ($drvRunning) {
        $ClientStatus = "FAIL"
        Add-Result FAIL "Client driver" "mrxsmb10: $desc и драйвер ЗАГРУЖЕН. Исходящие SMBv1 активны."
    } else {
        $ClientStatus = "WARN"
        Add-Result WARN "Client driver" "mrxsmb10: $desc (не Disabled), сейчас не загружен, но может стартовать. Требуется Start = 4."
    }
}

# DependOnService у LanmanWorkstation: MRxSmb10 там быть не должно
$dep = Get-RegistryValueSafe "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation" "DependOnService"
if ($null -ne $dep) {
    $depStr = ($dep -join ";")
    if (($dep | ForEach-Object { $_.ToString() }) -contains "MRxSmb10") {
        if ($ClientStatus -eq "PASS") { $ClientStatus = "WARN" }
        Add-Result WARN "Client dependency" "LanmanWorkstation DependOnService содержит MRxSmb10 ($depStr). При отключённом драйвере это ломает сетевой клиент. Убрать: sc.exe config lanmanworkstation depend= bowser/mrxsmb20/nsi"
    } else {
        Add-Result PASS "Client dependency" "DependOnService без MRxSmb10 ($depStr)."
    }
} else {
    Add-Result WARN "Client dependency" "Не удалось прочитать DependOnService у LanmanWorkstation."
}

# ============ PENDING REBOOT ============
if (Test-PendingReboot) {
    if ($ServerStatus -eq "PASS") { $ServerStatus = "WARN" }
    if ($ClientStatus -eq "PASS") { $ClientStatus = "WARN" }
    Add-Result WARN "Pending reboot" "Обнаружен ожидающий перезагрузки конфиг. Состояние SMBv1 может не соответствовать реестру до ребута."
}

# ============ PATCHES (только информационно, на итог не влияет) ============
Add-Section "3. INFO: PATCHES (не влияет на итог)"

if ($IsWin7) {
    try {
        $hf = Get-HotFix -ErrorAction Stop | Where-Object { $_.InstalledOn -ne $null } |
              Sort-Object InstalledOn -Descending | Select-Object -First 1
        if ($null -ne $hf) {
            $d = [datetime]$hf.InstalledOn
            if ($d -ge [datetime]"2020-01-14") {
                Add-Result WARN "Patches" "Win7 после конца поддержки: последний апдейт $($hf.HotFixID) от $d (ESU?)."
            } else {
                Add-Result WARN "Patches" "Последний апдейт $($hf.HotFixID) от $d. Для отключения SMBv1 патчи НЕ нужны (SMB1=0 работает и так), но см. MS17-010."
            }
        } else {
            Add-Result WARN "Patches" "Даты установки обновлений не читаются (часто бывает под неполными правами)."
        }
    } catch {
        Add-Result WARN "Patches" "Не удалось получить список обновлений."
    }
} else {
    [void]$Results.Add("[INFO] $OSCaption — проверка патчей вне области аудита SMBv1.")
}

# ============ FINAL ============
Add-Section "FINAL RESULT"
[void]$Results.Add("Server : $ServerStatus")
[void]$Results.Add("Client : $ClientStatus")

if ($ServerStatus -eq "FAIL" -or $ClientStatus -eq "FAIL" -or $FailCount -gt 0) {
    $OverallStatus = "FAIL"
    [void]$Results.Add("RESULT: FAIL — SMBv1 активен на клиенте и/или сервере.")
} elseif ($ServerStatus -eq "WARN" -or $ClientStatus -eq "WARN" -or $WarnCount -gt 0) {
    $OverallStatus = "PASS_WITH_WARNINGS"
    [void]$Results.Add("RESULT: PASS WITH WARNINGS — SMBv1 выключен, есть замечания (ребут/остатки конфигурации).")
} else {
    $OverallStatus = "PASS"
    [void]$Results.Add("RESULT: PASS — SMBv1 полностью отключён, фактическое состояние подтверждено.")
}

# ============ OUTPUT ============
$statusFile = $ComputerName + "_" + $OverallStatus + ".txt"
$saved = $false

if ($ShareAvailable) {
    try {
        $Results | Out-File (Join-Path $ResultDirectory $statusFile) -Encoding UTF8 -Force
        $saved = $true
        # строка в сводный CSV
        $csvLine = '"{0}";"{1}";"{2}";"{3}";"{4}";"{5}";"{6}"' -f `
            $ComputerName, $DateTime, $OSCaption, $OSBuild, $ServerStatus, $ClientStatus, $OverallStatus
        Add-Content -Path $SummaryCsv -Value $csvLine -Encoding UTF8
    } catch { $saved = $false }
}

if (-not $saved) {
    if (-not (Test-Path $LocalLogDirectory)) { New-Item $LocalLogDirectory -ItemType Directory -Force | Out-Null }
    $Results | Out-File (Join-Path $LocalLogDirectory $statusFile) -Encoding UTF8 -Force
}

Write-Host ""
$Results | ForEach-Object { Write-Host $_ }
Write-Host ""
Write-Host "Overall: $OverallStatus"