@echo off setlocal EnableExtensions DisableDelayedExpansion title OPS Windows Doctor set "WD_SELF=%~f0" set "WD_TEMP=%TEMP%\WindowsDoctor-%RANDOM%%RANDOM%.ps1" fltmc >nul 2>&1 if errorlevel 1 goto WD_NeedAdmin goto WD_AdminReady :WD_NeedAdmin echo Requesting administrator permission... powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '%~f0' -Verb RunAs" if errorlevel 1 goto WD_AdminError exit /b :WD_AdminError echo Administrator permission was not granted or PowerShell could not start. pause exit /b 1 :WD_AdminReady echo Windows Doctor Single File v1.0.7 echo Loading OPS Windows Doctor... rem The dot matches the underscore after #, keeping the payload marker out of this header. for /f "tokens=1 delims=:" %%A in ('findstr.exe /n /r /c:"^#._WD_PS_PAYLOAD__$" "%WD_SELF%"') do set "WD_MARK_LINE=%%A" if not defined WD_MARK_LINE goto WD_MarkerMissing rem more +N skips the first N lines, including the payload marker. powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$lines=[IO.File]::ReadAllLines($env:WD_SELF,[Text.Encoding]::UTF8);$payload=$lines[%WD_MARK_LINE%..($lines.Length-1)];[IO.File]::WriteAllLines($env:WD_TEMP,$payload,(New-Object Text.UTF8Encoding($true)))" if errorlevel 1 goto WD_ExtractError powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%WD_TEMP%" set "WD_EXIT=%errorlevel%" del /q "%WD_TEMP%" >nul 2>&1 if not "%WD_EXIT%"=="0" echo Windows Doctor stopped with exit code %WD_EXIT%. echo. pause exit /b %WD_EXIT% :WD_MarkerMissing echo PowerShell payload marker was not found. pause exit /b 2 :WD_ExtractError echo Failed to extract the PowerShell payload. pause exit /b 3 #__WD_PS_PAYLOAD__ #requires -version 4.0 # ============================================================ # OPS Windows Doctor v1.0 - Single File Edition # Windows 10 / 11 # Windows Server 2012 R2 / 2016 / 2019 / 2022 / 2025 # ============================================================ try { & cmd.exe /c "chcp 65001 >nul" | Out-Null } catch {} try { $Utf8Encoding = New-Object System.Text.UTF8Encoding($false) [Console]::OutputEncoding = $Utf8Encoding [Console]::InputEncoding = $Utf8Encoding $global:OutputEncoding = $Utf8Encoding } catch {} $Script:Version = "1.0" $Script:BaseDir = "C:\OPS-Windows-Doctor" $Script:BackupDir = Join-Path $Script:BaseDir "backup" $Script:BackupFile = Join-Path $Script:BackupDir "last-config.txt" $Script:LogDir = Join-Path $Script:BaseDir "logs" $Script:LogFile = Join-Path $Script:LogDir ("doctor-" + (Get-Date -Format "yyyy-MM-dd") + ".log") try { New-Item -Path $Script:BackupDir -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null New-Item -Path $Script:LogDir -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null } catch {} function Get-DoctorLanguage { $Detected = @() try { foreach ($i in (Get-WinUserLanguageList -ErrorAction Stop)) { if ($i.LanguageTag) { $Detected += $i.LanguageTag } } } catch {} try { $v = (Get-ItemProperty "HKCU:\Control Panel\International" -Name LocaleName -ErrorAction Stop).LocaleName if ($v) { $Detected += $v } } catch {} try { $v = (Get-Culture).Name if ($v) { $Detected += $v } } catch {} try { $v = (Get-UICulture).Name if ($v) { $Detected += $v } } catch {} try { $v = (Get-WinSystemLocale -ErrorAction Stop).Name if ($v) { $Detected += $v } } catch {} foreach ($v in @($Detected | Where-Object { $_ } | Select-Object -Unique)) { if ($v -match "^zh") { return "zh" } } foreach ($v in @($Detected | Where-Object { $_ } | Select-Object -Unique)) { if ($v -match "^en") { return "en" } } return "zh" } $Script:Language = Get-DoctorLanguage $Script:TextZH = @{ ToolName = "OPS Windows 系统诊断与修复工具" Supported = "Windows 10/11、Windows Server 2012 R2-2025" Menu1 = "一键检测并自动修复远程问题" Menu2 = "RDP 远程桌面专项检测与修复" Menu3 = "DNS 检测与修复" Menu4 = "网络协议栈检测与修复" Menu5 = "IP / 掩码 / 网关 / DNS 配置" Menu6 = "账户锁定检测与自动修复" Menu7 = "CC / 异常网络连接检测" Menu8 = "查询系统与网络基础信息" Menu0 = "退出" Select = "请选择" Query = "查询" Normal = "正常" Warning = "异常" Fixed = "修复" Error = "错误" Plan = "方案" PressEnter = "按 Enter 键返回" Invalid = "无效选项" ExitText = "工具已退出" } $Script:TextEN = @{ ToolName = "OPS Windows System Diagnostic and Repair Tool" Supported = "Windows 10/11, Windows Server 2012 R2-2025" Menu1 = "One-click remote diagnostics and repair" Menu2 = "RDP Remote Desktop diagnostics and repair" Menu3 = "DNS diagnostics and repair" Menu4 = "Network stack diagnostics and repair" Menu5 = "IP / subnet / gateway / DNS configuration" Menu6 = "Account lockout diagnostics and repair" Menu7 = "CC / abnormal network connection detection" Menu8 = "System and network information" Menu0 = "Exit" Select = "Select" Query = "INFO" Normal = "OK" Warning = "WARNING" Fixed = "FIXED" Error = "ERROR" Plan = "PLAN" PressEnter = "Press Enter to return" Invalid = "Invalid option" ExitText = "Tool exited" } if ($Script:Language -eq "zh") { $Script:T = $Script:TextZH } else { $Script:T = $Script:TextEN } function Get-DoctorText { param([string]$ZH,[string]$EN) if ($Script:Language -eq "zh") { return $ZH } return $EN } function Write-Info { param([string]$Message) Write-Host ("["+$Script:T.Query+"] "+$Message) -ForegroundColor Cyan } function Write-OK { param([string]$Message) Write-Host ("["+$Script:T.Normal+"] "+$Message) -ForegroundColor Green } function Write-Warn { param([string]$Message) Write-Host ("["+$Script:T.Warning+"] "+$Message) -ForegroundColor Yellow } function Write-Fix { param([string]$Message) Write-Host ("["+$Script:T.Fixed+"] "+$Message) -ForegroundColor Green } function Write-ErrorMessage { param([string]$Message) Write-Host ("["+$Script:T.Error+"] "+$Message) -ForegroundColor Red } function Write-Plan { param([string]$Message) Write-Host ("["+$Script:T.Plan+"] "+$Message) -ForegroundColor Yellow } function Write-Section { param([string]$Title) Write-Host ""; Write-Host ("==== "+$Title+" ====") -ForegroundColor Cyan } function Wait-Doctor { Write-Host ""; [void](Read-Host $Script:T.PressEnter) } function Write-Log { param([string]$Message) try { Add-Content -Path $Script:LogFile -Value ("["+(Get-Date -Format "yyyy-MM-dd HH:mm:ss")+"] "+$Message) -Encoding UTF8 } catch {} } function Test-Administrator { try { $id=[Security.Principal.WindowsIdentity]::GetCurrent() $p=New-Object Security.Principal.WindowsPrincipal($id) return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch { return $false } } function Show-Header { Clear-Host Write-Host "" Write-Host "============================================================" -ForegroundColor Green Write-Host (" OPS Windows Doctor v"+$Script:Version+" [Single File]") -ForegroundColor Green Write-Host (" "+$Script:T.ToolName) -ForegroundColor Green Write-Host (" "+$Script:T.Supported) -ForegroundColor Green Write-Host "============================================================" -ForegroundColor Green } function Test-IPv4Address { param([string]$Address) $ip=$null if ([System.Net.IPAddress]::TryParse($Address,[ref]$ip)) { return ($ip.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork) } return $false } function Test-SubnetMask { param([string]$Mask) if (-not (Test-IPv4Address $Mask)) { return $false } try { $binary="" foreach($part in $Mask.Split(".")) { $binary += [Convert]::ToString([int]$part,2).PadLeft(8,"0") } return ($binary -match "^1*0*$") } catch { return $false } } function Get-IPEnabledAdapters { try { return @(Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" -ErrorAction SilentlyContinue) } catch { return @() } } function Get-AdapterIPv4 { param($Adapter) if (-not $Adapter -or -not $Adapter.IPAddress) { return "" } return ($Adapter.IPAddress | Where-Object { $_ -match "^\d{1,3}(\.\d{1,3}){3}$" } | Select-Object -First 1) } function Get-AdapterDisplayName { param($Adapter) if (-not $Adapter) { return "" } try { $nic=Get-WmiObject Win32_NetworkAdapter -ErrorAction SilentlyContinue | Where-Object { $_.NetConnectionID -and ( $_.InterfaceIndex -eq $Adapter.InterfaceIndex -or $_.Index -eq $Adapter.Index ) } | Select-Object -First 1 if ($nic -and $nic.NetConnectionID) { return $nic.NetConnectionID } } catch {} return $Adapter.Description } function Get-PrimaryAdapter { try { $r=Get-WmiObject Win32_IP4RouteTable -Filter "Destination='0.0.0.0'" -ErrorAction SilentlyContinue | Sort-Object Metric1 | Select-Object -First 1 if ($r) { $a=Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" -ErrorAction SilentlyContinue | Where-Object { $_.InterfaceIndex -eq $r.InterfaceIndex } | Select-Object -First 1 if ($a) { return $a } } } catch {} return (Get-IPEnabledAdapters | Select-Object -First 1) } function Select-NetworkAdapter { $adapters=@(Get-IPEnabledAdapters) if ($adapters.Count -eq 0) { Write-ErrorMessage (Get-DoctorText "未找到启用 IPv4 的网卡" "No active IPv4 adapter was found") return $null } Write-Host "" foreach($a in $adapters) { Write-Host ("["+$a.InterfaceIndex+"] "+(Get-AdapterDisplayName $a)+" IPv4="+(Get-AdapterIPv4 $a)) } Write-Host "" $raw=Read-Host (Get-DoctorText "请输入网卡索引" "Enter adapter InterfaceIndex") $idx=0 if (-not [int]::TryParse($raw,[ref]$idx)) { Write-Warn (Get-DoctorText "网卡索引必须是数字" "InterfaceIndex must be numeric") return $null } $selected=$adapters | Where-Object { $_.InterfaceIndex -eq $idx } | Select-Object -First 1 if (-not $selected) { Write-Warn (Get-DoctorText "未找到指定网卡" "Specified adapter was not found") } return $selected } function Save-CurrentImportantConfig { param([string]$Reason) try { $lines=@() $lines += "OPS Windows Doctor - Last Configuration" $lines += "Time="+(Get-Date -Format "yyyy-MM-dd HH:mm:ss") $lines += "Reason="+$Reason $lines += "" try { $deny=(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -ErrorAction Stop).fDenyTSConnections $port=(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name PortNumber -ErrorAction Stop).PortNumber $lines += "RDP.Enabled="+($deny -eq 0) $lines += "RDP.Port="+$port } catch {} $lines += "" $lines += "NETWORK:" foreach($a in (Get-IPEnabledAdapters)) { $lines += "--------------------------------" $lines += "InterfaceIndex="+$a.InterfaceIndex $lines += "Description="+$a.Description $lines += "MAC="+$a.MACAddress $lines += "DHCPEnabled="+$a.DHCPEnabled $lines += "IP="+($a.IPAddress -join ",") $lines += "Mask="+($a.IPSubnet -join ",") $lines += "Gateway="+($a.DefaultIPGateway -join ",") $lines += "DNS="+($a.DNSServerSearchOrder -join ",") } Set-Content -Path $Script:BackupFile -Value $lines -Encoding UTF8 Write-Info (Get-DoctorText ("原配置已记录到 "+$Script:BackupFile) ("Original configuration saved to "+$Script:BackupFile)) } catch { Write-Warn (Get-DoctorText "配置记录失败,但未中止操作" "Configuration snapshot failed; operation was not stopped") } } # ============================================================ # RDP # ============================================================ function Get-RDPPort { try { return [int](Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name PortNumber -ErrorAction Stop).PortNumber } catch { Write-Log ("Failed to read RDP port: "+$_.Exception.Message) return 0 } } function Test-RDPEnabled { try { return ((Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -ErrorAction Stop).fDenyTSConnections -eq 0) } catch { return $false } } function Test-RDPPortListening { param([int]$Port) if ($Port -lt 1 -or $Port -gt 65535) { return $false } try { foreach($line in (netstat -ano -p tcp 2>$null)) { if ($line -match "LISTENING" -and $line -match "[:\]]$Port\s+") { return $true } } } catch {} return $false } function Get-PortOwner { param([int]$Port) try { foreach($line in (netstat -ano -p tcp 2>$null)) { if ($line -match "LISTENING" -and $line -match "[:\]]$Port\s+(\d+)\s*$") { $pidValue=[int]$Matches[1] $name="" try { $name=(Get-Process -Id $pidValue -ErrorAction Stop).ProcessName } catch {} return [PSCustomObject]@{PID=$pidValue;Process=$name} } } } catch {} return $null } function Test-CurrentSessionIsRDP { if ($env:SESSIONNAME -match "^RDP") { return $true } try { if ((query session $env:USERNAME 2>$null | Out-String) -match "rdp-tcp") { return $true } } catch {} return $false } function Enable-RDP { try { Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0 -ErrorAction Stop Write-Fix (Get-DoctorText "已开启 Windows 远程桌面" "Windows Remote Desktop has been enabled") return $true } catch { Write-ErrorMessage (Get-DoctorText "开启远程桌面失败" "Failed to enable Remote Desktop") return $false } } function Enable-RDPListener { try { $path="HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" $v=(Get-ItemProperty $path -Name fEnableWinStation -ErrorAction SilentlyContinue).fEnableWinStation if ($null -ne $v -and $v -eq 0) { Set-ItemProperty $path -Name fEnableWinStation -Value 1 -ErrorAction Stop Write-Fix (Get-DoctorText "已启用 RDP-Tcp Listener" "RDP-Tcp listener has been enabled") } return $true } catch { Write-Warn (Get-DoctorText "无法修改 RDP Listener 状态" "Unable to modify RDP listener state") return $false } } function Repair-TermService { try { $svc=Get-WmiObject Win32_Service -Filter "Name='TermService'" -ErrorAction Stop if (-not $svc) { Write-ErrorMessage (Get-DoctorText "未检测到 TermService" "TermService was not found") return $false } if ($svc.StartMode -eq "Disabled") { Write-Warn (Get-DoctorText "TermService 当前被禁用" "TermService is disabled") & sc.exe config TermService start= demand >$null 2>&1 Write-Fix (Get-DoctorText "已恢复 TermService 启动类型" "TermService startup mode has been restored") } $cur=Get-Service TermService -ErrorAction SilentlyContinue if ($cur -and $cur.Status -ne "Running") { Write-Warn (Get-DoctorText "Remote Desktop Services 未运行" "Remote Desktop Services is not running") Start-Service TermService -ErrorAction Stop Start-Sleep -Seconds 2 Write-Fix (Get-DoctorText "Remote Desktop Services 已启动" "Remote Desktop Services has been started") } else { Write-OK (Get-DoctorText "Remote Desktop Services 正在运行" "Remote Desktop Services is running") } return $true } catch { Write-ErrorMessage (Get-DoctorText "TermService 修复失败" "TermService repair failed") Write-Log $_.Exception.Message return $false } } function Ensure-RDPFirewall { param([int]$Port) if ($Port -lt 1 -or $Port -gt 65535) { return $false } $rule="OPS-Windows-Doctor-RDP-"+$Port try { netsh advfirewall firewall delete rule name="$rule" >$null 2>&1 netsh advfirewall firewall add rule name="$rule" dir=in action=allow protocol=TCP localport=$Port profile=any enable=yes >$null 2>&1 if ($LASTEXITCODE -eq 0) { Write-Fix (Get-DoctorText ("Windows 防火墙已放行 TCP/"+$Port) ("Windows Firewall now allows TCP/"+$Port)) return $true } } catch {} Write-ErrorMessage (Get-DoctorText ("防火墙放行 TCP/"+$Port+" 失败") ("Failed to allow TCP/"+$Port)) return $false } function Restart-RDPService { param([switch]$ForceCurrentRDP) if ((Test-CurrentSessionIsRDP) -and (-not $ForceCurrentRDP)) { Write-Warn (Get-DoctorText "当前正在使用 RDP,会话保护生效,暂不自动重启 TermService" "Current session is RDP; TermService restart was skipped") return $false } try { Restart-Service TermService -Force -ErrorAction Stop Start-Sleep -Seconds 4 Write-Fix (Get-DoctorText "TermService 已重新启动" "TermService has been restarted") return $true } catch { Write-ErrorMessage (Get-DoctorText "TermService 重启失败" "TermService restart failed") return $false } } function Invoke-RDPRepairCore { Write-Info (Get-DoctorText "检查远程桌面开关" "Checking Remote Desktop state") if (Test-RDPEnabled) { Write-OK (Get-DoctorText "远程桌面已开启" "Remote Desktop is enabled") } else { Write-Warn (Get-DoctorText "远程桌面未开启" "Remote Desktop is disabled") Enable-RDP | Out-Null } Write-Info (Get-DoctorText "检查 Remote Desktop Services" "Checking Remote Desktop Services") Repair-TermService | Out-Null Enable-RDPListener | Out-Null $port=Get-RDPPort if ($port -lt 1 -or $port -gt 65535) { Write-ErrorMessage (Get-DoctorText "无法读取有效的 RDP 端口,请进入 RDP 专项手动设置端口" "No valid RDP port was detected; configure it in the RDP menu") return $false } Write-Info (Get-DoctorText ("当前 RDP 端口: "+$port) ("Current RDP port: "+$port)) Ensure-RDPFirewall -Port $port | Out-Null if (Test-RDPPortListening -Port $port) { Write-OK (Get-DoctorText ("RDP TCP/"+$port+" 正在监听") ("RDP TCP/"+$port+" is listening")) return $true } Write-Warn (Get-DoctorText ("RDP TCP/"+$port+" 未监听") ("RDP TCP/"+$port+" is not listening")) Restart-RDPService | Out-Null if (Test-RDPPortListening -Port $port) { Write-Fix (Get-DoctorText ("RDP TCP/"+$port+" 已恢复监听") ("RDP TCP/"+$port+" is listening again")) return $true } Write-ErrorMessage (Get-DoctorText "RDP 仍未恢复,请进入 RDP 专项进一步处理" "RDP is still unavailable; use the RDP advanced menu") return $false } function Set-CustomRDPPort { Show-Header Write-Section (Get-DoctorText "修改 RDP 端口" "Change RDP Port") $current=Get-RDPPort Write-Info (Get-DoctorText ("当前 RDP 端口: "+$current) ("Current RDP port: "+$current)) $raw=Read-Host (Get-DoctorText "请输入新的 RDP 端口 (1-65535)" "Enter new RDP port (1-65535)") $new=0 if (-not [int]::TryParse($raw,[ref]$new)) { Write-Warn (Get-DoctorText "端口必须是数字" "Port must be numeric") Wait-Doctor return } if ($new -lt 1 -or $new -gt 65535) { Write-Warn (Get-DoctorText "端口范围必须为 1-65535" "Port must be between 1 and 65535") Wait-Doctor return } if ($new -eq $current) { Write-Info (Get-DoctorText "新端口与当前端口相同,无需修改" "The new port is already configured") Wait-Doctor return } $owner=Get-PortOwner -Port $new if ($owner) { Write-ErrorMessage (Get-DoctorText ("TCP/"+$new+" 已被占用") ("TCP/"+$new+" is already in use")) Write-Info ("PID="+$owner.PID+" Process="+$owner.Process) Wait-Doctor return } Write-Warn (Get-DoctorText ("即将把 RDP 端口从 "+$current+" 修改为 "+$new) ("RDP port will change from "+$current+" to "+$new)) $confirm=Read-Host (Get-DoctorText "输入 Y 确认修改" "Enter Y to confirm") if ($confirm -notmatch "^[Yy]$") { return } Save-CurrentImportantConfig -Reason "Change RDP port" if (-not (Ensure-RDPFirewall -Port $new)) { Write-ErrorMessage (Get-DoctorText "新端口防火墙放行失败,已取消端口修改" "Firewall preparation failed; port change cancelled") Wait-Doctor return } try { Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name PortNumber -Value $new -ErrorAction Stop Write-Fix (Get-DoctorText ("RDP 配置端口已修改为 "+$new) ("RDP port changed to "+$new)) } catch { Write-ErrorMessage (Get-DoctorText "RDP端口修改失败" "Failed to change RDP port") Wait-Doctor return } if (Test-CurrentSessionIsRDP) { Write-Warn (Get-DoctorText "当前正在通过 RDP 操作,重启 TermService 会中断当前连接" "Current session is RDP. Restarting TermService will disconnect this session") Write-Host "" Write-Host (Get-DoctorText "1) 立即重启 RDP 服务并应用新端口" "1) Restart RDP service now") Write-Host (Get-DoctorText "2) 暂不重启,稍后手动重启服务或服务器" "2) Apply later by restarting the service/server") $choice=Read-Host $Script:T.Select if ($choice -eq "1") { Restart-RDPService -ForceCurrentRDP | Out-Null } else { Write-Plan (Get-DoctorText ("稍后重启 TermService 或服务器后使用端口 "+$new) ("Restart TermService/server later and connect to port "+$new)) } } else { Restart-RDPService | Out-Null } Start-Sleep -Seconds 1 if (Test-RDPPortListening -Port $new) { Write-OK (Get-DoctorText ("新 RDP 端口 TCP/"+$new+" 已监听") ("New RDP port TCP/"+$new+" is listening")) } else { Write-Plan (Get-DoctorText "新端口尚未监听,可能需要重启 TermService 或服务器" "The new port is not listening yet; restart TermService or Windows") } Wait-Doctor } function Show-RDPDetails { Show-Header Write-Section (Get-DoctorText "RDP 详细状态" "RDP Detailed Status") $port=Get-RDPPort Write-Info (Get-DoctorText ("RDP 开启: "+(Test-RDPEnabled)) ("RDP enabled: "+(Test-RDPEnabled))) Write-Info (Get-DoctorText ("RDP 端口: "+$port) ("RDP port: "+$port)) Write-Info (Get-DoctorText ("端口监听: "+(Test-RDPPortListening -Port $port)) ("Port listening: "+(Test-RDPPortListening -Port $port))) try { $svc=Get-WmiObject Win32_Service -Filter "Name='TermService'" -ErrorAction Stop Write-Info ("TermService State="+$svc.State+" StartMode="+$svc.StartMode) } catch {} try { $p=Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" Write-Info ("UserAuthentication(NLA)="+$p.UserAuthentication) Write-Info ("SecurityLayer="+$p.SecurityLayer) Write-Info ("MinEncryptionLevel="+$p.MinEncryptionLevel) } catch {} Wait-Doctor } function Show-RDPLoginAudit { Show-Header Write-Section (Get-DoctorText "RDP 登录审计" "RDP Login Audit") $daysText=Read-Host (Get-DoctorText "查询最近多少天?直接回车默认为 7 天" "How many days to query? Press Enter for 7 days") $days=7 if ($daysText) { if (-not [int]::TryParse($daysText,[ref]$days) -or $days -lt 1 -or $days -gt 90) { Write-Warn (Get-DoctorText "天数必须是 1 到 90 之间的整数,已使用 7 天" "Days must be an integer from 1 to 90; using 7 days") $days=7 } } $startTime=(Get-Date).AddDays(-$days) Write-Info (Get-DoctorText ("查询范围: 最近 "+$days+" 天") ("Query range: last "+$days+" days")) $records=@() $securityCount=0 $rdpOperationalCount=0 try { $events=@(Get-WinEvent -FilterHashtable @{ LogName='Security'; Id=4624,4625; StartTime=$startTime } -MaxEvents 5000 -ErrorAction Stop) $securityCount=$events.Count } catch { Write-Warn (Get-DoctorText "无法读取 Windows 安全日志,将继续查询 RDP 连接日志" "Unable to read the Windows Security log; continuing with RDP connection logs") $events=@() } foreach($event in $events) { try { [xml]$xml=$event.ToXml() $data=@{} foreach($item in $xml.Event.EventData.Data) { if ($item.Name) { $data[$item.Name]=[string]$item.'#text' } } if ($data['LogonType'] -ne '10') { continue } $user=$data['TargetUserName'] if (-not $user) { $user="-" } if ($data['TargetDomainName'] -and $data['TargetDomainName'] -ne '-') { $user=$data['TargetDomainName']+'\'+$user } $ip=$data['IpAddress'] if (-not $ip -or $ip -eq '-') { $ip=(Get-DoctorText "本机或未知" "Local or unknown") } $workstation=$data['WorkstationName'] if (-not $workstation -or $workstation -eq '-') { $workstation="-" } $result=Get-DoctorText "成功" "Success" if ($event.Id -eq 4625) { $result=Get-DoctorText "失败" "Failed" } $records += New-Object PSObject -Property @{ Time=$event.TimeCreated Result=$result User=$user SourceIP=$ip Workstation=$workstation Source="Security" } } catch {} } try { $rdpEvents=@(Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'; Id=1149; StartTime=$startTime } -MaxEvents 2000 -ErrorAction Stop) $rdpOperationalCount=$rdpEvents.Count foreach($event in $rdpEvents) { try { [xml]$xml=$event.ToXml() $values=@($xml.Event.EventData.Data | ForEach-Object { [string]$_.'#text' }) $user="-" $ip=(Get-DoctorText "本机或未知" "Local or unknown") if ($values.Count -ge 1 -and $values[0]) { $user=$values[0] } if ($values.Count -ge 2 -and $values[1] -and $values[1] -ne '-') { $user=$values[1]+'\'+$user } if ($values.Count -ge 3 -and $values[2] -and $values[2] -ne '-') { $ip=$values[2] } $records += New-Object PSObject -Property @{ Time=$event.TimeCreated Result=(Get-DoctorText "成功" "Success") User=$user SourceIP=$ip Workstation="-" Source="RDP Operational" } } catch {} } } catch { Write-Warn (Get-DoctorText "未找到或无法读取 RDP 连接日志" "RDP connection log is unavailable or could not be read") } Write-Info (Get-DoctorText ("安全日志: "+$securityCount+" 条,RDP连接日志: "+$rdpOperationalCount+" 条") ("Security events: "+$securityCount+", RDP connection events: "+$rdpOperationalCount)) if ($records.Count -eq 0) { Write-Warn (Get-DoctorText "未找到 RDP 登录记录。可能近期没有登录,或服务器未保留相应日志" "No RDP login records were found. There may have been no recent logins, or the server may not retain the required logs") Wait-Doctor return } Write-Section (Get-DoctorText "最近 RDP 登录记录" "Recent RDP Login Records") $records | Sort-Object Time -Descending | Select-Object -First 50 Time,Result,User,SourceIP,Workstation,Source | Format-Table -AutoSize $successCount=@($records | Where-Object { $_.Result -eq (Get-DoctorText "成功" "Success") }).Count $failedCount=@($records | Where-Object { $_.Result -eq (Get-DoctorText "失败" "Failed") }).Count Write-Info (Get-DoctorText ("成功: "+$successCount+",失败: "+$failedCount) ("Success: "+$successCount+", Failed: "+$failedCount)) Write-Plan (Get-DoctorText "审计读取安全日志和 RDP 连接日志;不会修改安全日志或审核策略" "Audit reads Security and RDP connection logs; this tool does not modify logs or auditing policies") Wait-Doctor } function Show-RDPMenu { while($true) { Show-Header Write-Section (Get-DoctorText "RDP 远程桌面专项" "RDP Remote Desktop") Write-Host (Get-DoctorText "1) 自动检测并修复 RDP" "1) Automatic RDP diagnostics and repair") Write-Host (Get-DoctorText "2) 查看 RDP 详细配置" "2) View RDP details") Write-Host (Get-DoctorText "3) 修改 RDP 端口" "3) Change RDP port") Write-Host (Get-DoctorText "4) 检查并放行当前 RDP 防火墙端口" "4) Repair RDP firewall rule") Write-Host (Get-DoctorText "5) 重启 RDP 服务" "5) Restart RDP service") Write-Host (Get-DoctorText "6) RDP 登录审计" "6) RDP login audit") Write-Host "0) $($Script:T.Menu0)" Write-Host "" $choice=Read-Host $Script:T.Select switch($choice) { "1" { Show-Header; Write-Section $Script:T.Menu2; Invoke-RDPRepairCore | Out-Null; Wait-Doctor } "2" { Show-RDPDetails } "3" { Set-CustomRDPPort } "4" { Show-Header; $port=Get-RDPPort; Ensure-RDPFirewall -Port $port | Out-Null; Wait-Doctor } "5" { Show-Header if (Test-CurrentSessionIsRDP) { Write-Warn (Get-DoctorText "当前为 RDP 会话,重启服务会断开连接" "Current session is RDP and will be disconnected") $a=Read-Host (Get-DoctorText "输入 Y 确认重启" "Enter Y to confirm") if ($a -match "^[Yy]$") { Restart-RDPService -ForceCurrentRDP | Out-Null } } else { Restart-RDPService | Out-Null } Wait-Doctor } "6" { Show-RDPLoginAudit } "0" { return } default { Write-Warn $Script:T.Invalid; Start-Sleep -Seconds 1 } } } } # ============================================================ # DNS / Connectivity # ============================================================ function Test-InternetTCP { param([string]$HostAddress="1.1.1.1",[int]$Port=443,[int]$Timeout=3000) $client=New-Object System.Net.Sockets.TcpClient try { $ar=$client.BeginConnect($HostAddress,$Port,$null,$null) if (-not $ar.AsyncWaitHandle.WaitOne($Timeout)) { $client.Close() return $false } $client.EndConnect($ar) $client.Close() return $true } catch { try { $client.Close() } catch {} return $false } } function Test-DNSResolution { try { $r=[System.Net.Dns]::GetHostAddresses("www.microsoft.com") return ($r.Count -gt 0) } catch { return $false } } function Invoke-DNSAutoRepair { Write-Info (Get-DoctorText "测试公网基础连通性" "Testing basic Internet connectivity") if (Test-InternetTCP) { Write-OK (Get-DoctorText "公网 TCP 连通正常" "Internet TCP connectivity is available") } else { Write-Warn (Get-DoctorText "公网 TCP 测试失败,当前问题不一定是 DNS" "Internet TCP test failed; the issue may not be DNS") } Write-Info (Get-DoctorText "测试 DNS 解析" "Testing DNS resolution") if (Test-DNSResolution) { Write-OK (Get-DoctorText "DNS 解析正常" "DNS resolution is working") return $true } Write-Warn (Get-DoctorText "DNS 解析失败" "DNS resolution failed") ipconfig /flushdns >$null 2>&1 Write-Fix (Get-DoctorText "已刷新 DNS 缓存" "DNS cache has been flushed") try { Restart-Service Dnscache -Force -ErrorAction Stop Write-Fix (Get-DoctorText "DNS Client 服务已重新启动" "DNS Client service has been restarted") } catch { Write-Info (Get-DoctorText "DNS Client 服务无法重启,继续进行解析复检" "DNS Client could not be restarted; continuing the test") } Start-Sleep -Seconds 1 if (Test-DNSResolution) { Write-Fix (Get-DoctorText "DNS 解析已恢复" "DNS resolution has recovered") return $true } Write-Warn (Get-DoctorText "DNS 仍无法解析,请进入 DNS 专项设置 DNS 服务器" "DNS is still failing; configure DNS servers in the DNS menu") return $false } function Set-AdapterDNS { param($Adapter) if (-not $Adapter) { return } $dns1=Read-Host (Get-DoctorText "请输入首选 DNS" "Enter primary DNS") if (-not (Test-IPv4Address $dns1)) { Write-Warn (Get-DoctorText "DNS地址无效" "Invalid DNS address") return } $dns2=Read-Host (Get-DoctorText "请输入备用 DNS,可直接回车跳过" "Enter secondary DNS or press Enter to skip") if ($dns2 -and (-not (Test-IPv4Address $dns2))) { Write-Warn (Get-DoctorText "备用DNS地址无效" "Invalid secondary DNS address") return } Save-CurrentImportantConfig -Reason "Change DNS" $servers=@($dns1) if ($dns2) { $servers += $dns2 } try { $r=$Adapter.SetDNSServerSearchOrder($servers) if ($r.ReturnValue -eq 0 -or $r.ReturnValue -eq 1) { Write-Fix (Get-DoctorText ("DNS 已设置为 "+($servers -join ", ")) ("DNS changed to "+($servers -join ", "))) } else { Write-ErrorMessage (Get-DoctorText ("DNS修改失败,返回代码: "+$r.ReturnValue) ("DNS update failed. Return code: "+$r.ReturnValue)) } } catch { Write-ErrorMessage (Get-DoctorText "DNS设置失败" "DNS configuration failed") } } function Show-DNSMenu { while($true) { Show-Header Write-Section $Script:T.Menu3 $p=Get-PrimaryAdapter if ($p) { $name=Get-AdapterDisplayName $p Write-Info (Get-DoctorText ("当前网卡: "+$name) ("Current adapter: "+$name)) Write-Info ("DNS: "+($p.DNSServerSearchOrder -join ", ")) } Write-Host "" Write-Host (Get-DoctorText "1) DNS检测并自动修复" "1) DNS diagnostics and automatic repair") Write-Host (Get-DoctorText "2) 手动设置DNS" "2) Manually configure DNS") Write-Host (Get-DoctorText "3) DNS改为自动获取(适用于DHCP网卡)" "3) Set DNS to automatic (DHCP adapter)") Write-Host "0) $($Script:T.Menu0)" Write-Host "" $choice=Read-Host $Script:T.Select switch($choice) { "1" { Show-Header; Invoke-DNSAutoRepair | Out-Null; Wait-Doctor } "2" { $a=Select-NetworkAdapter; if($a){ Set-AdapterDNS $a; Wait-Doctor } } "3" { $a=Select-NetworkAdapter if($a) { Save-CurrentImportantConfig -Reason "DNS automatic" try { $r=$a.SetDNSServerSearchOrder($null) if ($r.ReturnValue -eq 0 -or $r.ReturnValue -eq 1) { Write-Fix (Get-DoctorText "DNS已恢复自动获取" "DNS has been set to automatic") } else { Write-ErrorMessage ("ReturnCode="+$r.ReturnValue) } } catch { Write-ErrorMessage (Get-DoctorText "DNS自动获取设置失败" "Failed to set automatic DNS") } Wait-Doctor } } "0" { return } default { Write-Warn $Script:T.Invalid; Start-Sleep -Seconds 1 } } } } # ============================================================ # Network stack # ============================================================ function Repair-NetworkStack { Show-Header Write-Section $Script:T.Menu4 Write-Warn (Get-DoctorText "该操作会重置 Winsock 和 TCP/IP,可能需要重启系统后完全生效" "This operation resets Winsock and TCP/IP and may require a reboot") $confirm=Read-Host (Get-DoctorText "输入 Y 确认执行" "Enter Y to confirm") if ($confirm -notmatch "^[Yy]$") { return } Save-CurrentImportantConfig -Reason "Network stack reset" Write-Info "netsh winsock reset" netsh winsock reset Write-Info "netsh int ip reset" netsh int ip reset Write-Info "ipconfig /flushdns" ipconfig /flushdns Write-Fix (Get-DoctorText "网络协议栈重置完成" "Network stack reset completed") Write-Plan (Get-DoctorText "建议完成当前工作后重启 Windows" "Restart Windows after completing current work") Wait-Doctor } # ============================================================ # IP configuration # ============================================================ function Show-AdapterConfiguration { param($Adapter) if (-not $Adapter) { return } Write-Host "" Write-Info ("InterfaceIndex: "+$Adapter.InterfaceIndex) $name=Get-AdapterDisplayName $Adapter Write-Info (Get-DoctorText ("网卡: "+$name) ("Adapter: "+$name)) Write-Info ("MAC: "+$Adapter.MACAddress) Write-Info ("DHCP: "+$Adapter.DHCPEnabled) Write-Info ("IP: "+($Adapter.IPAddress -join ", ")) Write-Info (Get-DoctorText ("掩码: "+($Adapter.IPSubnet -join ", ")) ("Subnet: "+($Adapter.IPSubnet -join ", "))) Write-Info (Get-DoctorText ("网关: "+($Adapter.DefaultIPGateway -join ", ")) ("Gateway: "+($Adapter.DefaultIPGateway -join ", "))) Write-Info ("DNS: "+($Adapter.DNSServerSearchOrder -join ", ")) } function Set-AdapterDHCP { param($Adapter) Save-CurrentImportantConfig -Reason "Enable DHCP" try { $r1=$Adapter.EnableDHCP() $null=$Adapter.SetDNSServerSearchOrder($null) if ($r1.ReturnValue -eq 0 -or $r1.ReturnValue -eq 1) { Write-Fix (Get-DoctorText "已启用 DHCP" "DHCP has been enabled") ipconfig /renew >$null 2>&1 } else { Write-ErrorMessage ("DHCP ReturnCode="+$r1.ReturnValue) } } catch { Write-ErrorMessage (Get-DoctorText "DHCP配置失败" "DHCP configuration failed") } } function Set-AdapterStaticIP { param($Adapter) $ip=Read-Host (Get-DoctorText "请输入IP地址" "Enter IP address") if (-not (Test-IPv4Address $ip)) { Write-Warn (Get-DoctorText "IP地址格式错误" "Invalid IP address") return } $mask=Read-Host (Get-DoctorText "请输入子网掩码,例如 255.255.255.0" "Enter subnet mask, e.g. 255.255.255.0") if (-not (Test-SubnetMask $mask)) { Write-Warn (Get-DoctorText "子网掩码格式错误" "Invalid subnet mask") return } $gw=Read-Host (Get-DoctorText "请输入默认网关" "Enter default gateway") if ($gw -and (-not (Test-IPv4Address $gw))) { Write-Warn (Get-DoctorText "网关格式错误" "Invalid gateway") return } $dns1=Read-Host (Get-DoctorText "请输入首选DNS,可回车跳过" "Enter primary DNS or press Enter to skip") if ($dns1 -and (-not (Test-IPv4Address $dns1))) { Write-Warn (Get-DoctorText "DNS格式错误" "Invalid DNS") return } $dns2="" if ($dns1) { $dns2=Read-Host (Get-DoctorText "请输入备用DNS,可回车跳过" "Enter secondary DNS or press Enter to skip") if ($dns2 -and (-not (Test-IPv4Address $dns2))) { Write-Warn (Get-DoctorText "备用DNS格式错误" "Invalid secondary DNS") return } } Write-Warn (Get-DoctorText "修改当前网卡IP可能导致远程连接立即中断" "Changing the current IP may immediately disconnect the remote session") $confirm=Read-Host (Get-DoctorText "输入 Y 确认修改" "Enter Y to confirm") if ($confirm -notmatch "^[Yy]$") { return } Save-CurrentImportantConfig -Reason "Static IP configuration" try { $r=$Adapter.EnableStatic(@($ip),@($mask)) if ($r.ReturnValue -ne 0 -and $r.ReturnValue -ne 1) { Write-ErrorMessage ("EnableStatic ReturnCode="+$r.ReturnValue) return } if ($gw) { $g=$Adapter.SetGateways(@($gw),@(1)) if ($g.ReturnValue -ne 0 -and $g.ReturnValue -ne 1) { Write-Warn ("SetGateway ReturnCode="+$g.ReturnValue) } } if ($dns1) { $servers=@($dns1) if ($dns2) { $servers += $dns2 } $d=$Adapter.SetDNSServerSearchOrder($servers) if ($d.ReturnValue -ne 0 -and $d.ReturnValue -ne 1) { Write-Warn ("SetDNS ReturnCode="+$d.ReturnValue) } } Write-Fix (Get-DoctorText "静态IP配置已提交" "Static IP configuration has been applied") } catch { Write-ErrorMessage (Get-DoctorText "静态IP设置失败" "Static IP configuration failed") } } function Show-IPMenu { while($true) { Show-Header Write-Section $Script:T.Menu5 Write-Host (Get-DoctorText "1) 查看全部活动网卡配置" "1) View active adapters") Write-Host (Get-DoctorText "2) 将指定网卡改为 DHCP" "2) Enable DHCP on an adapter") Write-Host (Get-DoctorText "3) 手动设置静态 IP / 掩码 / 网关 / DNS" "3) Configure static IP / subnet / gateway / DNS") Write-Host (Get-DoctorText "4) 仅修改 DNS" "4) Change DNS only") Write-Host "0) $($Script:T.Menu0)" Write-Host "" $choice=Read-Host $Script:T.Select switch($choice) { "1" { foreach($a in (Get-IPEnabledAdapters)) { Show-AdapterConfiguration $a; Write-Host "" } Wait-Doctor } "2" { $a=Select-NetworkAdapter if($a) { Show-AdapterConfiguration $a Write-Warn (Get-DoctorText "切换DHCP可能导致当前远程连接断开" "Enabling DHCP may disconnect the current session") $c=Read-Host (Get-DoctorText "输入 Y 确认" "Enter Y to confirm") if($c -match "^[Yy]$") { Set-AdapterDHCP $a } Wait-Doctor } } "3" { $a=Select-NetworkAdapter if($a) { Show-AdapterConfiguration $a; Set-AdapterStaticIP $a; Wait-Doctor } } "4" { $a=Select-NetworkAdapter if($a) { Set-AdapterDNS $a; Wait-Doctor } } "0" { return } default { Write-Warn $Script:T.Invalid; Start-Sleep -Seconds 1 } } } } # ============================================================ # Local account lockout # ============================================================ function Repair-LocalAccounts { param([switch]$Quiet) if (-not $Quiet) { Write-Section (Get-DoctorText "账户锁定检测与修复" "Account Lockout Diagnostics and Repair") } try { $cs=Get-WmiObject Win32_ComputerSystem -ErrorAction SilentlyContinue if ($cs -and $cs.DomainRole -ge 4) { Write-Warn (Get-DoctorText "检测到域控制器,为避免修改域账户策略,本模块不执行自动修改" "Domain Controller detected. Automatic account-policy changes are skipped") return } } catch {} Write-Info (Get-DoctorText "关闭本地账户锁定阈值" "Disabling local account lockout threshold") net accounts /lockoutthreshold:0 >$null 2>&1 if ($LASTEXITCODE -eq 0) { Write-Fix (Get-DoctorText "本地账户已设置为永不因错误密码次数自动锁定" "Local account lockout threshold has been disabled") } else { Write-Warn (Get-DoctorText "锁定策略修改失败或被组策略控制" "Lockout policy update failed or may be controlled by Group Policy") } $excluded=@("Guest","DefaultAccount","WDAGUtilityAccount") try { $users=@(Get-WmiObject Win32_UserAccount -Filter "LocalAccount=True" -ErrorAction SilentlyContinue | Where-Object { $_.Disabled -eq $false -and $excluded -notcontains $_.Name }) foreach($u in $users) { $name=$u.Name try { $adsi=[ADSI]("WinNT://"+$env:COMPUTERNAME+"/"+$name+",user") try { if ($adsi.IsAccountLocked) { $adsi.IsAccountLocked=$false $adsi.SetInfo() Write-Fix (Get-DoctorText ("已解锁本地账户: "+$name) ("Unlocked local account: "+$name)) } } catch {} try { $flags=[int]$adsi.UserFlags.Value $dontExpire=0x10000 if (($flags -band $dontExpire) -eq 0) { $adsi.UserFlags=($flags -bor $dontExpire) $adsi.SetInfo() Write-Fix (Get-DoctorText ("已设置密码永不过期: "+$name) ("Password set to never expire: "+$name)) } } catch {} } catch { Write-Warn (Get-DoctorText ("无法处理本地账户: "+$name) ("Unable to process local account: "+$name)) } } } catch { Write-Warn (Get-DoctorText "本地账户枚举失败" "Unable to enumerate local accounts") } Write-Info (Get-DoctorText "域账户和系统服务身份不会被修改" "Domain accounts and system service identities are not modified") } function Show-AccountRepair { Show-Header Repair-LocalAccounts Wait-Doctor } # ============================================================ # One-click # ============================================================ function OneClick-RemoteRepair { Show-Header Write-Section (Get-DoctorText "一键检测并自动修复远程问题" "One-click Remote Diagnostics and Repair") Write-Section "RDP" $rdp=Invoke-RDPRepairCore Write-Section (Get-DoctorText "账户" "Accounts") Repair-LocalAccounts -Quiet Write-Section (Get-DoctorText "基础网络" "Basic Network") $p=Get-PrimaryAdapter if ($p) { $name=Get-AdapterDisplayName $p Write-Info (Get-DoctorText ("活动网卡: "+$name) ("Active adapter: "+$name)) Write-Info ("IP: "+(Get-AdapterIPv4 $p)) Write-Info (Get-DoctorText ("网关: "+($p.DefaultIPGateway -join ", ")) ("Gateway: "+($p.DefaultIPGateway -join ", "))) } else { Write-Warn (Get-DoctorText "未检测到活动IPv4网卡" "No active IPv4 adapter detected") } if (Test-InternetTCP) { Write-OK (Get-DoctorText "公网TCP连通正常" "Internet TCP connectivity is available") } else { Write-Warn (Get-DoctorText "公网TCP连通测试失败" "Internet TCP connectivity test failed") } Write-Section "DNS" $dns=Invoke-DNSAutoRepair Write-Section (Get-DoctorText "最终结果" "Final Result") if ($rdp) { Write-OK (Get-DoctorText "RDP基础状态正常" "RDP basic status is healthy") } else { Write-Warn (Get-DoctorText "RDP仍存在异常,请进入菜单2继续处理" "RDP still has an issue; use menu 2") } if ($dns) { Write-OK (Get-DoctorText "DNS状态正常" "DNS status is healthy") } else { Write-Warn (Get-DoctorText "DNS仍存在异常,请进入菜单3继续处理" "DNS still has an issue; use menu 3") } Write-Log "One-click repair completed" Wait-Doctor } # ============================================================ # CC / abnormal connections # ============================================================ function Get-EndpointAddress { param([string]$Endpoint) if ($Endpoint -match "^\[([^\]]+)\]:(\d+)$") { return $Matches[1] } if ($Endpoint -match "^(.+):(\d+)$") { return $Matches[1] } return $Endpoint } function Get-EndpointPort { param([string]$Endpoint) if ($Endpoint -match ":(\d+)$") { return $Matches[1] } return "" } function Detect-AbnormalConnections { Show-Header Write-Section (Get-DoctorText "CC / 异常网络连接检测" "CC / Abnormal Network Connection Detection") $items=@() try { foreach($line in (netstat -ano -p tcp 2>$null)) { $text=$line.Trim() if (-not $text.StartsWith("TCP")) { continue } $parts=$text -split "\s+" if ($parts.Count -lt 4) { continue } $local=$parts[1] $remote=$parts[2] $state=$parts[3] $pidValue="" if ($parts.Count -ge 5) { $pidValue=$parts[4] } $items += New-Object PSObject -Property @{ RemoteAddress=(Get-EndpointAddress $remote) LocalPort=(Get-EndpointPort $local) State=$state PID=$pidValue } } } catch {} Write-Info (Get-DoctorText ("TCP连接记录数: "+$items.Count) ("TCP connection records: "+$items.Count)) Write-Section (Get-DoctorText "连接状态统计" "Connection State Summary") $items | Group-Object State | Sort-Object Count -Descending | Select-Object Count,Name | Format-Table -AutoSize Write-Section (Get-DoctorText "来源IP Top 20" "Top 20 Remote IPs") $items | Where-Object { $_.RemoteAddress -and $_.RemoteAddress -ne "0.0.0.0" -and $_.RemoteAddress -ne "127.0.0.1" -and $_.RemoteAddress -ne "::" -and $_.RemoteAddress -ne "::1" -and $_.RemoteAddress -ne "*" } | Group-Object RemoteAddress | Sort-Object Count -Descending | Select-Object -First 20 Count,Name | Format-Table -AutoSize Write-Section (Get-DoctorText "本地目标端口 Top 20" "Top 20 Local Ports") $items | Where-Object { $_.LocalPort } | Group-Object LocalPort | Sort-Object Count -Descending | Select-Object -First 20 Count,Name | Format-Table -AutoSize Write-Plan (Get-DoctorText "此模块只分析连接特征,不会自动判定或封禁IP" "This module only analyzes connection patterns and does not automatically block IP addresses") Wait-Doctor } # ============================================================ # System info # ============================================================ function Show-SystemInfo { Show-Header Write-Section (Get-DoctorText "系统与网络基础信息" "System and Network Information") try { $os=Get-WmiObject Win32_OperatingSystem -ErrorAction Stop Write-Info (Get-DoctorText ("操作系统: "+$os.Caption) ("Operating System: "+$os.Caption)) Write-Info (Get-DoctorText ("系统版本: "+$os.Version) ("System Version: "+$os.Version)) Write-Info (Get-DoctorText ("系统架构: "+$os.OSArchitecture) ("Architecture: "+$os.OSArchitecture)) Write-Info (Get-DoctorText ("最近启动时间: "+$os.ConvertToDateTime($os.LastBootUpTime)) ("Last boot: "+$os.ConvertToDateTime($os.LastBootUpTime))) Write-Info (Get-DoctorText ("可用内存(MB): "+[math]::Round($os.FreePhysicalMemory/1024,0)) ("Free memory(MB): "+[math]::Round($os.FreePhysicalMemory/1024,0))) } catch {} try { $cpu=Get-WmiObject Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 if($cpu){ Write-Info ("CPU: "+$cpu.Name) } } catch {} Write-Info (Get-DoctorText ("主机名: "+$env:COMPUTERNAME) ("Hostname: "+$env:COMPUTERNAME)) Write-Info (Get-DoctorText ("工具语言: "+$Script:Language) ("Tool language: "+$Script:Language)) Write-Info ("PowerShell: "+$PSVersionTable.PSVersion.ToString()) foreach($a in (Get-IPEnabledAdapters)) { Show-AdapterConfiguration $a } Wait-Doctor } # ============================================================ # Main menu # ============================================================ function Show-MainMenu { while($true) { Show-Header Write-Host "" Write-Host ("1) "+$Script:T.Menu1) Write-Host ("2) "+$Script:T.Menu2) Write-Host ("3) "+$Script:T.Menu3) Write-Host ("4) "+$Script:T.Menu4) Write-Host ("5) "+$Script:T.Menu5) Write-Host ("6) "+$Script:T.Menu6) Write-Host ("7) "+$Script:T.Menu7) Write-Host ("8) "+$Script:T.Menu8) Write-Host ("0) "+$Script:T.Menu0) Write-Host "" Write-Host "============================================================" -ForegroundColor Green $choice=Read-Host ($Script:T.Select+":") switch($choice) { "1" { OneClick-RemoteRepair } "2" { Show-RDPMenu } "3" { Show-DNSMenu } "4" { Repair-NetworkStack } "5" { Show-IPMenu } "6" { Show-AccountRepair } "7" { Detect-AbnormalConnections } "8" { Show-SystemInfo } "0" { Write-Host "" Write-OK $Script:T.ExitText Start-Sleep -Milliseconds 500 return } default { Write-Warn $Script:T.Invalid Start-Sleep -Seconds 1 } } } } if (-not (Test-Administrator)) { Write-Host "" Write-Host (Get-DoctorText "需要管理员权限运行" "Administrator privileges are required") -ForegroundColor Red Write-Host "" pause exit 1 } Write-Log ("Windows Doctor started. Language="+$Script:Language) Show-MainMenu