Загрузка данных
$ErrorActionPreference = 'SilentlyContinue'
$Results = @()
# klipa1 ... klipa35
$KlipaPattern = '^klipa(?:[1-9]|[12][0-9]|3[0-5])$'
$WshShell = New-Object -ComObject WScript.Shell
# ------------------------------------------------------------
# Функция: получить klipaN из RDP-файла
# ------------------------------------------------------------
function Get-KlipaFromRdp {
param(
[Parameter(Mandatory = $true)]
[string]$RdpPath
)
if (-not (Test-Path -LiteralPath $RdpPath -PathType Leaf)) {
return $null
}
try {
$FullAddressLine = Get-Content `
-LiteralPath $RdpPath `
-ErrorAction Stop |
Where-Object {
$_ -match '^\s*full\s+address\s*:'
} |
Select-Object -First 1
if (-not $FullAddressLine) {
return $null
}
if ($FullAddressLine -match '^\s*full\s+address\s*:\s*s\s*:\s*(.+?)\s*$') {
$Address = $Matches[1].Trim()
# klipa1:3389 -> klipa1
$TargetHost = ($Address -split ':')[0].Trim()
# klipa1.domain.local -> klipa1
$ShortHost = ($TargetHost -split '\.')[0].Trim()
if ($ShortHost -match $KlipaPattern) {
return $ShortHost.ToLower()
}
}
}
catch {
return $null
}
return $null
}
# ------------------------------------------------------------
# Пользовательские профили
# ------------------------------------------------------------
$UserProfiles = Get-ChildItem `
-LiteralPath 'C:\Users' `
-Directory `
-Force `
-ErrorAction SilentlyContinue |
Where-Object {
$_.Name -notin @(
'Public',
'Default',
'Default User',
'All Users'
)
}
foreach ($UserProfile in $UserProfiles) {
$UserName = $UserProfile.Name
# Только папки первого уровня пользователя.
# AppData полностью исключаем.
$FirstLevelFolders = Get-ChildItem `
-LiteralPath $UserProfile.FullName `
-Directory `
-Force `
-ErrorAction SilentlyContinue |
Where-Object {
$_.Name -ne 'AppData'
}
foreach ($Folder in $FirstLevelFolders) {
# ------------------------------------------------------------
# Все .RDP и .LNK рекурсивно внутри папки первого уровня
# ------------------------------------------------------------
$Files = Get-ChildItem `
-LiteralPath $Folder.FullName `
-Recurse `
-Force `
-ErrorAction SilentlyContinue |
Where-Object {
-not $_.PSIsContainer -and (
$_.Extension -ieq '.rdp' -or
$_.Extension -ieq '.lnk'
)
}
foreach ($File in $Files) {
# ========================================================
# Обычный RDP-файл
# ========================================================
if ($File.Extension -ieq '.rdp') {
$Klipa = Get-KlipaFromRdp -RdpPath $File.FullName
if ($Klipa) {
$Results += [PSCustomObject]@{
User = $UserName
Host = $Klipa
Type = 'RDP'
Path = $File.FullName
}
}
continue
}
# ========================================================
# Windows shortcut .LNK
# ========================================================
if ($File.Extension -ieq '.lnk') {
try {
$Shortcut = $WshShell.CreateShortcut($File.FullName)
$TargetPath = [Environment]::ExpandEnvironmentVariables(
$Shortcut.TargetPath
)
$Arguments = [Environment]::ExpandEnvironmentVariables(
$Shortcut.Arguments
)
$MatchedHost = $null
# ------------------------------------------------
# Вариант 1:
# ярлык непосредственно указывает на *.rdp
# ------------------------------------------------
if (
$TargetPath -and
$TargetPath -match '(?i)\.rdp$'
) {
$MatchedHost = Get-KlipaFromRdp -RdpPath $TargetPath
}
# ------------------------------------------------
# Вариант 2:
# mstsc.exe /v:klipa1
# mstsc.exe /v klipa1
# mstsc.exe /v:klipa1:3389
# ------------------------------------------------
if (
-not $MatchedHost -and
[System.IO.Path]::GetFileName($TargetPath) -ieq 'mstsc.exe'
) {
$VMatch = [regex]::Match(
$Arguments,
'(?i)(?:^|\s)/v(?::|\s+)\s*(?<address>[^\s"]+)'
)
if ($VMatch.Success) {
$Address = $VMatch.Groups['address'].Value.Trim()
$TargetHost = ($Address -split ':')[0].Trim()
$ShortHost = ($TargetHost -split '\.')[0].Trim()
if ($ShortHost -match $KlipaPattern) {
$MatchedHost = $ShortHost.ToLower()
}
}
}
# ------------------------------------------------
# Вариант 3:
#
# Target:
# C:\Windows\System32\mstsc.exe
#
# Arguments:
# "C:\Users\user\workplace\Default.rdp"
# ------------------------------------------------
if (
-not $MatchedHost -and
[System.IO.Path]::GetFileName($TargetPath) -ieq 'mstsc.exe' -and
$Arguments
) {
$RdpArgument = $null
# Сначала ищем путь в кавычках
$QuotedMatch = [regex]::Match(
$Arguments,
'(?i)"(?<path>[^"]+\.rdp)"'
)
if ($QuotedMatch.Success) {
$RdpArgument = $QuotedMatch.Groups['path'].Value
}
else {
# Путь без кавычек
$PlainMatch = [regex]::Match(
$Arguments,
'(?i)(?<path>[A-Z]:\\.+?\.rdp)(?:\s|$)'
)
if ($PlainMatch.Success) {
$RdpArgument = $PlainMatch.Groups['path'].Value
}
}
if ($RdpArgument) {
$RdpArgument = [Environment]::ExpandEnvironmentVariables(
$RdpArgument
)
$MatchedHost = Get-KlipaFromRdp `
-RdpPath $RdpArgument
}
}
if ($MatchedHost) {
$Results += [PSCustomObject]@{
User = $UserName
Host = $MatchedHost
Type = 'LNK'
Path = $File.FullName
}
}
}
catch {
# Битые/недоступные ярлыки пропускаем
}
}
}
}
}
# ------------------------------------------------------------
# Убираем дубли
# ------------------------------------------------------------
$Results = $Results |
Sort-Object User, Host, Type, Path -Unique
# ------------------------------------------------------------
# PDQ OUTPUT
# ------------------------------------------------------------
Write-Output '============================================================'
Write-Output 'RDP KLIPA SCAN'
Write-Output "Computer: $env:COMPUTERNAME"
Write-Output '============================================================'
if (-not $Results -or $Results.Count -eq 0) {
Write-Output 'Совпадений не найдено.'
}
else {
Write-Output "Найдено совпадений: $($Results.Count)"
Write-Output ''
foreach ($Result in $Results) {
Write-Output "Пользователь : $($Result.User)"
Write-Output "Клипа : $($Result.Host)"
Write-Output "Тип : $($Result.Type)"
Write-Output "Файл : $($Result.Path)"
Write-Output '------------------------------------------------------------'
}
}
Write-Output 'Сканирование завершено.'