当我们读到 WTS_CLIENT_ADDRESS 结构时
客户端网络地址由 RDP 客户端自己报告,当它连接到服务器时。 这可能不同于
实际连接到服务器的地址。例如,假设
客户端和服务器之间有一个 NAT。客户可以
报自己的IP地址,但实际连接的IP地址
到服务器的是 NAT 地址。对于 VPN 连接,IP 地址
客户端可能无法发现。如果不能被发现,
客户端可以报告它拥有的唯一 IP 地址,可能是 ISP
分配的地址。因为地址可能不是实际的网络
地址,它不应该用作客户端身份验证的一种形式。
为了从服务器视图中获取实际网络地址,我们可以使用WinStationQueryInformationW 和WinStationRemoteAddress——它返回WINSTATIONREMOTEADDRESS。
您可以复制粘贴此声明的或全部winsta.h(由于未知原因未包含在 sdk 中)
首先我们可以通过布局确定WINSTATIONREMOTEADDRESS 与SOCKADDR(SOCKADDR_IN 和SOCKADDR_IN6)相同。我们可以重新解释从WINSTATIONREMOTEADDRESS到SOCKADDR的转换指针。
但这是严重错误。结构有不同的对齐方式!
C_ASSERT(FIELD_OFFSET(SOCKADDR_IN, sin_port) == 2);
C_ASSERT(FIELD_OFFSET(WINSTATIONREMOTEADDRESS, ipv4.sin_port) == 4);
C_ASSERT(FIELD_OFFSET(SOCKADDR_IN, sin_addr) == 4);
C_ASSERT(FIELD_OFFSET(WINSTATIONREMOTEADDRESS, ipv4.in_addr) == 8);
使用WinStationQueryInformationW需要与winsta.lib链接或在运行时从winsta.dll
获取
所以最后的代码可以是下一个:
typedef enum _WINSTATIONINFOCLASS {
// ...
WinStationRemoteAddress = 29,
// ...
} WINSTATIONINFOCLASS;
#define LOGONID_CURRENT ((ULONG)-1)
typedef struct {
unsigned short sin_family;
union {
struct {
USHORT sin_port;
ULONG in_addr;
UCHAR sin_zero[8];
} ipv4;
struct {
USHORT sin6_port;
ULONG sin6_flowinfo;
USHORT sin6_addr[8];
ULONG sin6_scope_id;
} ipv6;
};
} WINSTATIONREMOTEADDRESS,
*PWINSTATIONREMOTEADDRESS;
EXTERN_C
DECLSPEC_IMPORT
BOOLEAN
WINAPI
WinStationQueryInformationW(
_In_opt_ HANDLE hServer,
_In_ ULONG SessionId,
_In_ WINSTATIONINFOCLASS WinStationInformationClass,
_Out_writes_bytes_(WinStationInformationLength) PVOID pWinStationInformation,
_In_ ULONG WinStationInformationLength,
_Out_ PULONG pReturnLength
);
ULONG GetRdpClientAddressFromServerView()
{
ULONG dwError = NOERROR;
ULONG cb;
union {
SOCKADDR sa;
SOCKADDR_IN sa4;
SOCKADDR_IN6 sa6;
};
WINSTATIONREMOTEADDRESS ra;
if (WinStationQueryInformationW(0, LOGONID_CURRENT, WinStationRemoteAddress, &ra, sizeof(ra), &cb))
{
switch (sa.sa_family = ra.sin_family)
{
case AF_INET:
sa4.sin_port = ra.ipv4.sin_port;
sa4.sin_addr.S_un.S_addr = ra.ipv4.in_addr;
RtlZeroMemory(sa4.sin_zero, sizeof(sa4.sin_zero));
cb = sizeof(SOCKADDR_IN);
break;
case AF_INET6:
sa6.sin6_port = ra.ipv6.sin6_port;
sa6.sin6_flowinfo = ra.ipv6.sin6_flowinfo;
memcpy(&sa6.sin6_addr, &ra.ipv6.sin6_addr, sizeof(in6_addr));
sa6.sin6_scope_id = ra.ipv6.sin6_scope_id;
cb = sizeof(SOCKADDR_IN6);
break;
default:
dwError = ERROR_GEN_FAILURE;
}
if (dwError == NOERROR)
{
// assume that WSAStartup already called
// WSADATA wd;
// WSAStartup(WINSOCK_VERSION, &wd);
char AddressString[64];
ULONG dwAddressStringLength = _countof(AddressString);
if (WSAAddressToStringA(&sa, cb, 0, AddressString, &dwAddressStringLength) == NOERROR)
{
DbgPrint("client ip is %s\n", AddressString);
}
else
{
dwError = WSAGetLastError();
}
}
}
else
{
dwError = GetLastError();
}
return dwError;
}