C#中的WinDivert

我想在C#代码中调用5个WinDivert函数,第一reflection:PInvoke这里是签名:

internal enum WINDIVERT_LAYER { WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */ WINDIVERT_LAYER_NETWORK_FORWARD = 1 /* Network layer (forwarded packets) */ } internal const UInt64 WINDIVERT_FLAG_SNIFF = 1; /* * typedef struct { UINT32 IfIdx; UINT32 SubIfIdx; UINT8 Direction; } WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS; * */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct WINDIVERT_ADDRESS { UInt32 IfIdx; UInt32 SubIfIdx; byte Direction; } /* * typedef struct { UINT8 HdrLength:4; UINT8 Version:4; UINT8 TOS; UINT16 Length; UINT16 Id; UINT16 FragOff0; UINT8 TTL; UINT8 Protocol; UINT16 Checksum; UINT32 SrcAddr; UINT32 DstAddr; } WINDIVERT_IPHDR, *PWINDIVERT_IPHDR; * */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct WINDIVERT_IPHDR { byte HdrLengthAndVersion; byte TOS; UInt16 Length; UInt16 Id; UInt16 FragOff0; byte TTL; byte Protocol; UInt16 Checksum; UInt32 SrcAddr; UInt32 DstAddr; } /* * typedef struct { UINT16 SrcPort; UINT16 DstPort; UINT16 Length; UINT16 Checksum; } WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR; * */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct WINDIVERT_UDPHDR { UInt16 SrcPort; UInt16 DstPort; UInt16 Length; UInt16 Checksum; } /* * HANDLE WinDivertOpen( __in const char *filter, __in WINDIVERT_LAYER layer, __in INT16 priority, __in UINT64 flags ); * */ [DllImport("WinDivert.dll", EntryPoint = "WinDivertOpen", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr WinDivertOpen( [MarshalAs(UnmanagedType.LPStr)] string filter, WINDIVERT_LAYER layer, Int16 priority, UInt64 flags); /* * BOOL WinDivertClose( __in HANDLE handle ); * */ [DllImport("WinDivert.dll", EntryPoint = "WinDivertClose", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return:MarshalAs(UnmanagedType.Bool)] internal static extern bool WinDivertClose(IntPtr handle); /* * BOOL WinDivertRecv( __in HANDLE handle, __out PVOID pPacket, __in UINT packetLen, __out_opt PWINDIVERT_ADDRESS pAddr, __out_opt UINT *recvLen ); * */ [DllImport("WinDivert.dll", EntryPoint = "WinDivertRecv", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool WinDivertRecv(IntPtr handle, out IntPtr pPacket, uint packetLen, [Optional] out IntPtr pAddr, [Optional] out uint readLen); /* * BOOL WinDivertHelperParsePacket( __in PVOID pPacket, __in UINT packetLen, __out_opt PWINDIVERT_IPHDR *ppIpHdr, __out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr, __out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr, __out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, __out_opt PWINDIVERT_TCPHDR *ppTcpHdr, __out_opt PWINDIVERT_UDPHDR *ppUdpHdr, __out_opt PVOID *ppData, __out_opt UINT *pDataLen ); * */ [DllImport("WinDivert.dll", EntryPoint = "WinDivertHelperParsePacket", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool WinDivertHelperParsePacket(IntPtr pPacket, uint packetLen, [Optional] out IntPtr ppIpHdr, [Optional] out IntPtr ppIpv6Hdr, [Optional] out IntPtr ppIcmpHdr, [Optional] out IntPtr ppTcpHdr, [Optional] out IntPtr ppUdpHdr, [Optional] out IntPtr ppData, [Optional]out uint pDataLen); /* * BOOL WinDivertSend( __in HANDLE handle, __in PVOID pPacket, __in UINT packetLen, __in PWINDIVERT_ADDRESS pAddr, __out_opt UINT *sendLen ); * */ [DllImport("WinDivert.dll", EntryPoint = "WinDivertSend", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool WinDivertSend(IntPtr handle, IntPtr pPacket, uint packetLen, IntPtr pAddr, [Optional] out uint sendLen); 

我已经设法避免调用WinDivertOpen()WinDivertClose()WinDivertRecv()出现( 87 = ERROR_INVALID_PARAMETER ),( 998 = ERROR_NOACCESS )错误,但我仍然收到System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory has been corrupted System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory has been corrupted在尝试调用WinDivertHelperParsePacket()System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory has been corrupted并且(998 = ERROR_NOACCESS) WinDivertHelperParsePacket()

这是代码:

 static void Main(string[] args) { const uint MAXBUF = 0xFFFF; IntPtr handle; IntPtr addr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_ADDRESS))); IntPtr packet = Marshal.AllocHGlobal((int)MAXBUF); uint packetLen; IntPtr ip_header = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_IPHDR))); IntPtr udp_header = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_UDPHDR))); IntPtr payload; uint payload_len; uint sendLen; IntPtr opt_param = IntPtr.Zero; byte[] managedPacket; IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); handle = NativeMethods.WinDivertOpen("udp.DstPort == 53", NativeMethods.WINDIVERT_LAYER.WINDIVERT_LAYER_NETWORK, 404, NativeMethods.WINDIVERT_FLAG_SNIFF); if (handle == INVALID_HANDLE_VALUE) Console.WriteLine("open error:" + Marshal.GetLastWin32Error()); else { while (true) { if (!NativeMethods.WinDivertRecv(handle, out packet, MAXBUF, out addr, out packetLen)) { Console.WriteLine("Recv error:" + Marshal.GetLastWin32Error()); continue; } try { managedPacket = new byte[(int)packetLen]; Marshal.Copy(packet, managedPacket, 0, (int)packetLen); // causes AccessViolationException Console.WriteLine("---------------------------------"); /*for (int i = 0; i < packetLen; i++) { Console.Write("{0:X}", managedPacket[i]); }*/ Console.WriteLine("---------------------------------"); } catch(Exception ex) { Console.WriteLine("copy error :" + ex.Message); } if (!NativeMethods.WinDivertHelperParsePacket(packet, packetLen, out ip_header, out opt_param, out opt_param, out opt_param, out udp_header, out payload, out payload_len)) // causes AccessViolationException { Console.WriteLine("Parse error:" + Marshal.GetLastWin32Error()); //continue; } if (!NativeMethods.WinDivertSend(handle, packet, packetLen, addr, out sendLen)) { Console.WriteLine("Send error:" + Marshal.GetLastWin32Error()); continue; } } /*if (!NativeMethods.WinDivertClose(handle)) Console.WriteLine("close error:" + Marshal.GetLastWin32Error());*/ } Console.ReadKey(); } 

我的老板告诉我,在C ++中编写COM对象更好/更容易,它包装C调用并将其暴露给C#以避免编组和内存处理的痛苦。 我应该坚持使用PInvoke还是采用COM方式?

编辑:更新

我尝试了两种不同的分配非托管内存的方法,但都失败了(允许使用不安全的代码):

  byte[] managedPacket = new byte[(int)packetLen]; NativeMethods.WINDIVERT_ADDRESS windivertAddr = new NativeMethods.WINDIVERT_ADDRESS(); GCHandle managedPacketHandle = GCHandle.Alloc(managedPacket, GCHandleType.Pinned); IntPtr managedPacketPointer = managedPacketHandle.AddrOfPinnedObject(); GCHandle windivertAddrHandle = GCHandle.Alloc(windivertAddr, GCHandleType.Pinned); IntPtr windivertAddrPointer = managedPacketHandle.AddrOfPinnedObject(); NativeMethods.WinDivertRecv(handle, out managedPacketPointer, (uint)(packetLen * Marshal.SizeOf(typeof(System.Byte))), out windivertAddrPointer , out readLen); // output of managed array and struct fields = 0 and it still causes unhandled AccessViolationException even inside a try/catch managedPacketHandle.Free(); windivertAddrPointer.Free(); 

并且:

 IntPtr packet = Marshal.AllocHGlobal((int)packetLen * Marshal.SizeOf(typeof(System.Byte))); IntPtr addr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_ADDRESS))); NativeMethods.WinDivertRecv(handle, out packet, (uint)(packetLen * Marshal.SizeOf(typeof(System.Byte))), out addr, out readLen) byte[] managedPacket = new byte[(int)packetLen]; Marshal.Copy(packet, managedPacket, 0, (int)readLen); NativeMethods.WINDIVERT_ADDRESS windivertAddr = (NativeMethods.WINDIVERT_ADDRESS)Marshal.PtrToStructure(addr, typeof(NativeMethods.WINDIVERT_ADDRESS)); // no output of managed array and struct fields and the same unhandled AccessViolationException Marshal.FreeHGlobal(addr); Marshal.FreeHGlobal(packet); 

有时在循环中,WinDivRecv会因LastWin32Error而失败: 6 = INVALID_HANDLE_VALUE是因为GC正在处理句柄? 我尝试了GC.KeepAlive(handle)并没有改变任何东西。

C ++ \ CLI包装器:(非托管C DLL和托管C#代码之间的桥接)

[以下评论中的建议选项]

我按照以下步骤操作:

  1. 创建C ++ / CLI库项目
  2. 创建一个包装C函数的Native C ++类
  3. 使用指向本机类实例的字段创建托管C ++ / CLI类,并包装所有非托管方法并执行必要的编组操作。
  4. 尝试使用着名的LNK2019和LNK2028构建==>失败

我应该将WinDivert DLL添加为引用还是仅添加WinDivert.h? 内核驱动程序.sys文件怎么样? 老实说,它并不比PInvoke容易,但更糟糕的是,我仍然需要对非blittable数据类型和struct / enum定义进行相同的编组操作!