Search code examples
driverwdkndisraw-ethernetethercat

Error 87 in WriteFile function (NDIS) when using EtherType 0x88A4 (EtherCat)


I am trying to send a Raw Ethernet frame over layer 2, using the prottest.c example code for the NDIS driver, in C. The example works without problem, but when I modify the Ether Type (0x88A4 EtherCat) and adapt the frame with the necessary structure and information, the Writefile function always returns Error 87 (Incorrect parameters).

Is it not possible to write with this function on Layer 2, in Raw, without the TCP/IP stack, what could be wrong?

Thanks for your help. Best regards.

VOID
DoWriteProc(
    HANDLE  Handle
)
{
    PUCHAR      pWriteBuf = NULL;
    PUCHAR      pData;
    INT         SendCount;
    PETH_HEADER pEthHeader;
    DWORD       BytesWritten;
    BOOLEAN     bSuccess;

    DEBUGP(("DoWriteProc\n"));
    SendCount = 0;

    do
    {
        pWriteBuf = malloc(PacketLength);

        if (pWriteBuf == NULL)
        {
            DEBUGP(("DoWriteProc: Failed to malloc %d bytes\n", PacketLength));
            break;
        }
        pEthHeader = (PETH_HEADER)pWriteBuf;
        pEthHeader->EthType = EthType;

        if (bUseFakeAddress)
        {
            memcpy(pEthHeader->SrcAddr, FakeSrcMacAddr, MAC_ADDR_LEN);
        }
        else
        {
            memcpy(pEthHeader->SrcAddr, SrcMacAddr, MAC_ADDR_LEN);
        }

        memcpy(pEthHeader->DstAddr, DstMacAddr, MAC_ADDR_LEN);

        pData = (PUCHAR)(pEthHeader + 1);

        *pData++ = (UCHAR)0x8C; //Lenght
        *pData++ = (UCHAR)0x45; //Res & Type

        *pData++ = (UCHAR)0xD0; //Publisher
        *pData++ = (UCHAR)0x50;
        *pData++ = (UCHAR)0x99;
        *pData++ = (UCHAR)0x45;
        *pData++ = (UCHAR)0x34;
        *pData++ = (UCHAR)0x9D;

        *pData++ = (UCHAR)0x01; //Count
        *pData++ = (UCHAR)0x00;

        *pData++ = (UCHAR)0x00; //Cycle
        *pData++ = (UCHAR)0x00;

        *pData++ = (UCHAR)0x00; //Res

        *pData++ = (UCHAR)0x28; //EAP_SM

        *pData++ = (UCHAR)0x04; //PD ID
        *pData++ = (UCHAR)0x00;

        *pData++ = (UCHAR)0x00; //Version
        *pData++ = (UCHAR)0x00;

        *pData++ = (UCHAR)0x78; //Lenght
        *pData++ = (UCHAR)0x05;

        *pData++ = (UCHAR)0x00; //Quality
        *pData++ = (UCHAR)0x00;

        unsigned char j = 0;
        for (int k = 0; k < 1400; k++) //Data
        {
            *pData++ = (UCHAR)j;

            j++;
            if (j > 0xFF)
            {
                j = 0;
            }
        }
        
        SendCount = 0;

        while (TRUE)
        {

            bSuccess = (BOOLEAN)WriteFile(
                Handle,
                pWriteBuf,
                PacketLength,
                &BytesWritten,
                NULL);
            DWORD err = GetLastError();
            printf("ERROR: %i", err);
            if (!bSuccess)
            {
                PRINTF(("DoWriteProc: WriteFile failed on Handle %p\n", Handle));
                break;
            }
            SendCount++;

            DEBUGP(("DoWriteProc: sent %d bytes\n", BytesWritten));

            if ((NumberOfPackets != -1) && (SendCount == NumberOfPackets))
            {
                break;
            }
        }

    } while (FALSE);

    if (pWriteBuf)
    {
        free(pWriteBuf);
    }

    PRINTF(("DoWriteProc: finished sending %d packets of %d bytes each\n",
        SendCount, PacketLength));}


HANDLE
OpenHandle(_In_ PSTR pDeviceName){
    DWORD   DesiredAccess;
    DWORD   ShareMode;
    LPSECURITY_ATTRIBUTES   lpSecurityAttributes = NULL;

    DWORD   CreationDistribution;
    DWORD   FlagsAndAttributes;
    HANDLE  Handle;
    DWORD   BytesReturned;

    DesiredAccess = GENERIC_READ | GENERIC_WRITE;
    ShareMode = 0;
    CreationDistribution = OPEN_EXISTING;
    FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;

    Handle = CreateFileA(
        pDeviceName,
        DesiredAccess,
        ShareMode,
        lpSecurityAttributes,
        CreationDistribution,
        FlagsAndAttributes,
        NULL
    );
    if (Handle == INVALID_HANDLE_VALUE)
    {
        DEBUGP(("Creating file failed, error %x\n", GetLastError()));
        return Handle;
    }
    //
    //  Wait for the driver to finish binding.
    //
    if (!DeviceIoControl(
        Handle,
        IOCTL_NDISPROT_BIND_WAIT,
        NULL,
        0,
        NULL,
        0,
        &BytesReturned,
        NULL))
    {
        DEBUGP(("IOCTL_NDISIO_BIND_WAIT failed, error %x\n", GetLastError()));
        CloseHandle(Handle);
        Handle = INVALID_HANDLE_VALUE;
    }

    return (Handle);
}

Solution

  • For security, the driver refuses to send these types of packets by default.

    Of course, since you have the source code to the driver, you are free to modify this restriction however you like — it's your driver. You can add a line to specifically allow the 0x88A4 EtherType, or delete the entire if-statement to allow all EtherTypes. You can require the usermode process to be running as Administrator if it wants to send "suspicious" network frames.

    A bit more detail on the security angle. If you allow untrusted users/programs to place arbitrary data onto the network, that may compromise or weaken network security. This is why the sample driver (and Windows in general) does not allow arbitrary programs to put arbitrary data on the network.

    For example, a malicious program that has unrestricted access to the Ethernet layer can advertise a malicious DHCP server that points clients to a malicious DNS server, conduct ARP poisoning attacks on your switch, DoS a switch (with, for example, 802.3x PAUSE frames, or with LLDPDUs that undermine the QoS policy), or circumvent any firewall policies you might have.

    These potential attacks aren't necessarily a deal-breaker: consider that this is roughly the equivalent of allowing someone to plug an arbitrary unmanaged device into an Ethernet jack on your network. If your network already has measures in place to defend against hostile Ethernet endpoints, then removing restrictions from the sample driver not making things much worse. Alternatively, if you have some level of trust for all the users & code on the PCs that will run your driver, then modifying the driver won't matter. Or if your threat model already assumes the network is hostile and unreliable, then removing these restrictions will only help fulfill your threat model's expectations. ;)