# 服务器登录监控脚本 - 完整版(监控所有RDP相关登录类型) # 监控远程桌面登录并通过钉钉机器人推送通知(支持加签) param ( [string]$DingTalkWebhookUrl = "https://oapi.dingtalk.com/robot/send?access_token=d28bd09159097d9cc5793a183990927ce637bd8addafb5e4586e2687ca317039", [string]$DingTalkSecret = "SECd4bf3fb7703bd2826896deefa68d579e9945a67058ee9047ac5f8757ae800729" ) # 钉钉消息发送函数(支持加签) function Send-DingTalkMessage { param( [string]$消息内容 ) try { # 计算签名 $timestamp = [DateTimeOffset]::Now.ToUnixTimeMilliseconds() $stringToSign = "$timestamp`n$DingTalkSecret" $hmacsha = New-Object System.Security.Cryptography.HMACSHA256 $hmacsha.key = [Text.Encoding]::UTF8.GetBytes($DingTalkSecret) $signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signatureBase64 = [Convert]::ToBase64String($signature) $encodedSignature = [System.Web.HttpUtility]::UrlEncode($signatureBase64) # 完整的请求URL $fullUrl = "$DingTalkWebhookUrl×tamp=$timestamp&sign=$encodedSignature" $body = @{ msgtype = "text" text = @{ content = $消息内容 } } | ConvertTo-Json -Depth 10 $response = Invoke-RestMethod -Uri $fullUrl -Method Post -Body $body -ContentType "application/json; charset=utf-8" Write-Host "✓ 钉钉消息发送成功: $($response.errmsg)" -ForegroundColor Green } catch { Write-Error "✗ 钉钉消息发送失败: $_" } } # 登录类型说明 $登录类型说明 = @{ "2" = "交互式登录(本地控制台)" "3" = "网络登录(含NLA身份验证)" "4" = "批处理(计划任务)" "5" = "服务登录" "7" = "会话解锁" "8" = "网络明文(如IIS基本认证)" "9" = "新凭证(RunAs)" "10" = "远程交互(RDP无NLA)" "11" = "缓存交互(域凭据缓存)" } # 程序启动信息 Write-Host "========================================" -ForegroundColor Cyan Write-Host " 服务器登录监控服务已启动" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "服务器名称: $env:COMPUTERNAME" -ForegroundColor Yellow Write-Host "启动时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow Write-Host "监控类型: 所有远程桌面相关登录(类型3、7、10)" -ForegroundColor Yellow Write-Host "检查间隔: 5秒" -ForegroundColor Yellow Write-Host "========================================" -ForegroundColor Cyan # 记录上次检查的时间 $上次检查时间 = (Get-Date).AddHours(-1) # 记录已发送过的登录事件,避免重复通知 $已通知事件 = @{} # 主循环 while ($true) { try { # 获取新的登录事件(Event ID 4624) $事件列表 = Get-WinEvent -FilterHashtable @{ LogName = 'Security' ID = 4624 StartTime = $上次检查时间 } -ErrorAction SilentlyContinue foreach ($事件 in $事件列表) { # 解析事件XML $事件Xml = [xml]$事件.ToXml() # 获取登录类型 $登录类型 = ($事件Xml.Event.EventData.Data | Where-Object {$_.Name -eq "LogonType"}).'#text' # 监控所有可能的远程桌面相关登录类型:3(NLA验证)、7(解锁)、10(传统RDP) if ($登录类型 -eq "3" -or $登录类型 -eq "7" -or $登录类型 -eq "10") { # 获取登录信息 $用户名 = ($事件Xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetUserName"}).'#text' $域名 = ($事件Xml.Event.EventData.Data | Where-Object {$_.Name -eq "TargetDomainName"}).'#text' $来源IP = ($事件Xml.Event.EventData.Data | Where-Object {$_.Name -eq "IpAddress"}).'#text' $登录时间 = $事件.TimeCreated $记录ID = $事件.RecordId $登录GUID = ($事件Xml.Event.EventData.Data | Where-Object {$_.Name -eq "LogonGuid"}).'#text' # 过滤本地IP和无效IP $有效IP = $来源IP -and $来源IP -ne "127.0.0.1" -and $来源IP -ne "::1" -and $来源IP -ne "-" if ($有效IP) { # 生成唯一标识,避免重复通知 $事件标识 = "$记录ID-$来源IP-$用户名" if (-not $已通知事件.ContainsKey($事件标识)) { # 清理过旧的记录,避免内存溢出 if ($已通知事件.Count -gt 1000) { $已通知事件.Clear() } # 处理空域名 if ([string]::IsNullOrEmpty($域名) -or $域名 -eq "-") { $域名 = $env:COMPUTERNAME } # 获取登录类型说明 $类型说明 = if ($登录类型说明.ContainsKey($登录类型)) { $登录类型说明[$登录类型] } else { "未知类型($登录类型)" } # 添加额外说明 $额外说明 = "" if ($登录类型 -eq "3") { $额外说明 = "(使用NLA验证的RDP连接)" } elseif ($登录类型 -eq "7") { $额外说明 = "(远程会话解锁)" } elseif ($登录类型 -eq "10") { $额外说明 = "(传统RDP连接)" } # 构建钉钉消息 $消息内容 = @" 【服务器登录告警】 ━━━━━━━━━━━━━━━━━━━━━━ 🏢 服务器:$env:COMPUTERNAME 📅 时间:$($登录时间.ToString('yyyy-MM-dd HH:mm:ss')) 👤 用户:$域名\$用户名 🔑 登录类型:$类型说明 $额外说明 🌐 来源IP:$来源IP 📝 记录ID:$记录ID ━━━━━━━━━━━━━━━━━━━━━━ "@ # 显示日志 Write-Host "`n[$(Get-Date -Format 'HH:mm:ss')] 检测到新的远程登录" -ForegroundColor Yellow Write-Host " 用户: $域名\$用户名" -ForegroundColor White Write-Host " 类型: $类型说明 $额外说明" -ForegroundColor Cyan Write-Host " 来源IP: $来源IP" -ForegroundColor White Write-Host " 时间: $($登录时间.ToString('HH:mm:ss'))" -ForegroundColor White # 发送钉钉通知 Send-DingTalkMessage -消息内容 $消息内容 # 记录已通知 $已通知事件[$事件标识] = $true } } } } # 更新上次检查时间 $上次检查时间 = Get-Date # 显示运行状态(每5分钟显示一次) if (((Get-Date).Minute % 5) -eq 0 -and (Get-Date).Second -lt 5) { Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 监控运行中,已监控到 $($已通知事件.Count) 次登录" -ForegroundColor Gray } # 等待5秒后继续检查 Start-Sleep -Seconds 5 } catch { Write-Error "监控过程发生错误: $_" Write-Host "等待10秒后重试..." -ForegroundColor Red Start-Sleep -Seconds 10 } }