以下是一个C#实现的解决方案,用于C#实现Windows系统远程桌面3389端口来访者IP地址检测,并强制断开不在白名单的非法IP地址连接,支持IPv4和IPv6地址判断,如果是IPv6地址则直接强制断开:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace TcpConnectionMonitor
{
public class TcpConnectionManager
{
[StructLayout(LayoutKind.Sequential)]
public struct MIB_TCPROW
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
}
[StructLayout(LayoutKind.Sequential)]
public struct MIB_TCP6ROW
{
public uint dwState;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] localAddr;
public uint localScopeId;
public uint localPort;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] remoteAddr;
public uint remoteScopeId;
public uint remotePort;
}
[DllImport("iphlpapi.dll", SetLastError = true)]
public static extern int SetTcpEntry(ref MIB_TCPROW pTcpRow);
[DllImport("iphlpapi.dll", SetLastError = true)]
public static extern int SetTcpEntry6(ref MIB_TCP6ROW pTcp6Row);
public static void DisconnectNonWhitelistedConnections(int targetPort, HashSet<string> whiteList)
{
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
foreach (TcpConnectionInformation connection in connections)
{
if (connection.LocalEndPoint.Port == targetPort)
{
IPAddress remoteAddress = connection.RemoteEndPoint.Address;
string remoteIp = remoteAddress.ToString();
if (remoteAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
try
{
MIB_TCP6ROW row6 = new MIB_TCP6ROW
{
dwState = 12,
localAddr = connection.LocalEndPoint.Address.GetAddressBytes(),
localPort = (uint)IPAddress.HostToNetworkOrder((short)connection.LocalEndPoint.Port),
remoteAddr = remoteAddress.GetAddressBytes(),
remotePort = (uint)IPAddress.HostToNetworkOrder((short)connection.RemoteEndPoint.Port),
localScopeId = 0,
remoteScopeId = 0
};
int result = SetTcpEntry6(ref row6);
Console.WriteLine(result == 0 ?
$"已断开IPv6连接:{remoteIp}" :
$"IPv6断开失败(错误码:{result}):{remoteIp}");
}
catch (Exception ex)
{
Console.WriteLine($"处理IPv6连接时出错:{ex.Message}");
}
}
else if (!whiteList.Contains(remoteIp))
{
try
{
MIB_TCPROW row = new MIB_TCPROW
{
dwState = 12,
dwLocalAddr = BitConverter.ToUInt32(connection.LocalEndPoint.Address.GetAddressBytes(), 0),
dwLocalPort = (uint)IPAddress.HostToNetworkOrder((short)connection.LocalEndPoint.Port),
dwRemoteAddr = BitConverter.ToUInt32(remoteAddress.GetAddressBytes(), 0),
dwRemotePort = (uint)IPAddress.HostToNetworkOrder((short)connection.RemoteEndPoint.Port)
};
int result = SetTcpEntry(ref row);
Console.WriteLine(result == 0 ?
$"已断开IPv4连接:{remoteIp}" :
$"IPv4断开失败(错误码:{result}):{remoteIp}");
}
catch (Exception ex)
{
Console.WriteLine($"处理IPv4连接时出错:{ex.Message}");
}
}
}
}
}
}
class Program
{
static void Main(string[] args)
{
HashSet<string> whiteList = new HashSet<string>
{
"192.168.1.100",
"10.0.0.5"
};
try
{
TcpConnectionManager.DisconnectNonWhitelistedConnections(3389, whiteList);
}
catch (Exception ex)
{
Console.WriteLine($"运行时错误:{ex.Message}");
}
}
}
}
双协议支持:
新增MIB_TCP6ROW
结构体处理IPv6连接
添加SetTcpEntry6
API调用接口
智能检测逻辑:
if (remoteAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
}
else if (!whiteList.Contains(remoteIp))
{
}
增强的错误处理:
分离IPv4/IPv6的错误日志
明确显示操作结果和错误码
使用注意事项:
需要先添加System.Net.NetworkInformation
引用
白名单IP需根据实际情况修改
测试前建议改用非关键端口进行验证
生产环境应考虑添加日志记录和异常处理
权限要求:
端口范围:
支持同时监控IPv4和IPv6的3389端口
自动过滤其他端口流量
网络字节序:
自动处理IPv6地址的128位字节转换
正确转换端口号的网络字节序
结构体转换:
使用MIB_TCPROW
结构体与Windows API交互
IP地址和端口需要转换为网络字节序
白名单检查:直接使用HashSet进行快速查找
日志输出:
明确区分IPv4/IPv6操作结果
显示API调用的详细错误码
该版本实现了对IPv6连接的主动断开功能,同时优化了以下方面:
使用更精确的地址族检测逻辑
改进结构体字段的初始化方式
增强网络字节序转换的可靠性
提供更详细的运行时反馈信息
对于无法处理的连接类型(如IPv6连接在旧系统上的兼容性问题),程序会通过错误码反馈具体故障原因。
此代码会实时检测所有连接到3389端口的TCP连接,并自动断开非白名单IP的连接,有效增强RDP服务的安全性。
其他注意事项:
1、HashSet 白名单地址维护方法参见:http://29753.oa22.cn
2、Windows系统的远程桌面端口是允许更改的,所以更稳妥的做法是要先读取Windows系统远程桌面的服务端口,以下是获取远程桌面服务端口的代码:
string PortNumber = "";
RegistryKey localKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32);
try
{
RegistryKey rk_Winlogon = localKey.OpenSubKey(@"System\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\Tcp", true);
if (rk_Winlogon != null)
{
foreach (string vname in rk_Winlogon.GetValueNames())
{
if (vname == "PortNumber")
{
PortNumber = rk_Winlogon.GetValue("PortNumber").ToString();
break;
}
}
rk_Winlogon.Close();
}
}
catch (Exception) { }
该文章在 2025/3/13 12:46:51 编辑过