C#中的WinDivert

时间:2014-04-25 14:35:23

标签: c# c pinvoke unmanaged-memory

我想在C#代码中调用5个WinDivert函数,第一反射: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);

我设法避免在87 = ERROR_INVALID_PARAMETER998 = ERROR_NOACCESSWinDivertOpen()的来电时出现(WinDivertClose()),(WinDivertRecv())错误,但我还是在尝试拨打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 ++中编写一个包含C调用并将其暴露给C#的COM对象更好/更容易,以避免编组和内存处理的痛苦。我应该坚持使用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会因LastWin32错误而失败:6 = INVALID_HANDLE_VALUE是不是因为GC搞乱了句柄?我试过了GC.KeepAlive(handle)并且它没有改变任何东西。

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

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

我按照以下步骤操作:

  1. 创建C ++ / CLI库项目
  2. 创建一个包装C函数的Native C ++类
  3. 使用指向本机类实例的字段创建托管C ++ / CLI类,并包装所有非托管方法并执行必要的编组。
  4. 尝试构建==&gt;失败的是着名的LNK2019和LNK2028
  5. 我应该将WinDivert DLL添加为引用还是仅添加WinDivert.h?内核驱动程序.sys文件怎么样? 说实话,它并不比PInvoke容易,但更糟糕的是,我仍然必须对非blittable数据类型和struct / enum定义进行相同的编组操作!

0 个答案:

没有答案