Files
NetVizor/WinDivertNet/WinDivertWrapper/WinDivert.cs
T
jimmy 2ba20f8e8a U: 更新WinDivert函数封装并调整参数类型,增强错误处理和稳定性
A: 添加SafeRecv等安全调用封装,优化数据包接收逻辑
A: 提供Win32错误代码的详细解析打印
U: 更新数据包解析函数的实现,调整pDataLen类型
A: 新增PacketHeaders的ToString方法,用于调试输出
U: 重构PacketSniffer,优化IPv4/IPv6数据包解析及日志输出
2025-05-19 22:39:22 +08:00

663 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace WinDivertNet.WinDivertWrapper;
using System;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// WinDivert 的 C# 封装类
/// </summary>
public static class WinDivert
{
#region 常量定义
// 层级常量
public const uint WINDIVERT_LAYER_NETWORK = 0;
public const uint WINDIVERT_LAYER_NETWORK_FORWARD = 1;
public const uint WINDIVERT_LAYER_FLOW = 2;
public const uint WINDIVERT_LAYER_SOCKET = 3;
public const uint WINDIVERT_LAYER_REFLECT = 4;
// 标志常量
public const ulong WINDIVERT_FLAG_SNIFF = 1;
public const ulong WINDIVERT_FLAG_DROP = 2;
public const ulong WINDIVERT_FLAG_RECV_ONLY = 4;
public const ulong WINDIVERT_FLAG_SEND_ONLY = 8;
public const ulong WINDIVERT_FLAG_NO_INSTALL = 16;
public const ulong WINDIVERT_FLAG_FRAGMENTS = 32;
public const ulong WINDIVERT_FLAG_RECV_PARTIAL = 64;
public const ulong WINDIVERT_FLAG_SEND_PARTIAL = 128;
// 事件常量
public const byte WINDIVERT_EVENT_NETWORK_PACKET = 0;
public const byte WINDIVERT_EVENT_FLOW_ESTABLISHED = 1;
public const byte WINDIVERT_EVENT_FLOW_DELETED = 2;
public const byte WINDIVERT_EVENT_SOCKET_BIND = 3;
public const byte WINDIVERT_EVENT_SOCKET_CONNECT = 4;
public const byte WINDIVERT_EVENT_SOCKET_LISTEN = 5;
public const byte WINDIVERT_EVENT_SOCKET_ACCEPT = 6;
public const byte WINDIVERT_EVENT_REFLECT_OPEN = 7;
public const byte WINDIVERT_EVENT_REFLECT_CLOSE = 8;
// 方向常量
public const byte WINDIVERT_DIRECTION_OUTBOUND = 0;
public const byte WINDIVERT_DIRECTION_INBOUND = 1;
// 参数常量
public const int WINDIVERT_PARAM_QUEUE_LENGTH = 0;
public const int WINDIVERT_PARAM_QUEUE_TIME = 1;
public const int WINDIVERT_PARAM_QUEUE_SIZE = 2;
public const int WINDIVERT_PARAM_VERSION_MAJOR = 3;
public const int WINDIVERT_PARAM_VERSION_MINOR = 4;
// 校验和常量
public const uint WINDIVERT_HELPER_NO_REPLACE = 0;
public const uint WINDIVERT_HELPER_NO_ICMPV6 = 0;
public const uint WINDIVERT_HELPER_NO_ICMP = 0;
public const uint WINDIVERT_HELPER_NO_TCP = 0;
public const uint WINDIVERT_HELPER_NO_UDP = 0;
public const uint WINDIVERT_HELPER_NO_IP = 0;
public const uint WINDIVERT_HELPER_NO_IPV6 = 0;
// 优先级范围
public const short WINDIVERT_PRIORITY_HIGHEST = 30000;
public const short WINDIVERT_PRIORITY_LOWEST = -30000;
public const short WINDIVERT_PRIORITY_DEFAULT = 0;
#endregion
#region 结构体定义
/// <summary>
/// WinDivert 地址结构
/// </summary>
// [StructLayout(LayoutKind.Sequential)]
// public struct WINDIVERT_ADDRESS
// {
// public long Timestamp; // 时间戳
// public byte Layer; // 层级
// public byte Event; // 事件
// public byte Flags; // 标志
// public byte Reserved1; // 保留
// public uint IfIdx; // 接口索引
// public uint SubIfIdx; // 子接口索引
// public byte Direction; // 方向
// public byte Reserved2; // 保留
// public ulong Reserved3; // 保留
// }
// 确保结构体布局正确 - 使用StructLayout和FieldOffset
// 完全匹配WinDivert 2.0版本的结构体定义
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_ADDRESS
{
public long Timestamp;
public byte Layer;
public byte Event;
public ushort Flags;
public uint IfIdx;
public uint SubIfIdx;
// 注意:在WinDivert 2.0中,Reserved字段是64位(8字节)对齐的
// 所以我们使用固定大小的字节数组
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] Reserved;
}
/// <summary>
/// IPv4 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_IPHDR
{
public byte HdrLength4; // 组合字段: 版本 (4 bits) + 头长度 (4 bits)
public byte TOS; // 服务类型
public ushort Length; // 总长度
public ushort Id; // 标识
public ushort FragOff0; // 组合字段: 标志 (3 bits) + 分片偏移 (13 bits)
public byte TTL; // 生存时间
public byte Protocol; // 协议
public ushort Checksum; // 检验和
public uint SrcAddr; // 源地址
public uint DstAddr; // 目标地址
}
/// <summary>
/// IPv6 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_IPV6HDR
{
public uint FlowLabel0; // 组合字段: 版本 (4 bits) + 通信类 (8 bits) + 流标签 (20 bits)
public ushort Length; // 有效载荷长度
public byte NextHdr; // 下一个头
public byte HopLimit; // 跳数限制
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] SrcAddr; // 源地址
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] DstAddr; // 目标地址
}
/// <summary>
/// TCP 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_TCPHDR
{
public ushort SrcPort; // 源端口
public ushort DstPort; // 目标端口
public uint SeqNum; // 序列号
public uint AckNum; // 确认号
public ushort Reserved10; // 组合字段: 数据偏移 (4 bits) + 保留 (6 bits) + 标志 (6 bits)
public ushort Window; // 窗口大小
public ushort Checksum; // 检验和
public ushort UrgPtr; // 紧急指针
}
/// <summary>
/// UDP 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_UDPHDR
{
public ushort SrcPort; // 源端口
public ushort DstPort; // 目标端口
public ushort Length; // 长度
public ushort Checksum; // 检验和
}
/// <summary>
/// ICMP 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_ICMPHDR
{
public byte Type; // 类型
public byte Code; // 代码
public ushort Checksum; // 检验和
public uint Body; // 消息体
}
/// <summary>
/// ICMPv6 头结构
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WINDIVERT_ICMPV6HDR
{
public byte Type; // 类型
public byte Code; // 代码
public ushort Checksum; // 检验和
public uint Body; // 消息体
}
#endregion
#region 原生 API 导入
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern IntPtr WinDivertOpen(
[MarshalAs(UnmanagedType.LPStr)] string filter,
uint layer,
short priority,
ulong flags);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool WinDivertRecv(
IntPtr handle,
[Out] byte[] pPacket,
uint packetLen,
ref WINDIVERT_ADDRESS pAddr,
ref uint readLen);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool WinDivertRecvEx(
IntPtr handle,
[Out] byte[] pPacket,
uint packetLen,
uint recvFlags,
[In, Out] ref WINDIVERT_ADDRESS pAddr,
[In, Out] ref uint readLen,
IntPtr lpOverlapped);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertSend(
IntPtr handle,
byte[] pPacket,
uint packetLen,
ref WINDIVERT_ADDRESS pAddr,
ref uint writeLen);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertSendEx(
IntPtr handle,
byte[] pPacket,
uint packetLen,
uint flags,
ref WINDIVERT_ADDRESS pAddr,
ref uint writeLen,
IntPtr overlapped);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool WinDivertClose(
IntPtr handle);
// 获取错误信息的帮助函数
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint WinDivertHelperNtohs(ushort value);
// 获取WinDivert特定错误
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr WinDivertHelperFormatFilter(
[MarshalAs(UnmanagedType.LPStr)] string filter,
WinDivertFormatFlags formatFlags,
IntPtr buffer,
uint bufferLen);
// 用于格式化过滤器的标志
[Flags]
public enum WinDivertFormatFlags : uint
{
Normal = 0,
NoNumbers = 1,
Aliases = 2,
}
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertSetParam(
IntPtr handle,
int param,
ulong value);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertGetParam(
IntPtr handle,
int param,
ref ulong value);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint WinDivertHelperCalcChecksums(
byte[] pPacket,
uint packetLen,
ref WINDIVERT_ADDRESS pAddr,
uint flags);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperParsePacket(
byte[] pPacket,
uint packetLen,
out IntPtr ppIpHdr,
out IntPtr ppIpv6Hdr,
out IntPtr ppIcmpHdr,
out IntPtr ppIcmpv6Hdr,
out IntPtr ppTcpHdr,
out IntPtr ppUdpHdr,
out IntPtr ppData,
out ushort pDataLen
);
// 安全包装 - 处理异常大小问题
public static bool SafeRecv(IntPtr handle, byte[] buffer, ref WINDIVERT_ADDRESS addr, out byte[] packet)
{
packet = null;
// 确保结构体正确初始化
if (addr.Reserved == null)
{
addr.Reserved = new byte[8];
}
uint readLen = 0;
// 使用基础函数调用
bool result = WinDivertRecv(handle, buffer, (uint)buffer.Length, ref addr, ref readLen);
if (!result) return false;
// *** 关键修复:处理异常大小 ***
// 通过数据包的第一个字节验证它是IPv4还是IPv6数据包
if (buffer.Length > 0)
{
byte version = (byte)(buffer[0] >> 4);
// IPv4数据包版本是4,IPv6是6
if (version == 4 || version == 6)
{
// 检查IP头部中的长度字段
// 对于IPv4,长度在字节3-4中;对于IPv6,长度在字节5-6中
ushort ipLength;
if (version == 4 && buffer.Length >= 4)
{
ipLength = (ushort)((buffer[2] << 8) | buffer[3]);
// IPv4报头最小20字节,最大65535字节
if (ipLength >= 20 && ipLength <= 65535)
{
readLen = ipLength;
}
else if (buffer.Length >= 20) // 如果长度字段无效,至少读取IPv4头
{
readLen = 1500; // 使用标准以太网MTU大小
}
}
else if (version == 6 && buffer.Length >= 6)
{
ipLength = (ushort)((buffer[4] << 8) | buffer[5]);
// IPv6报头固定40字节,加上有效载荷
if (ipLength + 40 <= 65535)
{
readLen = (uint)(ipLength + 40);
}
else if (buffer.Length >= 40) // 如果长度字段无效,至少读取IPv6头
{
readLen = 1500; // 使用标准以太网MTU大小
}
}
}
// 如果仍然出现异常大小,强制使用安全值
if (readLen > 9000 || readLen == 0)
{
// 设置为安全的默认值(标准以太网MTU)
readLen = 1500;
}
}
// 只提取有效数据
packet = new byte[readLen];
Array.Copy(buffer, packet, readLen);
return true;
}
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperParseIPv4Address(
[MarshalAs(UnmanagedType.LPStr)] string str,
ref uint pAddr);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperParseIPv6Address(
[MarshalAs(UnmanagedType.LPStr)] string str,
byte[] pAddr);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperFormatIPv4Address(
uint addr,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
uint bufLen);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperFormatIPv6Address(
byte[] addr,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer,
uint bufLen);
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperCheckFilter(
[MarshalAs(UnmanagedType.LPStr)] string filter,
uint layer,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder errorStr,
uint errorStrLen);
#endregion
#region 辅助方法
/// <summary>
/// 打开 WinDivert 句柄
/// </summary>
/// <param name="filter">过滤器表达式</param>
/// <param name="layer">层级</param>
/// <param name="priority">优先级</param>
/// <param name="flags">标志</param>
/// <returns>WinDivert 句柄</returns>
public static IntPtr Open(string filter, uint layer, short priority, ulong flags) =>
WinDivertOpen(filter, layer, priority, flags);
/// <summary>
/// 接收数据包
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="addr">地址结构</param>
/// <param name="readLen">实际读取长度</param>
/// <returns>是否成功</returns>
public static bool Recv(IntPtr handle, byte[] buffer, uint bufferLength, ref WINDIVERT_ADDRESS addr,
ref uint readLen)
{
// 明确初始化readLen为0
readLen = 0;
// 确保WINDIVERT_ADDRESS结构体初始化正确
if (addr.Reserved == null)
{
addr.Reserved = new byte[8];
}
return WinDivertRecv(handle, buffer, bufferLength, ref addr, ref readLen);
}
/// <summary>
/// 扩展接收数据包
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="flags">标志</param>
/// <param name="addr">地址结构</param>
/// <param name="readLen">实际读取长度</param>
/// <param name="overlapped">重叠 I/O 结构</param>
/// <returns>是否成功</returns>
// 更安全的Recv包装
public static bool RecvEx(IntPtr handle, byte[] buffer, ref WINDIVERT_ADDRESS addr, ref uint readLen)
{
// 确保结构体正确初始化
if (addr.Reserved == null)
addr.Reserved = new byte[8];
// 清零readLen
readLen = 0;
// 使用Ex版本,提供更多控制和稳定性
return WinDivertRecvEx(
handle,
buffer,
(uint)buffer.Length,
0, // 默认接收标志
ref addr,
ref readLen,
IntPtr.Zero // 非异步调用
);
}
/// <summary>
/// 发送数据包
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="addr">地址结构</param>
/// <param name="writeLen">实际写入长度</param>
/// <returns>是否成功</returns>
public static bool Send(IntPtr handle, byte[] buffer, uint len, ref WINDIVERT_ADDRESS addr, ref uint writeLen) =>
WinDivertSend(handle, buffer, len, ref addr, ref writeLen);
/// <summary>
/// 扩展发送数据包
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="flags">标志</param>
/// <param name="addr">地址结构</param>
/// <param name="writeLen">实际写入长度</param>
/// <param name="overlapped">重叠 I/O 结构</param>
/// <returns>是否成功</returns>
public static bool SendEx(IntPtr handle, byte[] buffer, uint len, uint flags, ref WINDIVERT_ADDRESS addr,
ref uint writeLen, IntPtr overlapped) =>
WinDivertSendEx(handle, buffer, len, flags, ref addr, ref writeLen, overlapped);
/// <summary>
/// 关闭 WinDivert 句柄
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <returns>是否成功</returns>
public static bool Close(IntPtr handle) =>
WinDivertClose(handle);
/// <summary>
/// 设置参数
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="param">参数类型</param>
/// <param name="value">参数值</param>
/// <returns>是否成功</returns>
public static bool SetParam(IntPtr handle, int param, ulong value) =>
WinDivertSetParam(handle, param, value);
/// <summary>
/// 获取参数
/// </summary>
/// <param name="handle">WinDivert 句柄</param>
/// <param name="param">参数类型</param>
/// <param name="value">参数值</param>
/// <returns>是否成功</returns>
public static bool GetParam(IntPtr handle, int param, ref ulong value) =>
WinDivertGetParam(handle, param, ref value);
/// <summary>
/// 计算校验和
/// </summary>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="addr">地址结构</param>
/// <param name="flags">标志</param>
/// <returns>校验和结果</returns>
public static uint CalcChecksums(byte[] buffer, uint len, ref WINDIVERT_ADDRESS addr, uint flags) =>
WinDivertHelperCalcChecksums(buffer, len, ref addr, flags);
/// <summary>
/// 解析数据包【传递至此的 buffer 必须是新的!必须是空的!不能是用过的!】
/// </summary>
/// <param name="buffer">数据缓冲区</param>
/// <param name="len">缓冲区长度</param>
/// <param name="ppIpHdr">IPv4 头指针</param>
/// <param name="ppIpv6Hdr">IPv6 头指针</param>
/// <param name="ppIcmpHdr">ICMP 头指针</param>
/// <param name="ppIcmpv6Hdr">ICMPv6 头指针</param>
/// <param name="ppTcpHdr">TCP 头指针</param>
/// <param name="ppUdpHdr">UDP 头指针</param>
/// <param name="ppData">数据指针</param>
/// <param name="pDataLen">数据长度</param>
/// <returns>是否成功</returns>
public static bool ParsePacket(
byte[] buffer,
uint len,
ref IntPtr ppIpHdr,
ref IntPtr ppIpv6Hdr,
ref IntPtr ppIcmpHdr,
ref IntPtr ppIcmpv6Hdr,
ref IntPtr ppTcpHdr,
ref IntPtr ppUdpHdr,
ref IntPtr ppData,
ref ushort pDataLen
)
{
return WinDivertHelperParsePacket(
buffer,
len,
out ppIpHdr,
out ppIpv6Hdr,
out ppIcmpHdr,
out ppIcmpv6Hdr,
out ppTcpHdr,
out ppUdpHdr,
out ppData,
out pDataLen
);
}
/// <summary>
/// 解析 IPv4 地址
/// </summary>
/// <param name="str">IP 地址字符串</param>
/// <param name="pAddr">地址结果</param>
/// <returns>是否成功</returns>
public static bool ParseIPv4Address(string str, ref uint pAddr) =>
WinDivertHelperParseIPv4Address(str, ref pAddr);
/// <summary>
/// 解析 IPv6 地址
/// </summary>
/// <param name="str">IP 地址字符串</param>
/// <param name="pAddr">地址结果</param>
/// <returns>是否成功</returns>
public static bool ParseIPv6Address(string str, byte[] pAddr) =>
WinDivertHelperParseIPv6Address(str, pAddr);
/// <summary>
/// 格式化 IPv4 地址
/// </summary>
/// <param name="addr">IP 地址</param>
/// <returns>格式化后的字符串</returns>
public static string FormatIPv4Address(uint addr)
{
StringBuilder buffer = new StringBuilder(16);
if (WinDivertHelperFormatIPv4Address(addr, buffer, 16))
{
return buffer.ToString();
}
return string.Empty;
}
/// <summary>
/// 格式化 IPv6 地址
/// </summary>
/// <param name="addr">IP 地址</param>
/// <returns>格式化后的字符串</returns>
public static string FormatIPv6Address(byte[] addr)
{
StringBuilder buffer = new StringBuilder(46);
if (WinDivertHelperFormatIPv6Address(addr, buffer, 46))
{
return buffer.ToString();
}
return string.Empty;
}
/// <summary>
/// 检查过滤器语法
/// </summary>
/// <param name="filter">过滤器表达式</param>
/// <param name="layer">层级</param>
/// <param name="errorStr">错误信息</param>
/// <param name="errorStrLen">错误信息长度</param>
/// <returns>是否有效</returns>
public static bool CheckFilter(string filter, uint layer, StringBuilder errorStr, uint errorStrLen) =>
WinDivertHelperCheckFilter(filter, layer, errorStr, errorStrLen);
#endregion
[StructLayout(LayoutKind.Sequential)]
public struct PacketHeaders
{
public IntPtr IpHdr;
public IntPtr Ipv6Hdr;
public IntPtr IcmpHdr;
public IntPtr Icmpv6Hdr;
public IntPtr TcpHdr;
public IntPtr UdpHdr;
public IntPtr Data;
public uint DataLen;
}
[DllImport("WinDivert.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool WinDivertHelperParsePacketEx(
[In] byte[] pPacket,
uint packetLen,
ref PacketHeaders headers);
}