Μια συνεδρία RDP χρειάζεται διεύθυνση πελάτη, και οι προφανείς πηγές διαφωνούν. Το CLIENTNAME δίνει όνομα υπολογιστή, το μητρώο κρατά τιμές από άλλες συνεδρίες, και ο ομότιμος της υποδοχής μπορεί να είναι διεύθυνση πύλης ή NAT αντί για τον πελάτη. Τα Windows εκθέτουν όντως μια αναφερόμενη διεύθυνση πελάτη ανά συνεδρία, αλλά πρέπει να ζητήσετε τη σωστή συνεδρία και να αποκωδικοποιήσετε σωστά την απάντηση.

Αγκυρώστε την αναζήτηση σε μία συνεδρία

Ξεκινήστε από τη συνεδρία, όχι από το πρόσωπο. Ένας κεντρικός υπολογιστής RDP των Windows μπορεί να φέρει πολλές συνεδρίες για έναν λογαριασμό, και μια διεύθυνση που είδατε πριν από μια επανασύνδεση μπορεί να μην ταιριάζει πλέον με τον ενεργό σύνδεσμο. Καταγράψτε μαζί το αναγνωριστικό συνεδρίας, τον χρήστη και τον χρόνο παρατήρησης. Το αναγνωριστικό συνεδρίας είναι το κλειδί που περιμένει το API, και είναι η τιμή που εμποδίζει δύο συνεδρίες να μπερδευτούν.

Στον κεντρικό υπολογιστή, τα εγκατεστημένα εργαλεία συνεδρίας απαριθμούν ό,τι υπάρχει· χρησιμοποιήστε την προβολή διαχείρισης μόνο όταν έχετε δικαίωμα σε αυτήν. Επιβεβαιώστε το επιλεγμένο αναγνωριστικό αμέσως πριν από το ερώτημα, γιατί ένα ανακυκλωμένο αναγνωριστικό επιστρέφει δεδομένα άλλης συνεδρίας.

qwinsta

Καλέστε το WTSQuerySessionInformationW για αυτό το αναγνωριστικό

Το API Υπηρεσιών Τερματικού απαντά στην ερώτηση με κλάση πληροφοριών 14, WTSClientAddress. Ένα μικρό περιτύλιγμα ctypes αρκεί στα Windows, και αποφεύγει την ανάγνωση της κυψέλης άλλου χρήστη. Ο κώδικας παρακάτω παίρνει ρητό αναγνωριστικό συνεδρίας και ρωτά τον τοπικό κεντρικό υπολογιστή.

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)

Περάστε το ακέραιο αναγνωριστικό συνεδρίας που μόλις επαληθεύσατε, και κρατήστε τη μηδενική λαβή διακομιστή για το τοπικό μηχάνημα. Αντιμετωπίστε μια αποτυχημένη κλήση ως σφάλμα και όχι ως απούσα διεύθυνση, γιατί τα δύο οδηγούν σε διαφορετικά συμπεράσματα. Το ερώτημα για τη συνεδρία άλλου χρήστη απαιτεί την κατάλληλη άδεια Query Information, οπότε εκτελέστε το εργαλείο με λογαριασμό στον οποίο έχει χορηγηθεί αντί να κλιμακώσετε ad hoc. Το buffer που επιστρέφεται ανήκει στο API και πρέπει να απελευθερωθεί ακόμη και όταν η αποκωδικοποίηση αποτύχει.

Αποκωδικοποιήστε την οικογένεια και μετά απελευθερώστε το buffer

Η δομή δεν είναι συμβολοσειρά IP. Το πεδίο οικογένειας καθορίζει πώς διαβάζονται τα είκοσι byte της διεύθυνσης: το IPv4 ξεκινά στο offset δύο, ενώ το IPv6 ξεκινά στο offset μηδέν. Η μη καθορισμένη διεύθυνση σημαίνει ότι ο πελάτης δεν ανέφερε καμία, κάτι που δεν είναι το ίδιο με κενή συμβολοσειρά. Οι συνδέσεις μέσω Remote Desktop Gateway τεκμηριώνονται ως περίπτωση όπου η διεύθυνση δεν είναι διαθέσιμη, οπότε μια συνεδρία μέσω πύλης δεν αναφέρει τίποτα χρήσιμο για την πραγματική διεύθυνση του πελάτη. Η συσχέτιση τότε πρέπει να μετακινηθεί στα δικά της αρχεία της πύλης.

Εκτυπώστε την οικογένεια δίπλα στη διεύθυνση κατά την αποσφαλμάτωση, γιατί η αναντιστοιχία μεταξύ τους είναι η συνήθης αιτία μιας εύλογης αλλά λανθασμένης τιμής. Απελευθερώστε το buffer με το WTSFreeMemory σε μπλοκ finally, ώστε μια εξαίρεση να μην το διαρρεύσει.

Διαχωρίστε την αναφερόμενη διεύθυνση από την απόδειξη

Μια αναφερόμενη διεύθυνση πελάτη είναι δήλωση που παρέχει ο πελάτης, όχι αξιόπιστη παρατήρηση. Δεν είναι ο ομότιμος TCP, δεν είναι ο πίνακας μισθώσεων της πύλης VPN και δεν είναι η δημόσια διεύθυνση του πελάτη. Όταν το πραγματικό ερώτημα είναι ποια μίσθωση VPN ανήκει σε ένα πρόσωπο, συσχετίστε ταυτόχρονα την πιστοποιημένη εγγραφή συνεδρίας του διακομιστή VPN. Όταν το ερώτημα είναι ποια πηγή έφτασε στον ακροατή, διαβάστε τον πίνακα συνδέσεων ή τα επιτρεπόμενα αρχεία καταγραφής της πλευράς διακομιστή.

Κρατήστε αυτές τις τιμές επισημασμένες χωριστά σε κάθε εργαλείο υποστήριξης. Ένα αποτέλεσμα WTS που λείπει πρέπει να παραμένει απόν αντί να πέφτει σε μια αυθαίρετη τιμή μητρώου από άλλη συνεδρία. Καταγράψτε το αναγνωριστικό συνεδρίας δίπλα στη διεύθυνση, γιατί ο ίδιος χρήστης μπορεί να επανασυνδεθεί και να λάβει νέα τιμή. Ούτε το όνομα πελάτη ούτε η αναφερόμενη διεύθυνση αποτελούν επαρκή απόδειξη για την παροχή πρόσβασης.

Σχετικά άρθρα