Vilken adress rapporterar Windows för en RDP-session?
WTS-API:et returnerar en rapporterad klientadress för en RDP-session, och registret gör det inte. Fråga efter rätt sessions-ID och avkoda IPv4 eller IPv6 korrekt.
En RDP-session behöver en klientadress, och de självklara källorna är inte överens. CLIENTNAME ger ett datornamn, registret innehåller värden från andra sessioner, och socketens motpart kan vara en gateway- eller NAT-adress i stället för klienten. Windows tillhandahåller faktiskt en rapporterad klientadress per session, men du måste fråga efter rätt session och avkoda svaret korrekt.
Förankra sökningen i en enda session
Börja med sessionen, inte med personen. En Windows RDP-värd kan bära flera sessioner för ett konto, och en adress som sågs före en återanslutning stämmer kanske inte längre med den aktiva förbindelsen. Anteckna sessions-ID, användare och observationstid tillsammans. Sessions-ID:t är nyckeln som API:et förväntar sig, och det är värdet som hindrar två sessioner från att blandas ihop.
På värden listar de installerade sessionsverktygen vad som finns; använd den administrativa vyn bara när du har rätt till den. Bekräfta det valda ID:t direkt före frågan, eftersom ett återanvänt ID returnerar en annan sessions data.
qwinsta
Anropa WTSQuerySessionInformationW för det ID:t
Terminal Services-API:et besvarar frågan med informationsklass 14, WTSClientAddress. En liten ctypes-wrapper räcker på Windows, och den undviker att läsa en annan användares hive. Koden nedan tar ett explicit sessions-ID och frågar den lokala värden.
import ctypes
import ipaddress
import socket
from ctypes import wintypes
class WTS_CLIENT_ADDRESS(ctypes.Structure):
_fields_ = [("AddressFamily", wintypes.DWORD),
("Address", ctypes.c_ubyte * 20)]
wts = ctypes.WinDLL("Wtsapi32", use_last_error=True)
query = wts.WTSQuerySessionInformationW
query.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.c_int,
ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(wintypes.DWORD)]
query.restype = wintypes.BOOL
free = wts.WTSFreeMemory
free.argtypes = [ctypes.c_void_p]
def client_address(session_id):
buf = ctypes.c_void_p()
size = wintypes.DWORD()
if not query(None, session_id, 14, ctypes.byref(buf), ctypes.byref(size)):
raise ctypes.WinError(ctypes.get_last_error())
try:
if size.value < ctypes.sizeof(WTS_CLIENT_ADDRESS):
raise RuntimeError("short WTS buffer")
value = ctypes.cast(buf, ctypes.POINTER(WTS_CLIENT_ADDRESS)).contents
raw = bytes(value.Address)
if value.AddressFamily == socket.AF_INET:
addr = ipaddress.ip_address(raw[2:6])
elif value.AddressFamily == socket.AF_INET6:
addr = ipaddress.ip_address(raw[:16])
else:
return None
return None if addr.is_unspecified else str(addr)
finally:
free(buf)
Skicka heltals-sessions-ID:t som du just verifierade och behåll noll-serverhandtaget för den lokala datorn. Behandla ett misslyckat anrop som ett fel i stället för som en saknad adress, eftersom de två leder till olika slutsatser. En fråga om en annan användares session kräver rätt Query Information-behörighet, så kör verktyget under ett konto som fått den i stället för att eskalera ad hoc. Den returnerade bufferten tillhör API:et och måste frigöras även när avkodningen misslyckas.
Avkoda familjen och frigör sedan bufferten
Strukturen är inte en IP-sträng. Dess familjefält avgör hur de tjugo adressbyten läses: IPv4 börjar vid offset två, medan IPv6 börjar vid offset noll. Den ospecificerade adressen betyder att klienten inte rapporterade någon, vilket inte är samma sak som en tom sträng. Anslutningar via Remote Desktop Gateway är dokumenterade som ett fall där adressen inte är tillgänglig, så en gatewayförmedlad session rapporterar ingenting användbart om klientens verkliga adress. Korrelationen måste då flyttas till gatewayens egna register.
Skriv ut familjen bredvid adressen när du felsöker, eftersom en diskrepans mellan de två är den vanliga orsaken till ett trovärdigt men felaktigt värde. Frigör bufferten med WTSFreeMemory i ett finally-block så att ett undantag inte läcker den.
Skilj den rapporterade adressen från bevis
En rapporterad klientadress är ett påstående som klienten lämnar, inte en tillförlitlig observation. Den är inte TCP-motparten, inte VPN-gatewayens leasetabell och inte klientens offentliga adress. När den egentliga frågan är vilken VPN-lease som tillhör en person, korrelera VPN-serverns autentiserade sessionspost samtidigt. När frågan är vilken källa som nådde lyssnaren, läs anslutningstabellen eller de tillåtna serverloggarna.
Håll dessa värden separat märkta i varje supportverktyg. Ett saknat WTS-resultat bör förbli saknat i stället för att falla tillbaka på ett godtyckligt registervärde från en annan session. Logga sessions-ID:t bredvid adressen, eftersom samma användare kan återansluta och få ett nytt värde. Varken klientnamnet eller den rapporterade adressen är tillräckliga bevis för att bevilja åtkomst.