WinDivert в C#

Я хочу вызвать 5 функций WinDivert в коде C#, первый рефлекс: 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_PARAMETER), (998 = ERROR_NOACCESS) ошибки при звонках на WinDivertOpen(), 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();
    }

Мой начальник сказал мне, что лучше / проще написать COM-объект на C++, который оборачивает вызовы 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++, который оборачивает функции C
  3. Создайте управляемый класс C++/CLI с полем, указывающим на экземпляр нативного класса, оберните все неуправляемые методы и выполните необходимое сортирование.
  4. Попробуй построить ==> не получится с известными LNK2019 и LNK2028

Должен ли я добавить WinDivert DLL в качестве ссылки или только WinDivert.h? А как насчет файлов драйвера.sys? И, честно говоря, это не проще, чем PInvoke, но намного хуже, мне все еще нужно выполнить такое же упорядочение неблизких типов данных и определений struct/enum!

0 ответов

Другие вопросы по тегам