?以下是一個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 編輯過