Welche Adresse meldet Windows für eine RDP-Sitzung?
Die WTS-API liefert eine gemeldete Clientadresse für eine RDP-Sitzung, die Registrierung nicht. Fragen Sie die richtige Sitzungs-ID ab und dekodieren Sie IPv4 oder IPv6 korrekt.
Eine RDP-Sitzung braucht eine Clientadresse, und die naheliegenden Quellen widersprechen sich. CLIENTNAME liefert einen Computernamen, die Registrierung enthält Werte aus anderen Sitzungen, und der Socket-Gegenpart kann eine Gateway- oder NAT-Adresse statt des Clients sein. Windows stellt zwar eine gemeldete Clientadresse pro Sitzung bereit, aber Sie müssen die richtige Sitzung abfragen und die Antwort korrekt dekodieren.
Verankern Sie die Suche an einer Sitzung
Beginnen Sie mit der Sitzung, nicht mit der Person. Ein Windows-RDP-Host kann mehrere Sitzungen für ein Konto führen, und eine Adresse, die vor einer erneuten Verbindung gesehen wurde, passt möglicherweise nicht mehr zur aktiven Verbindung. Notieren Sie Sitzungs-ID, Benutzer und Beobachtungszeit zusammen. Die Sitzungs-ID ist der Schlüssel, den die API erwartet, und sie ist der Wert, der verhindert, dass zwei Sitzungen verwechselt werden.
Auf dem Host listet die installierte Sitzungswerkzeugkette auf, was existiert; nutzen Sie die administrative Ansicht nur, wenn Sie dazu berechtigt sind. Bestätigen Sie die gewählte ID unmittelbar vor der Abfrage, denn eine wiederverwendete ID liefert die Daten einer anderen Sitzung.
qwinsta
Rufen Sie WTSQuerySessionInformationW für diese ID auf
Die Terminaldienste-API beantwortet die Frage mit der Informationsklasse 14, WTSClientAddress. Ein kleiner ctypes-Wrapper genügt unter Windows, und er vermeidet es, den Hive eines anderen Benutzers zu lesen. Der Code unten nimmt eine explizite Sitzungs-ID und fragt den lokalen Host.
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)
Übergeben Sie die ganzzahlige Sitzungs-ID, die Sie gerade geprüft haben, und behalten Sie das Null-Serverhandle für den lokalen Rechner. Behandeln Sie einen fehlgeschlagenen Aufruf als Fehler und nicht als fehlende Adresse, denn beides führt zu unterschiedlichen Schlüssen. Das Abfragen der Sitzung eines anderen Benutzers erfordert die entsprechende Berechtigung Query Information, führen Sie das Werkzeug daher unter einem Konto aus, dem sie erteilt wurde, statt ad hoc zu eskalieren. Der zurückgegebene Puffer gehört der API und muss auch dann freigegeben werden, wenn die Dekodierung fehlschlägt.
Dekodieren Sie die Familie und geben Sie dann den Puffer frei
Die Struktur ist keine IP-Zeichenkette. Ihr Familienfeld entscheidet, wie die zwanzig Adressbytes gelesen werden: IPv4 beginnt bei Offset zwei, IPv6 dagegen bei Offset null. Die unspezifizierte Adresse bedeutet, dass der Client keine gemeldet hat, was nicht dasselbe ist wie eine leere Zeichenkette. Verbindungen über das Remote Desktop Gateway sind als Fall dokumentiert, in dem die Adresse nicht verfügbar ist, eine über ein Gateway vermittelte Sitzung meldet also nichts Brauchbares zur echten Adresse des Clients. Die Korrelation muss dann zu den eigenen Aufzeichnungen des Gateways wandern.
Geben Sie beim Debuggen die Familie neben der Adresse aus, denn eine Abweichung zwischen beiden ist die übliche Ursache für einen plausiblen, aber falschen Wert. Geben Sie den Puffer mit WTSFreeMemory in einem finally-Block frei, damit eine Ausnahme ihn nicht leckt.
Trennen Sie die gemeldete Adresse vom Nachweis
Eine gemeldete Clientadresse ist eine vom Client gelieferte Aussage, keine verlässliche Beobachtung. Sie ist nicht der TCP-Gegenpart, nicht die Lease-Tabelle des VPN-Gateways und nicht die öffentliche Adresse des Clients. Wenn die eigentliche Frage lautet, welcher VPN-Lease zu einer Person gehört, korrelieren Sie gleichzeitig den authentifizierten Sitzungseintrag des VPN-Servers. Wenn die Frage lautet, welche Quelle den Listener erreicht hat, lesen Sie die Verbindungstabelle oder die zulässigen serverseitigen Protokolle.
Halten Sie diese Werte in jedem Supportwerkzeug getrennt beschriftet. Ein fehlendes WTS-Ergebnis sollte fehlend bleiben, statt auf einen beliebigen Registrierungswert aus einer anderen Sitzung zurückzufallen. Protokollieren Sie die Sitzungs-ID neben der Adresse, denn derselbe Benutzer kann sich erneut verbinden und einen neuen Wert erhalten. Weder der Clientname noch die gemeldete Adresse sind ausreichender Nachweis für die Gewährung von Zugriff.