Які адрас Windows паведамляе для сеанса RDP?
WTS API вяртае паведамлены адрас кліента для аднаго сеанса RDP, а рэестр — не. Запытайце правільны ID сеанса і правільна дэкадуйце IPv4 або IPv6.
Сеансу RDP патрэбны адрас кліента, і відавочныя крыніцы не ўзгадняюцца. CLIENTNAME дае імя камп’ютара, рэестр захоўвае значэнні з іншых сеансаў, а суразмоўца сокета можа быць адрасам шлюза ці NAT, а не кліента. Windows усё ж паведамляе адрас кліента для кожнага сеанса, але трэба запытаць менавіта патрэбны сеанс і правільна дэкадаваць адказ.
Прывяжыце пошук да аднаго сеанса
Пачынайце з сеанса, а не з чалавека. Хост RDP пад Windows можа трымаць некалькі сеансаў для аднаго ўліковага запісу, і адрас, заўважаны да перападлучэння, можа ўжо не адпавядаць жывому злучэнню. Запішыце ID сеанса, карыстальніка і час назірання разам. ID сеанса — гэта ключ, якога чакае API, і менавіта ён не дае зблытаць два сеансы.
На хосце ўсталяваныя сродкі сеансаў пералічваюць тое, што існуе; карыстайцеся адміністрацыйным выглядам толькі тады, калі маеце на яго права. Пацвердзіце абраны ID непасрэдна перад запытам, бо паўторна выкарыстаны ID вяртае даныя іншага сеанса.
qwinsta
Выклічце WTSQuerySessionInformationW для гэтага ID
API службаў тэрміналаў адказвае на пытанне класам інфармацыі 14, WTSClientAddress. Невялікая абгортка ctypes дастатковая ў Windows, і яна пазбягае чытання куста іншага карыстальніка. Код ніжэй прымае яўны ID сеанса і запытвае лакальны хост.
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)
Перадайце цэлалікавы ID сеанса, які вы толькі што праверылі, і захавайце нулявы дэскрыптар сервера для лакальнай машыны. Стаўцеся да няўдалага выкліку як да памылкі, а не як да адсутнага адраса, бо гэта вядзе да розных высноў. Запыт сеанса іншага карыстальніка патрабуе адпаведнага дазволу Query Information, таму запускайце сродак пад уліковым запісам, якому гэты дазвол дадзены, замест разовага павышэння правоў. Вернуты буфер належыць API і павінен быць вызвалены нават калі дэкадаванне не ўдалося.
Дэкадуйце сямейства, затым вызваліце буфер
Структура — не радок IP. Яе поле сямейства вызначае, як чытаць дваццаць байтаў адраса: IPv4 пачынаецца са зруху два, а IPv6 — са зруху нуль. Нявызначаны адрас азначае, што кліент яго не паведаміў, і гэта не тое самае, што пусты радок. Злучэнні праз Remote Desktop Gateway апісаны як выпадак, калі адрас недаступны, таму сеанс праз шлюз не паведамляе нічога карыснага пра рэальны адрас кліента. Тады суаднясенне трэба перанесці на ўласныя запісы шлюза.
Друкуйце сямейства побач з адрасам падчас адладкі, бо неадпаведнасць паміж імі — звычайная прычына праўдападобнага, але няправільнага значэння. Вызваліце буфер з дапамогай WTSFreeMemory у блоку finally, каб выключэнне не выклікала ўцечку.
Аддзяліце паведамлены адрас ад доказаў
Паведамлены адрас кліента — гэта заява, якую дае кліент, а не даверанае назіранне. Гэта не суразмоўца TCP, не табліца арэнды VPN-шлюза і не публічны адрас кліента. Калі сапраўднае пытанне ў тым, якая арэнда VPN належыць чалавеку, суаднясіце аўтэнтыфікаваны запіс сеанса VPN-сервера ў той жа час. Калі пытанне ў тым, якая крыніца дасягнула слухача, чытайце табліцу злучэнняў або дазволеныя серверныя журналы.
Трымайце гэтыя значэнні асобна пазначанымі ў любой дапаможнай прыладзе. Адсутны вынік WTS павінен заставацца адсутным, а не падмяняцца адвольным рэестравым значэннем з іншага сеанса. Запісвайце ID сеанса побач з адрасам, бо той самы карыстальнік можа перападключыцца і атрымаць новае значэнне. Ні імя кліента, ні паведамлены адрас не з’яўляюцца дастатковым доказам для выдачы доступу.