basil00 / WinDivert

WinDivert: Windows Packet Divert
https://reqrypt.org/windivert.html
Other
2.55k stars 507 forks source link

Error 87 from WinDivertSendEx #172

Closed jjxtra closed 5 years ago

jjxtra commented 5 years ago

I am using C# to PInvoke into WinDivert 2.0 (downloaded from https://reqrypt.org/download/WinDivert-2.0.0-rc-A.zip). I am calling WinDivertSendEx and getting an error 87 back after receiving a 997 io pending error. The low field of the send overlapped is always a large negative number. I've tried passing a null overlapped and it immediately returns an error 87.

WinDivertRecvEx seems to be working fine.

As a side note, have you considered providing PInvoke wrappers for WinDivert? Might increase your sales a bit for commercial licenses, otherwise people are forced to use buggy NuGet packages that are not kept up to date.

Any suggestions?

Here is my code:

public class WinDivertInterop
{
    private IntPtr winDivertHandle;

    [DllImport("WinDivert.dll", EntryPoint = "WinDivertSendEx", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    [SuppressUnmanagedCodeSecurity]
    public static extern bool WinDivertSendEx([In()] IntPtr handle, [In()] IntPtr pPacket, uint packetLen, ref uint sendLen, ulong flags, [In()] IntPtr pAddr, ref uint addrLen, [In][Out] ref NativeOverlapped lpOverlapped);

    public static bool WinDivertSendEx(IntPtr handle, WinDivertBuffer packet, ref uint packetByteCount, ulong flags, IntPtr address, ref uint addressByteCount, ref NativeOverlapped overlapped)
    {
        return WinDivertSendEx(handle, packet.BufferPointer, packetByteCount, ref packetByteCount, flags, address, ref addressByteCount, ref overlapped);
    }

    private void ReadThread()
    {
        const uint addressBufferCount = 255;
        const int errorIoPending = 997;

        uint addressBufferByteCount = (uint)sizeof(WinDivertAddress) * addressBufferCount;
        uint addressByteCount = 0;
        uint packetByteCount = 0;
        uint readPacketByteCount = 0;
        uint addressCount = 0;
        IntPtr packetOffset;
        WinDivertParseResult result;
        WinDivertBuffer packet = new WinDivertBuffer();

        IntPtr addressBuffer = Marshal.AllocHGlobal((int)addressBufferByteCount);
        NativeOverlapped recvOverlapped = new NativeOverlapped();
        AutoResetEvent recvEvent = new AutoResetEvent(false);
        recvOverlapped.EventHandle = recvEvent.SafeWaitHandle.DangerousGetHandle();
        NativeOverlapped sendOverlapped = new NativeOverlapped();
        AutoResetEvent sendEvent = new AutoResetEvent(false);
        sendOverlapped.EventHandle = sendEvent.SafeWaitHandle.DangerousGetHandle();

        try
        {
            while (!Disposed)
            {
                Console.WriteLine("Read started...");
                addressByteCount = addressBufferByteCount;
                packetByteCount = packet.Length;
                if (!WinDivert.WinDivertRecvEx(winDivertHandle, packet, ref readPacketByteCount, 0, addressBuffer, ref addressByteCount, ref recvOverlapped))
                {
                    var error = Marshal.GetLastWin32Error();

                    if (error != errorIoPending)
                    {
                        Console.WriteLine(string.Format("Unknown IO error ID {0} while awaiting receive overlapped result.", error));
                        continue;
                    }

                    // wait while not disposed
                    while (!Disposed && !recvEvent.WaitOne(1000)) { }

                    // get result
                    if (!Kernel32.GetOverlappedResult(winDivertHandle, ref recvOverlapped, ref readPacketByteCount, false))
                    {
                        Console.WriteLine("Failed to get overlapped receive result: {0}", Marshal.GetLastWin32Error());
                        continue;
                    }
                }

                Console.WriteLine("Read bytes: {0}", readPacketByteCount);

                // setup the offset and length, they will get updated automatically by the API
                // the number of packets will match the number of addresses
                packetOffset = packet.BufferPointer;
                packetByteCount = packet.Length;
                addressCount = (addressByteCount / (uint)sizeof(WinDivertAddress));
                for (uint i = 0; i < addressCount; i++)
                {
                    WinDivertAddress* address = &((WinDivertAddress*)addressBuffer)[i];
                    result = WinDivert.WinDivertHelperParsePacket(ref packetByteCount, ref packetOffset);

                    if (result.IPv4Header != null && result.TcpHeader != null)
                    {
                        Console.WriteLine($"V4 TCP packet outbound {address->Outbound} from {result.IPv4Header->SrcAddr}:{result.TcpHeader->SrcPort} to {result.IPv4Header->DstAddr}:{result.TcpHeader->DstPort}");
                    }
                    else if (result.IPv6Header != null && result.TcpHeader != null)
                    {
                        Console.WriteLine($"V4 TCP packet outbound {address->Outbound} from {result.IPv6Header->SrcAddr}:{result.TcpHeader->SrcPort} to {result.IPv6Header->DstAddr}:{result.TcpHeader->DstPort}");
                    }

                    Console.WriteLine($"{nameof(WinDivertAddress.Timestamp)} - {address->Timestamp}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Event)} - {address->Event}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Layer)} - {address->Layer}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Sniffed)} - {address->Sniffed}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Outbound)} - {address->Outbound}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Loopback)} - {address->Loopback}");
                    Console.WriteLine($"{nameof(WinDivertAddress.Impostor)} - {address->Impostor}");
                    Console.WriteLine($"{nameof(WinDivertAddress.IPv6)} - {address->IPv6}");
                    Console.WriteLine($"{nameof(WinDivertAddress.IPChecksum)} - {address->IPChecksum}");
                    Console.WriteLine($"{nameof(WinDivertAddress.TCPChecksum)} - {address->TCPChecksum}");
                    Console.WriteLine($"{nameof(WinDivertAddress.UDPChecksum)} - {address->UDPChecksum}");

                    // Console.WriteLine(WinDivert.WinDivertHelperCalcChecksums(packet, ref addr, WinDivertChecksumHelperParam.All));
                }

                if (!WinDivert.WinDivertSendEx(winDivertHandle, packet, ref readPacketByteCount, 0, addressBuffer, ref addressByteCount, ref sendOverlapped))
                {
                    var error = Marshal.GetLastWin32Error();
                    if (error != errorIoPending)
                    {
                        Console.WriteLine(string.Format("Unknown IO error ID {0} while awaiting send overlapped result.", error));
                        continue;
                    }

                    // wait while not disposed
                    while (!Disposed && !sendEvent.WaitOne(1000)) { }

                    // get result, always error 87 here
                    if (!Kernel32.GetOverlappedResult(winDivertHandle, ref sendOverlapped, ref readPacketByteCount, false))
                    {
                        Console.WriteLine("Failed to get overlapped send result: {0}", Marshal.GetLastWin32Error());
                        continue;
                    }
                }

                Console.WriteLine("Sent bytes: {0}", readPacketByteCount);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Fatal error in read thread: {0}", ex);
        }
        finally
        {
            Marshal.FreeHGlobal(addressBuffer);
            WinDivert.WinDivertClose(winDivertHandle);
        }
    }

    /// <summary>
    /// Constructor
    /// </summary>
    public WinDivertInterop()
    {
        if (Environment.Is64BitProcess)
        {
            File.Copy(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WinDivert64.dll"), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WinDivert.dll"), true);
        }
        else
        {
            File.Copy(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WinDivert32.dll"), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WinDivert.dll"), true);
        }
        winDivertHandle = WinDivert.WinDivertOpen("inbound and tcp", WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
        WinDivert.WinDivertSetParam(winDivertHandle, WinDivertParam.QueueLen, 16384);
        WinDivert.WinDivertSetParam(winDivertHandle, WinDivertParam.QueueTime, 8000);
        WinDivert.WinDivertSetParam(winDivertHandle, WinDivertParam.QueueSize, 33554432);
        Task.Run(ReadThread);
    }
}
basil00 commented 5 years ago

This might be a legitimate invalid parameter. This bit looks suspicious to me: ref uint addrLen

The addrLen parameter of WinDivertSendEx is a UINT not a pointer-to-UINT.

jjxtra commented 5 years ago

Checking without ref...

jjxtra commented 5 years ago

That was it, working great now!