<# .SYNOPSIS GPER Win7 Prepare — установка .NET 4.8 и WMF 5.1 (PowerShell 5.1) .DESCRIPTION Загружает и устанавливает компоненты для GPER Installer на Windows 7. Совместим с PowerShell 2.0 (минимальная версия на Win7). ЗАПУСК (из cmd с правами администратора): powershell -Command "$wc=New-Object Net.WebClient;iex $wc.DownloadString('https://gper.ru/win7/win7.ps1')" Скрипт сохраняется в %APPDATA%\gper.ru\install\win7.ps1 и перезапускается с диска для работы Task Scheduler после перезагрузок. Этапы: Step 1a: KB3033929 (поддержка SHA-2) - перезагрузка при установке Step 1b: .NET Framework 4.8 - перезагрузка обязательна Step 2: WMF 5.1 / PS 5.1 - перезагрузка обязательна Готово: irm https://gper.ru/ps/bootstrap.ps1 | iex Авто-продолжение: скрипт регистрируется в Task Scheduler перед каждой перезагрузкой. Задача запускается ONLOGON с RunLevel=Highest (повышенные права, без UAC). После перезагрузки: пауза 10 секунд, затем автоматическое продолжение. Любая клавиша во время паузы = пропуск ожидания. .NOTES Version: 2.1.0 Compatibility: PowerShell 2.0+, Windows 7 SP1 x64/x86 Encoding: UTF-8 with BOM (кириллица корректно отображается в PS2) #> # =============================================================== # НАСТРОЙКА КОДИРОВКИ КОНСОЛИ (для русского текста в PS2) # =============================================================== # Сохраняем текущую кодировку и устанавливаем UTF-8 $originalCP = [Console]::OutputEncoding # ВНИМАНИЕ: НЕ меняем [Console]::OutputEncoding на Win7 — ломает буфер консоли (ошибка 0x1F) # PS2 читает файл корректно по BOM, PS3+ обрабатывает UTF-8 нативно # =============================================================== # SELF-BOOTSTRAP: если запущен через iex, сохраняем и перезапускаем # =============================================================== if (-not $MyInvocation.MyCommand.Path) { $bsDir = "$env:APPDATA\gper.ru\install" $bsFile = "$bsDir\win7.ps1" $bsUrl = 'https://gper.ru/win7/win7.ps1' if (-not (Test-Path $bsDir)) { New-Item -ItemType Directory -Path $bsDir -Force | Out-Null } $bsWc = New-Object System.Net.WebClient # DownloadFile - байтовое копирование, кодировка сохраняется $bsWc.DownloadFile($bsUrl, $bsFile) $bsPs = "$env:windir\system32\WindowsPowerShell\v1.0\powershell.exe" $bsArg = "-ExecutionPolicy Bypass -NoProfile -File `"$bsFile`"" Start-Process -FilePath $bsPs -ArgumentList $bsArg -Verb RunAs exit } # =============================================================== # ИДЕНТИФИКАТОРЫ И ПУТИ # =============================================================== $myPath = $MyInvocation.MyCommand.Path $gperDir = "$env:APPDATA\gper.ru\install" $stateFile = "$gperDir\win7-state.txt" $autorunName = 'GperWin7Prepare' $winlogonKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' $autoLoginFlag = "$gperDir\autologin.flag" $logFile = "$gperDir\win7.log" # =============================================================== # БАЗОВЫЕ URL # WIN7_HTTP — HTTP (работает на непропатченном Win7) # WIN7_GH — HTTPS GitHub fallback (только после установки SHA2+TLS) # =============================================================== $WIN7_HTTP = 'http://fps.gper.ru/win7/soft' $WIN7_GH = 'https://github.com/gper-ru/win7-soft/releases/latest/download' # =============================================================== # ХЭШИ ФАЙЛОВ ДЛЯ ПРОВЕРКИ ЦЕЛОСТНОСТИ (SHA256) # =============================================================== $fileHashes = @{ DotNet48 = '0A3A390C47E639D0F7FC65B21195FEE6B7F65B066F80F70C60FAB191D14B7E40' DotNet48Rus = '08CB9A3D8D71A32AEB09920CFB7B9083FC297DD13E11E136071DF0BF2D3B24D1' WMF51_x64 = 'C51ABFC65DFF5F6858C0B9F6420CAA0C166DF7A6D8B484F8A3C68EA99F0032B0' WMF51_x86 = '4575C47BEDF732C7A215A5DD9E184AC2F06F10C0F23FCEF38EEC96B0FF3BD121' SHA2_x64 = '5318587007EDB6C8B29310FF18DA479A162B486B9101A7DE735F94A70DBC3B31' SHA2_x86 = '246C300A6AE6DCA99453F6839745AC0015953528A7065BED1B015F91B80CF64D' } # Основные URL (HTTP — работают без патчей) $installerUrls = @{ DotNet48 = "$WIN7_HTTP/net-fw/NDP48-x86-x64-AllOS-ENU.exe" DotNet48Rus = "$WIN7_HTTP/net-fw/ndp48-x86-x64-allos-rus.exe" WMF51_x64 = "$WIN7_HTTP/updates/x64/KB3191566-x64.msu" WMF51_x86 = "$WIN7_HTTP/updates/x86/KB3191566-x86.msu" SHA2_x64 = "$WIN7_HTTP/updates/x64/KB3033929-x64.msu" SHA2_x86 = "$WIN7_HTTP/updates/x86/KB3033929-x86.msu" } # Fallback URL (GitHub HTTPS — только после установки SHA2+TLS, плоская структура) $installerUrlsFallback = @{ DotNet48 = "$WIN7_GH/NDP48-x86-x64-AllOS-ENU.exe" DotNet48Rus = "$WIN7_GH/ndp48-x86-x64-allos-rus.exe" WMF51_x64 = "$WIN7_GH/KB3191566-x64.msu" WMF51_x86 = "$WIN7_GH/KB3191566-x86.msu" SHA2_x64 = "$WIN7_GH/KB3033929-x64.msu" SHA2_x86 = "$WIN7_GH/KB3033929-x86.msu" } # Код ошибки CERT_E_UNTRUSTEDROOT = 0x800B0109 $EXIT_CERT_ERROR = -2146762487 $tmpDir = Join-Path $env:TEMP 'gper-w7' # =============================================================== # ФУНКЦИИ ВЫВОДА (PS2-совместимые, UTF-8) # =============================================================== function Write-Header { param([string]$m) Write-Host "=== $m ===" -ForegroundColor Cyan } function Write-Step { param([string]$m) Write-Host "--- $m" -ForegroundColor Cyan } function Write-Ok { param([string]$m) Write-Host "[OK] $m" -ForegroundColor Green } function Write-Warn { param([string]$m) Write-Host "[!] $m" -ForegroundColor Yellow } function Write-Err { param([string]$m) Write-Host "[X] $m" -ForegroundColor Red } function Write-Info { param([string]$m) Write-Host " $m" -ForegroundColor Gray } function Write-Success { param([string]$m) Write-Host "[+] $m" -ForegroundColor Green } # =============================================================== # СИСТЕМНЫЕ ПРОВЕРКИ # =============================================================== function Test-IsAdmin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() return (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator) } function Test-Is64bit { # [Environment]::Is64BitOperatingSystem требует .NET 4.0; PS2 использует .NET 2.0 CLR return ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') -or ($env:PROCESSOR_ARCHITEW6432 -eq 'AMD64') } function Test-IsServicePack1 { # Windows 7 SP1 обязателен для .NET 4.8 и WMF 5.1 try { $csd = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -EA Stop).CSDVersion return ($csd -eq 'Service Pack 1') } catch { return $false } } function Get-DotNetRelease { try { $p = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -EA SilentlyContinue if ($p -and $p.Release) { return [int]$p.Release } } catch {} return 0 } function Test-FileHash { param([string]$FilePath, [string]$ExpectedHash) if ([string]::IsNullOrEmpty($ExpectedHash)) { return $true # Если хэш не указан, пропускаем проверку } try { $sha256 = [System.Security.Cryptography.SHA256]::Create() $bytes = [System.IO.File]::ReadAllBytes($FilePath) $hash = [BitConverter]::ToString($sha256.ComputeHash($bytes)).Replace('-', '').ToLower() # Dispose() не существует в .NET 2.0 (PS2) — не вызываем if ($hash -eq $ExpectedHash.ToLower()) { return $true } else { Write-Warn "Хэш файла не совпадает!" Write-Info "Ожидается: $ExpectedHash" Write-Info "Получен: $hash" return $false } } catch { Write-Warn "Ошибка проверки хэша: $_" return $false } } # =============================================================== # ФАЙЛ СОСТОЯНИЯ - отслеживание прогресса между перезагрузками # =============================================================== function Get-State { # Get-Content -Raw требует PS3+; используем .NET метод для PS2 if (Test-Path $stateFile) { return [System.IO.File]::ReadAllText($stateFile, [System.Text.Encoding]::ASCII).Trim() } return $null } function Set-State { param([string]$Phase) $dir = Split-Path $stateFile -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } [System.IO.File]::WriteAllText($stateFile, $Phase, [System.Text.Encoding]::ASCII) } function Clear-State { Remove-Item $stateFile -EA SilentlyContinue } # =============================================================== # АВТОЗАПУСК - регистрация/удаление задачи в Task Scheduler # =============================================================== function Register-Autorun { if (-not $myPath) { Write-Warn 'Невозможно зарегистрировать автозапуск: путь к скрипту неизвестен' return } # Task Scheduler с RunLevel=Highest: запускается с повышенными правами после входа & "$env:windir\system32\schtasks.exe" /create /tn $autorunName ` /tr "powershell.exe -ExecutionPolicy Bypass -NoProfile -File `"$myPath`"" ` /sc ONLOGON /rl HIGHEST /f 2>&1 | Out-Null Write-Info "Задача зарегистрирована: скрипт продолжится после перезагрузки" } function Remove-Autorun { & "$env:windir\system32\schtasks.exe" /delete /tn $autorunName /f 2>&1 | Out-Null } # =============================================================== # АВТО-ВХОД - временная настройка для автоматических перезагрузок # =============================================================== function Test-UserHasNoPassword { # LogonUser с пустой строкой - успех означает, что аккаунт без пароля try { $sig = @' [DllImport("advapi32.dll", SetLastError=true)] public static extern bool LogonUser(string lpUsername, string lpDomain, string lpPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool CloseHandle(IntPtr hObject); '@ $api = Add-Type -MemberDefinition $sig -Name 'AuthCheck' -Namespace 'Gper' -PassThru -EA Stop $tok = [IntPtr]::Zero # LOGON32_LOGON_NETWORK = 3, LOGON32_PROVIDER_DEFAULT = 0 $ok = $api[0]::LogonUser($env:USERNAME, '.', '', 3, 0, [ref]$tok) if ($tok -ne [IntPtr]::Zero) { $null = $api[0]::CloseHandle($tok) } return $ok } catch { return $false # При ошибке предполагаем, что пароль есть } } function Read-Password { Write-Host ' Пароль (Enter = без пароля): ' -ForegroundColor Yellow -NoNewline $ss = Read-Host -AsSecureString if ($ss.Length -eq 0) { return '' } $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($ss) try { return [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } function Enable-AutoLogin { param([string]$Password) Set-ItemProperty -Path $winlogonKey -Name 'AutoAdminLogon' -Value '1' -Type String Set-ItemProperty -Path $winlogonKey -Name 'DefaultUserName' -Value $env:USERNAME -Type String Set-ItemProperty -Path $winlogonKey -Name 'DefaultDomainName' -Value $env:COMPUTERNAME -Type String Set-ItemProperty -Path $winlogonKey -Name 'DefaultPassword' -Value $Password -Type String [System.IO.File]::WriteAllText($autoLoginFlag, '1', [System.Text.Encoding]::ASCII) Write-Ok "Авто-вход настроен для: $env:USERNAME (будет отключён по завершении)" } function Disable-AutoLogin { if (-not (Test-Path $autoLoginFlag)) { return } # Мы это не включали - не трогаем Set-ItemProperty -Path $winlogonKey -Name 'AutoAdminLogon' -Value '0' -EA SilentlyContinue Remove-ItemProperty -Path $winlogonKey -Name 'DefaultPassword' -EA SilentlyContinue Remove-Item $autoLoginFlag -EA SilentlyContinue Write-Ok 'Авто-вход отключён - учётные данные удалены из реестра' } # =============================================================== # ПАУЗА С ОБРАТНЫМ ОТСЧЁТОМ # =============================================================== function Wait-Countdown { param([string]$Message, [int]$Seconds = 10) for ($i = $Seconds; $i -gt 0; $i--) { Write-Host "`r $Message $i сек... (любая клавиша = сейчас)" -ForegroundColor Yellow -NoNewline $deadline = [datetime]::Now.AddSeconds(1) while ([datetime]::Now -lt $deadline) { if ([Console]::KeyAvailable) { $null = [Console]::ReadKey($true) $i = 0 break } Start-Sleep -Milliseconds 100 } } Write-Host '' } # =============================================================== # ПЕРЕЗАГРУЗКА # =============================================================== function Request-Reboot { param([string]$Reason) Write-Host '' Write-Warn $Reason Write-Info 'Скрипт продолжится автоматически после перезагрузки.' Register-Autorun Write-Host '' Wait-Countdown -Message 'Перезагрузка через' -Seconds 15 Write-Ok 'Перезагрузка...' Restart-Computer -Force exit 0 } # =============================================================== # ПРОВЕРКА ДОСТУПНОСТИ URL (PS2-совместимо через .NET WebRequest) # =============================================================== function Test-UrlHead { param([string]$Url) try { $req = [System.Net.WebRequest]::Create($Url) $req.Method = 'HEAD' $req.Timeout = 8000 $resp = $req.GetResponse() $code = [int]$resp.StatusCode $resp.Close() return ($code -ge 200 -and $code -lt 400) } catch { return $false } } # =============================================================== # ЗАГРУЗКА ФАЙЛОВ (bitsadmin - PS2-совместимый, HTTP) # Параметр FallbackUrl — GitHub/альтернативный источник # =============================================================== function Save-File { param( [string]$Url, [string]$OutFile, [string]$ExpectedHash = '', [string]$FallbackUrl = '' ) $name = $Url.Split('/')[-1] Write-Info "Загрузка: $name" if (Test-Path $OutFile) { if (-not [string]::IsNullOrEmpty($ExpectedHash)) { if (Test-FileHash -FilePath $OutFile -ExpectedHash $ExpectedHash) { Write-Info "Файл уже существует, хэш совпадает — пропуск." return $true } else { Write-Warn "Файл существует, но хэш не совпадает — перезагрузка." Remove-Item $OutFile -Force } } else { Write-Info "Файл уже существует — пропуск." return $true } } $bits = "$env:windir\system32\bitsadmin.exe" if (-not (Test-Path $bits)) { Write-Err 'bitsadmin.exe не найден.' return $false } # Выбираем URL: проверяем основной, при недоступности — fallback $activeUrl = $Url Write-Info "Проверка: $Url" if (-not (Test-UrlHead -Url $Url)) { if ($FallbackUrl) { Write-Warn "Основной URL недоступен — fallback: $FallbackUrl" $activeUrl = $FallbackUrl } else { Write-Warn "Основной URL недоступен, fallback не задан — попытка загрузки всё равно." } } $jobName = "gper-$([datetime]::Now.ToString('HHmmss'))" $result = & $bits /transfer $jobName /priority foreground $activeUrl $OutFile 2>&1 if (-not (Test-Path $OutFile)) { Write-Err "Ошибка загрузки: $activeUrl" Write-Info "bitsadmin: $result" return $false } if (-not [string]::IsNullOrEmpty($ExpectedHash)) { if (-not (Test-FileHash -FilePath $OutFile -ExpectedHash $ExpectedHash)) { Write-Err 'Хэш загруженного файла не совпадает!' Remove-Item $OutFile -Force -EA SilentlyContinue return $false } } $sizeMB = [Math]::Round((Get-Item $OutFile).Length / 1MB, 1) Write-Ok "Загружено: $name ($sizeMB МБ)" return $true } # =============================================================== # ОСНОВНАЯ ЛОГИКА # =============================================================== # Создаём директорию и запускаем логирование if (-not (Test-Path $gperDir)) { New-Item -ItemType Directory -Path $gperDir -Force | Out-Null } # Transcript: только PS 3+. На PS 2.0 конфликтует с консолью cmd.exe (ошибка 0x1F) if ($PSVersionTable.PSVersion.Major -ge 3) { Start-Transcript -Path $logFile -Append -Force 2>$null } # Заголовок Write-Host '' Write-Host '============================================================' -ForegroundColor Cyan Write-Host ' GPER Win7 Prepare v2.0' -ForegroundColor Cyan Write-Host '============================================================' -ForegroundColor Cyan Write-Host " Лог: $logFile" -ForegroundColor DarkGray Write-Host '' # -- Проверка продолжения после перезагрузки -- $isResuming = Get-State if ($isResuming) { Write-Host ' [Продолжение после перезагрузки - этап: ' -NoNewline -ForegroundColor Cyan Write-Host $isResuming -NoNewline -ForegroundColor White Write-Host ']' -ForegroundColor Cyan Write-Host '' Remove-Autorun # Удаляем сразу, перерегистрируем только если нужна ещё одна перезагрузка Wait-Countdown -Message 'Продолжение установки через' -Seconds 10 Write-Host '' } # -- Проверка версии ОС -- $osv = [Environment]::OSVersion.Version if ($osv.Major -ne 6 -or $osv.Minor -ne 1) { Write-Warn "Скрипт разработан для Windows 7 (6.1). Ваша ОС: $($osv)" } # -- Проверка Service Pack 1 -- if (-not (Test-IsServicePack1)) { Write-Err 'Требуется Windows 7 Service Pack 1!' Write-Warn 'Установите SP1 и запустите скрипт повторно.' Write-Info 'Скачать: https://www.microsoft.com/ru-ru/download/details.aspx?id=5842' exit 1 } Write-Ok 'Windows 7 SP1 обнаружен' # -- Проверка прав администратора -- if (-not (Test-IsAdmin)) { Write-Err 'Требуются права администратора!' Write-Warn 'Запустите cmd от имени администратора и выполните:' Write-Host '' if ($myPath) { Write-Host " powershell -ExecutionPolicy Bypass -File `"$myPath`"" -ForegroundColor White } else { Write-Host ' powershell -ExecutionPolicy Bypass -File "%APPDATA%\gper.ru\install\win7.ps1"' -ForegroundColor White } Write-Host '' exit 1 } $is64 = Test-Is64bit $arch = if ($is64) { 'x64' } else { 'x86' } $psVersion = $PSVersionTable.PSVersion Write-Ok "Права администратора OK | PowerShell $psVersion | $arch | $env:COMPUTERNAME" # -- Настройка авто-входа -- if (-not (Test-Path $autoLoginFlag)) { Write-Host '' Write-Step 'Настройка авто-входа для автоматических перезагрузок...' if (Test-UserHasNoPassword) { Write-Info 'Пароль не обнаружен - авто-вход без учётных данных' Enable-AutoLogin -Password '' } else { Write-Info 'У аккаунта есть пароль. Введите его для автоматического входа после перезагрузок.' Write-Info 'Временно сохраняется в реестре (открытый текст) - удаляется по завершении.' $loginPwd = Read-Password Enable-AutoLogin -Password $loginPwd } } # -- Создаём временную директорию -- if (-not (Test-Path $tmpDir)) { New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null } # -- Текущее состояние -- $dotNetRelease = Get-DotNetRelease $psMajor = $PSVersionTable.PSVersion.Major $dotNet48OK = ($dotNetRelease -ge 528040) $ps51OK = ($psMajor -ge 5) Write-Host '' Write-Info ".NET: release=$dotNetRelease $(if ($dotNet48OK) { '[>= 4.8 OK]' } else { '[требуется 4.8]' })" Write-Info "PowerShell: $($PSVersionTable.PSVersion) $(if ($ps51OK) { '[>= 5.1 OK]' } else { '[требуется 5.1]' })" Write-Host '' # =============================================================== # ЭТАП 1: .NET Framework 4.8 # =============================================================== if ($dotNet48OK) { Write-Ok 'Этап 1: .NET Framework 4.8 - уже установлен' } else { # -- Этап 1a: KB3033929 (поддержка SHA-2) - нужен ДО .NET 4.8 -- $sha2Key = if ($is64) { 'SHA2_x64' } else { 'SHA2_x86' } $sha2Url = $installerUrls[$sha2Key] $sha2Fall = $installerUrlsFallback[$sha2Key] $sha2Hash = $fileHashes[$sha2Key] $sha2File = Join-Path $tmpDir $sha2Url.Split('/')[-1] Write-Step 'Этап 1a: Установка поддержки SHA-2 (KB3033929)...' Write-Info 'Требуется для проверки подписи .NET 4.8 на непропатченной Windows 7' $sha2Available = Save-File -Url $sha2Url -OutFile $sha2File -ExpectedHash $sha2Hash -FallbackUrl $sha2Fall if ($sha2Available) { Write-Host "Запущена установка $($sha2File)..." -ForegroundColor Cyan $p = Start-Process -FilePath "$env:windir\system32\wusa.exe" ` -ArgumentList "`"$sha2File`"", '/quiet', '/norestart' -Wait -PassThru # 0 = успех, 2359302 = уже установлен, 3010 = нужна перезагрузка if ($p.ExitCode -eq 0 -or $p.ExitCode -eq 2359302) { Write-Ok 'KB3033929 установлен (или уже был установлен)' } elseif ($p.ExitCode -eq 3010) { Write-Ok 'KB3033929 установлен' Set-State 'kb3033929' Request-Reboot 'Требуется перезагрузка после KB3033929. Скрипт продолжится автоматически.' } else { Write-Warn "KB3033929: код завершения $($p.ExitCode) - продолжаем выполнение" } } else { Write-Warn 'KB3033929 не найден на сервере - если установка .NET упадёт, добавьте файл' Write-Info 'Скачать: https://www.catalog.update.microsoft.com/Search.aspx?q=KB3033929' } # -- Этап 1b: .NET Framework 4.8 -- Write-Step 'Этап 1b: Установка .NET Framework 4.8...' $dotnetFile = Join-Path $tmpDir $installerUrls.DotNet48.Split('/')[-1] $downloadOK = Save-File -Url $installerUrls.DotNet48 -OutFile $dotnetFile -ExpectedHash $fileHashes.DotNet48 ` -FallbackUrl $installerUrlsFallback.DotNet48 if (-not $downloadOK) { Write-Err 'Не удалось загрузить .NET Framework 4.8' exit 1 } Write-Host "Запущена установка $($dotnetFile)..." -ForegroundColor Cyan $proc = Start-Process -FilePath $dotnetFile -ArgumentList '/q', '/norestart' -Wait -PassThru # 0 = успех, 3010 = нужна перезагрузка, 1641 = перезагрузка инициирована if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 3010 -or $proc.ExitCode -eq 1641) { Write-Ok ".NET 4.8 установлен (код завершения: $($proc.ExitCode))" Set-State 'dotnet48' Request-Reboot 'Требуется перезагрузка после установки .NET 4.8.' } elseif ($proc.ExitCode -eq $EXIT_CERT_ERROR) { Write-Err '.NET 4.8: ошибка CERT_E_UNTRUSTEDROOT (0x800B0109)' Write-Warn 'KB3033929 (поддержка SHA-2) не установлен или требуется перезагрузка.' Write-Info 'Попробуйте: перезагрузка и повторный запуск скрипта.' Write-Info 'Если проблема сохраняется, установите KB3033929 вручную из каталога Microsoft Update' exit 1 } else { Write-Err "Установщик .NET 4.8 завершился с ошибкой (код: $($proc.ExitCode))" Write-Info 'Подробные логи: %TEMP%\Microsoft .NET Framework 4.8 Setup*' exit 1 } } # =============================================================== # ЭТАП 1В: .NET 4.8 RUS Language Pack # =============================================================== Write-Step 'Этап 1в: Установка .NET 4.8 RUS Language Pack...' $dotnetRusFile = Join-Path $tmpDir $installerUrls.DotNet48Rus.Split('/')[-1] $downloadOK = Save-File -Url $installerUrls.DotNet48Rus -OutFile $dotnetRusFile ` -ExpectedHash $fileHashes.DotNet48Rus -FallbackUrl $installerUrlsFallback.DotNet48Rus if ($downloadOK) { Write-Host "Запущена установка $($dotnetRusFile)..." -ForegroundColor Cyan $proc = Start-Process -FilePath $dotnetRusFile -ArgumentList '/q', '/norestart' -Wait -PassThru # 0 = OK, 1642 = уже установлен if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 1642) { Write-Ok ".NET 4.8 RUS Language Pack установлен (код: $($proc.ExitCode))" } else { Write-Warn ".NET 4.8 RUS Language Pack: код завершения $($proc.ExitCode) - не критично, продолжаем" } } else { Write-Warn '.NET 4.8 RUS Language Pack не загружен — пропуск (не критично)' } # =============================================================== # ЭТАП 2: WMF 5.1 (PowerShell 5.1) # =============================================================== if ($ps51OK) { Write-Ok 'Этап 2: PowerShell 5.1 (WMF 5.1) - уже установлен' } else { # WMF 5.1 требует минимум .NET 4.5 if ($dotNetRelease -lt 378389) { Write-Err 'WMF 5.1 требует .NET Framework 4.5+. Запустите скрипт после перезагрузки .NET 4.8.' exit 1 } # WMF 5.1 несовместим с WMF 3.0 (KB2506143) $wmf3 = Get-WmiObject -Query "SELECT HotFixID FROM Win32_QuickFixEngineering WHERE HotFixID = 'KB2506143'" -EA SilentlyContinue if ($wmf3) { Write-Err 'WMF 3.0 (KB2506143) установлен - должен быть удалён перед WMF 5.1!' Write-Warn 'Удалите KB2506143 через Панель управления -> Программы -> Установленные обновления' exit 1 } Write-Step 'Этап 2: Установка WMF 5.1 (PowerShell 5.1)...' $wmfKey = if ($is64) { 'WMF51_x64' } else { 'WMF51_x86' } $wmfUrl = $installerUrls[$wmfKey] $wmfFall = $installerUrlsFallback[$wmfKey] $wmfHash = $fileHashes[$wmfKey] $wmfFile = Join-Path $tmpDir $wmfUrl.Split('/')[-1] $downloadOK = Save-File -Url $wmfUrl -OutFile $wmfFile -ExpectedHash $wmfHash -FallbackUrl $wmfFall if (-not $downloadOK) { Write-Err 'Не удалось загрузить WMF 5.1' exit 1 } Write-Host "Запущена установка $($wmfFile)..." -ForegroundColor Cyan $wusa = "$env:windir\system32\wusa.exe" $proc = Start-Process -FilePath $wusa -ArgumentList "`"$wmfFile`"", '/quiet', '/norestart' -Wait -PassThru # 0 = OK, 3010 = нужна перезагрузка, 2359302 = уже установлен if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 3010) { Write-Ok "WMF 5.1 установлен (код завершения: $($proc.ExitCode))" Set-State 'wmf51' Request-Reboot 'Требуется перезагрузка после установки WMF 5.1.' } elseif ($proc.ExitCode -eq 2359302) { Write-Ok 'WMF 5.1 уже был установлен' } else { Write-Err "Установщик WMF 5.1 завершился с ошибкой (код: $($proc.ExitCode))" Write-Warn 'Проверьте логи wusa в %TEMP%' exit 1 } } # =============================================================== # ЗАВЕРШЕНИЕ - очистка, настройка TLS, проверка # =============================================================== # Очистка Clear-State Remove-Autorun Disable-AutoLogin # -- .NET Strong Crypto: включает TLS 1.2 для PowerShell без перезагрузки -- Write-Step 'Включение .NET Strong Crypto (TLS 1.2 для irm, без перезагрузки)...' try { $cryptoKeys = @( 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319' ) foreach ($k in $cryptoKeys) { Set-ItemProperty -Path $k -Name 'SchUseStrongCrypto' -Value 1 -Type DWord -Force Set-ItemProperty -Path $k -Name 'SystemDefaultTlsVersions' -Value 1 -Type DWord -Force } Write-Ok '.NET Strong Crypto включён' } catch { Write-Warn "Ошибка настройки .NET Strong Crypto: $_" } # -- SCHANNEL TLS 1.2: системно (требует перезагрузки для полного действия) -- Write-Step 'Настройка TLS 1.2 в SCHANNEL (системно, полный эффект после перезагрузки)...' $schBase = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2' try { foreach ($sub in @('Client', 'Server')) { $path = "$schBase\$sub" if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null } Set-ItemProperty -Path $path -Name 'Enabled' -Value 1 -Type DWord -Force Set-ItemProperty -Path $path -Name 'DisabledByDefault' -Value 0 -Type DWord -Force } Write-Ok 'SCHANNEL TLS 1.2 настроен' } catch { Write-Warn "Ошибка настройки SCHANNEL TLS 1.2: $_" } # -- Тест HTTPS в новом процессе PowerShell -- Write-Step 'Проверка HTTPS в новом сеансе PowerShell...' $psExe = "$env:windir\system32\WindowsPowerShell\v1.0\powershell.exe" $testScript = @' try { (New-Object Net.WebClient).DownloadString('https://gper.ru/') | Out-Null } catch { $e = $_.Exception.Message if ($e -match 'SSL|TLS|secure channel|SecureChannel') { "SSL_FAIL: $e" } else { "HTTP_OK: $e" } } '@ $encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($testScript)) $result = & $psExe -NoProfile -NonInteractive -EncodedCommand $encoded 2>&1 if (-not $result) { Write-Ok 'Тест HTTPS: OK (gper.ru доступен через TLS 1.2)' } elseif ("$result" -match '^HTTP_OK') { Write-Ok 'Тест HTTPS: OK (TLS 1.2 работает, сервер вернул HTTP ошибку - ожидаемо)' } elseif ("$result" -match '^SSL_FAIL') { Write-Warn 'Тест HTTPS: сбой согласования TLS в новом сеансе.' Write-Warn 'Требуется перезагрузка для применения изменений SCHANNEL.' Write-Info 'После перезагрузки irm должен работать. Или установите вручную:' Write-Info ' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12' } else { Write-Warn "Результат теста HTTPS: $result" } # Очистка временных файлов Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue # 1. Принудительно включаем поддержку TLS 1.2 (иначе скачивание на Win7 не сработает) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # 2. Проверка статуса активации через старый WMI (LicenseStatus 1 = Licensed) $isLicensed = Get-WmiObject SoftwareLicensingProduct -Filter "PartialProductKey IS NOT NULL AND LicenseStatus = 1" if ($isLicensed) { Write-Host "[OK] Windows уже активирована" -ForegroundColor Green # Пауза для PS 2.0 # Write-Host "Press any key to exit..." # $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") # exit } else { # Создаем объект для скачивания $web = New-Object System.Net.WebClient $web.Encoding = [System.Text.Encoding]::UTF8 # 3. Запуск активатора try { # $activatorCode = $web.DownloadString("https://get.activated.win") Write-Host 'Загрузка активатора https://get.activated.win' -ForegroundColor Red $activatorCode = irm https://get.activated.win Write-Host 'Запуск скрипта активации Windows' -ForegroundColor Red & ([ScriptBlock]::Create($activatorCode)) /Z-Windows } catch { Write-Host "Ошибка загрузки активатора" -ForegroundColor Red } # 4. Настройка политики и запуск установщика Set-ExecutionPolicy Bypass -Force try { # $setupCode = $web.DownloadString("https://gper.ru") Write-Host 'Загрузка скрипта https://gper.ru/ps/gper-install.ps1' -ForegroundColor Red $setupCode = Invoke-RestMethod https://gper.ru/ps/gper-install.ps1 Write-Host 'Запуск скрипта gper-install.ps1' -ForegroundColor Red # Invoke-Expression $setupCode & ([ScriptBlock]::Create($setupCode)) } catch { Write-Host 'Ошибка загрузки https://gper.ru/ps/gper-install.ps1' -ForegroundColor Red } } # Итоговое сообщение Write-Host '' Write-Host '============================================================' -ForegroundColor Green Write-Host ' Подготовка завершена!' -ForegroundColor Green Write-Host '============================================================' -ForegroundColor Green Write-Host '' Write-Ok '.NET Framework 4.8 - установлен' Write-Ok 'PowerShell 5.1 - установлен' Write-Ok 'TLS 1.2 - настроен' Write-Host '' Write-Step 'Теперь запустите GPER Installer:' Write-Host '' Write-Host ' irm https://gper.ru/ps/bootstrap.ps1 | iex' -ForegroundColor White Write-Host '' if ($PSVersionTable.PSVersion.Major -ge 3) { Stop-Transcript 2>$null }