یک نشست RDP به نشانی کلاینت نیاز دارد و منابع بدیهی با هم توافق ندارند. CLIENTNAME یک نام رایانه می‌دهد، رجیستری مقادیر نشست‌های دیگر را نگه می‌دارد و همتای سوکت می‌تواند نشانی دروازه یا NAT باشد نه کلاینت. ویندوز واقعاً برای هر نشست یک نشانی کلاینت گزارش‌شده فراهم می‌کند، اما باید نشست درست را بخواهید و پاسخ را به‌درستی رمزگشایی کنید.

جست‌وجو را به یک نشست گره بزنید

با نشست شروع کنید، نه با شخص. یک میزبان RDP ویندوز ممکن است چندین نشست را برای یک حساب داشته باشد و نشانی‌ای که پیش از اتصال دوباره دیده شده ممکن است دیگر با پیوند زنده نخواند. شناسه نشست، کاربر و زمان مشاهده را با هم ثبت کنید. شناسه نشست همان کلیدی است که رابط انتظار دارد و همان مقداری است که مانع سردرگمی دو نشست می‌شود.

در میزبان، ابزارهای نصب‌شدهٔ نشست آنچه وجود دارد را فهرست می‌کنند؛ از نمای مدیریتی فقط زمانی استفاده کنید که به آن مجاز هستید. شناسه انتخاب‌شده را درست پیش از پرس‌وجو تأیید کنید، چون شناسه‌ای که دوباره به کار رفته است داده‌های نشست دیگری را برمی‌گرداند.

qwinsta

WTSQuerySessionInformationW را برای همان شناسه فراخوانی کنید

رابط Terminal Services با کلاس اطلاعات 14 به پرسش پاسخ می‌دهد، WTSClientAddress. یک بسته‌بند کوچک ctypes در ویندوز کافی است و از خواندن hive کاربر دیگر پرهیز می‌کند. کد زیر شناسه نشست صریح می‌گیرد و از میزبان محلی می‌پرسد.

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 نیاز دارد، پس ابزار را زیر حسابی اجرا کنید که آن مجوز را گرفته است، نه اینکه موردی دسترسی را بالا ببرید. بافر برگشتی از آنِ رابط است و باید آزاد شود حتی وقتی رمزگشایی شکست می‌خورد.

خانواده را رمزگشایی کنید و سپس بافر را آزاد کنید

ساختار یک رشتهٔ IP نیست. میدان خانوادهٔ آن تعیین می‌کند بیست بایت نشانی چگونه خوانده شود: IPv4 از افست دو آغاز می‌شود و IPv6 از افست صفر. نشانی نامشخص یعنی کلاینت هیچ نشانی‌ای گزارش نکرده است و این با رشتهٔ خالی یکی نیست. اتصال‌های دروازهٔ دسکتاپ راه دور به‌عنوان حالتی مستند شده‌اند که نشانی در آن در دسترس نیست، پس نشستی که از دروازه می‌گذرد چیزی سودمند دربارهٔ نشانی واقعی کلاینت گزارش نمی‌کند. آنگاه همبستگی باید به سوابق خود دروازه منتقل شود.

هنگام اشکال‌زدایی خانواده را کنار نشانی چاپ کنید، چون ناسازگاری میان این دو علت معمول یک مقدار باورپذیر اما نادرست است. بافر را با WTSFreeMemory در بلوک finally آزاد کنید تا استثنا آن را نشت ندهد.

نشانی گزارش‌شده را از اثبات جدا کنید

نشانی کلاینت گزارش‌شده بیانیه‌ای است که کلاینت ارائه می‌دهد، نه مشاهدهٔ قابل اعتماد. این همتای TCP نیست، جدول اجارهٔ دروازهٔ VPN نیست و نشانی عمومی کلاینت هم نیست. وقتی پرسش واقعی این است که کدام اجارهٔ VPN به یک شخص تعلق دارد، هم‌زمان سابقهٔ نشست احرازشدهٔ سرور VPN را همبسته کنید. وقتی پرسش این است که کدام منبع به شنونده رسیده است، جدول اتصال‌ها یا گزارش‌های مجاز سمت سرور را بخوانید.

این مقادیر را در هر ابزار پشتیبانی جداگانه برچسب بزنید. نتیجهٔ غایب WTS باید غایب بماند نه اینکه به یک مقدار دلخواه رجیستری از نشست دیگر بازگردد. شناسهٔ نشست را کنار نشانی ثبت کنید، چون همان کاربر می‌تواند دوباره متصل شود و مقدار تازه بگیرد. نه نام کلاینت و نه نشانی گزارش‌شده هیچ‌یک شواهد کافی برای دادن دسترسی نیست.

مقالات مرتبط