Une session RDP a besoin d’une adresse client, et les sources évidentes ne concordent pas. CLIENTNAME donne un nom d’ordinateur, le registre contient des valeurs d’autres sessions, et l’homologue de socket peut être une adresse de passerelle ou NAT plutôt que le client. Windows expose bien une adresse client signalée par session, mais il faut demander la bonne session et décoder correctement la réponse.

Ancrez la recherche sur une seule session

Partez de la session, pas de la personne. Un hôte RDP Windows peut porter plusieurs sessions pour un même compte, et une adresse vue avant une reconnexion peut ne plus correspondre au lien actif. Notez ensemble l’ID de session, l’utilisateur et l’heure d’observation. L’ID de session est la clé qu’attend l’API, et c’est la valeur qui empêche de confondre deux sessions.

Sur l’hôte, les outils de session installés listent ce qui existe ; n’utilisez la vue administrative que lorsque vous y avez droit. Confirmez l’ID choisi juste avant l’interrogation, car un ID recyclé renvoie les données d’une autre session.

qwinsta

Appelez WTSQuerySessionInformationW pour cet ID

L’API Terminal Services répond à la question avec la classe d’informations 14, WTSClientAddress. Un petit wrapper ctypes suffit sous Windows, et il évite de lire la ruche d’un autre utilisateur. Le code ci-dessous prend un ID de session explicite et interroge l’hôte 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)

Passez l’ID de session entier que vous venez de vérifier et conservez le handle serveur nul pour la machine locale. Traitez un appel échoué comme une erreur et non comme une adresse absente, car les deux mènent à des conclusions différentes. Interroger la session d’un autre utilisateur exige l’autorisation Query Information appropriée, alors exécutez l’outil sous un compte qui l’a reçue au lieu d’élever les privilèges au cas par cas. Le tampon renvoyé appartient à l’API et doit être libéré même lorsque le décodage échoue.

Décodez la famille, puis libérez le tampon

La structure n’est pas une chaîne IP. Son champ de famille décide comment lire les vingt octets d’adresse : IPv4 commence à l’offset deux, tandis qu’IPv6 commence à l’offset zéro. L’adresse non spécifiée signifie que le client n’en a signalé aucune, ce qui n’est pas la même chose qu’une chaîne vide. Les connexions via Remote Desktop Gateway sont documentées comme un cas où l’adresse n’est pas disponible, donc une session relayée par passerelle ne signale rien d’utile sur l’adresse réelle du client. La corrélation doit alors passer aux propres enregistrements de la passerelle.

Imprimez la famille à côté de l’adresse pendant le débogage, car une divergence entre les deux est la cause habituelle d’une valeur plausible mais fausse. Libérez le tampon avec WTSFreeMemory dans un bloc finally pour qu’une exception ne le fuie pas.

Séparez l’adresse signalée de la preuve

Une adresse client signalée est une déclaration fournie par le client, non une observation fiable. Ce n’est ni l’homologue TCP, ni la table des baux de la passerelle VPN, ni l’adresse publique du client. Quand la vraie question est de savoir quel bail VPN appartient à une personne, corrélez en même temps l’enregistrement de session authentifié du serveur VPN. Quand la question est de savoir quelle source a atteint l’écouteur, lisez la table de connexions ou les journaux autorisés côté serveur.

Gardez ces valeurs étiquetées séparément dans tout outil de support. Un résultat WTS manquant doit rester manquant plutôt que de retomber sur une valeur de registre arbitraire d’une autre session. Journalisez l’ID de session à côté de l’adresse, car le même utilisateur peut se reconnecter et recevoir une nouvelle valeur. Ni le nom du client ni l’adresse signalée ne constituent une preuve suffisante pour accorder un accès.

Articles similaires