Ce adresă raportează Windows pentru o sesiune RDP?
API-ul WTS returnează o adresă de client raportată pentru o sesiune RDP, iar registrul nu. Interogați ID-ul de sesiune corect și decodificați corect IPv4 sau IPv6.
O sesiune RDP are nevoie de o adresă de client, iar sursele evidente nu sunt de acord. CLIENTNAME oferă un nume de computer, registrul conține valori din alte sesiuni, iar partenerul socketului poate fi o adresă de gateway sau NAT în locul clientului. Windows expune într-adevăr o adresă de client raportată pentru fiecare sesiune, dar trebuie să interogați sesiunea corectă și să decodificați corect răspunsul.
Ancorați căutarea într-o singură sesiune
Începeți cu sesiunea, nu cu persoana. Un host RDP Windows poate purta mai multe sesiuni pentru un singur cont, iar o adresă văzută înainte de o reconectare poate să nu mai corespundă legăturii active. Notați împreună ID-ul sesiunii, utilizatorul și momentul observării. ID-ul sesiunii este cheia pe care o așteaptă API-ul și este valoarea care împiedică două sesiuni să fie confundate.
Pe host, instrumentele de sesiune instalate listează ce există; folosiți vizualizarea administrativă doar când aveți dreptul la ea. Confirmați ID-ul ales imediat înainte de interogare, deoarece un ID reciclat returnează datele altei sesiuni.
qwinsta
Apelați WTSQuerySessionInformationW pentru acel ID
API-ul Terminal Services răspunde la întrebare cu clasa de informații 14, WTSClientAddress. Un mic wrapper ctypes este suficient pe Windows și evită citirea hive-ului altui utilizator. Codul de mai jos preia un ID de sesiune explicit și întreabă hostul local.
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)
Transmiteți ID-ul de sesiune întreg pe care tocmai l-ați verificat și păstrați handle-ul de server nul pentru mașina locală. Tratați un apel eșuat ca eroare, nu ca adresă absentă, deoarece cele două duc la concluzii diferite. Interogarea sesiunii altui utilizator necesită permisiunea Query Information corespunzătoare, așa că rulați instrumentul sub un cont care a primit-o în loc să escaladați ad hoc. Bufferul returnat aparține API-ului și trebuie eliberat chiar și când decodificarea eșuează.
Decodificați familia, apoi eliberați bufferul
Structura nu este un șir IP. Câmpul său de familie decide cum se citesc cei douăzeci de octeți de adresă: IPv4 începe la offsetul doi, în timp ce IPv6 începe la offsetul zero. Adresa nespecificată înseamnă că clientul nu a raportat niciuna, ceea ce nu este același lucru cu un șir gol. Conexiunile prin Remote Desktop Gateway sunt documentate ca un caz în care adresa nu este disponibilă, așa că o sesiune intermediată de gateway nu raportează nimic util despre adresa reală a clientului. Corelarea trebuie atunci să treacă la înregistrările proprii ale gateway-ului.
Tipăriți familia lângă adresă în timpul depanării, deoarece o nepotrivire între cele două este cauza obișnuită a unei valori plauzibile dar greșite. Eliberați bufferul cu WTSFreeMemory într-un bloc finally, astfel încât o excepție să nu îl scurgă.
Separați adresa raportată de dovadă
O adresă de client raportată este o declarație furnizată de client, nu o observație de încredere. Nu este partenerul TCP, nu este tabelul de închirieri al gateway-ului VPN și nu este adresa publică a clientului. Când întrebarea reală este ce închiriere VPN aparține unei persoane, corelați în același timp înregistrarea de sesiune autentificată a serverului VPN. Când întrebarea este ce sursă a ajuns la ascultător, citiți tabelul de conexiuni sau jurnalele permise din partea serverului.
Păstrați aceste valori etichetate separat în orice instrument de suport. Un rezultat WTS lipsă trebuie să rămână lipsă în loc să cadă pe o valoare de registru arbitrară din altă sesiune. Înregistrați ID-ul sesiunii lângă adresă, deoarece același utilizator se poate reconecta și poate primi o valoare nouă. Nici numele clientului, nici adresa raportată nu sunt dovezi suficiente pentru a acorda acces.