1

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_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();
    }

私の上司は、マーシャリングとメモリ処理の苦痛を避けるために、C 呼び出しをラップして C# に公開する C++ で COM オブジェクトを作成する方が良い/簡単だと言いました。PInvoke に固執する必要がありますか、それとも COM の方法を使用する必要がありますか?

編集:更新

アンマネージ メモリを割り当てる 2 つの異なる方法を試しましたが、どちらも失敗しました (安全でないコードが許可されています)。

 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 よりも簡単ではありませんが、さらに悪いことに、blittable でないデータ型と struct/enum 定義の同じマーシャリングを行う必要があります。

4

0 に答える 0