Redirect per-app DNS requests at the ALE_CONNECT_REDIRECT_V4 layer using WFP

2k Views Asked by At

I am trying to redirect DNS requests on a per-app basis using WFP (Windows Filtering Platform). I want to redirect to a public DNS server - not a local proxy. I have a callout driver at the ALE_CONNECT_REDIRECT_V4 layer. When I trace DNS requests at this layer, i can see them going out just fine.

However, when i rewrite the DNS server ip (using INETADDR_SET_ADDRESS) to another public server such as 1.1.1.1 (i'm only rewriting to public servers, not a local proxy) I see the rewritten DNS requests leaving on wireshark and also their responses coming in, successfully -- yet the application whose DNS i'm rewriting does not receive those DNS responses - it fails to resolve the hostname.

I have disabled the DNS cache so that the DNS requests come directly from the application, rather than the svchost.exe process.

Why is this? Do I have to somehow also hook incoming packets and restore the DNS server to the one the application expects? I'm at a loss.

1

There are 1 best solutions below

3
Luke On BEST ANSWER

After trying this out for myself, yes, for DNS traffic over UDP you have to intercept the inbound datagrams and modify the source address using the clone-drop-reinject method. DNS traffic over TCP is unaffected since it is a connection-based protocol whereas UDP is connectionless.

Full minimal working driver code: https://pastebin.com/tCHqNnJH

Relevant extract:

void UpdateIpv4HeaderChecksum(PIPV4_HEADER IpHeader, UINT32 IpHeaderSize)
{
    UINT32 Checksum = 0;
    UINT32 WordCount = IpHeaderSize / sizeof(UINT16);
    UINT16* Header = (UINT16*)IpHeader;

    IpHeader->Checksum = 0;

    for (UINT8 WordIndex = 0; WordIndex < WordCount; WordIndex++)
    {
        Checksum += Header[WordIndex];
    }

    Checksum = (Checksum & 0x0000ffff) + (Checksum >> 16);
    Checksum += (Checksum >> 16);

    IpHeader->Checksum = (UINT16)~Checksum;
}

void NTAPI DriverDatagramDataInjectComplete(
    _In_ void* context,
    _Inout_ NET_BUFFER_LIST* netBufferList,
    _In_ BOOLEAN dispatchLevel
)
{
    UNREFERENCED_PARAMETER(context);
    UNREFERENCED_PARAMETER(dispatchLevel);

    if (!NT_SUCCESS(netBufferList->Status))
    {
        DoTraceMessage(Default, "DriverDatagramDataInjectComplete() Status=%!STATUS!", netBufferList->Status);
    }

    FwpsFreeCloneNetBufferList(netBufferList, 0);
}

void NTAPI DriverDatagramDataClassify(
    _In_ const FWPS_INCOMING_VALUES* inFixedValues,
    _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    _Inout_opt_ void* layerData,
    _In_opt_ const void* classifyContext,
    _In_ const FWPS_FILTER* filter,
    _In_ UINT64 flowContext,
    _Inout_ FWPS_CLASSIFY_OUT* classifyOut
)
{
    UNREFERENCED_PARAMETER(layerData);
    UNREFERENCED_PARAMETER(classifyContext);
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(flowContext);

    UINT32 RemoteAddress = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32;
    UINT16 RemotePort = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_PORT].value.uint16;
    IF_INDEX InterfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32;
    IF_INDEX SubInterfaceIndex = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32;
    FWP_DIRECTION Direction = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32;

    FWPS_PACKET_INJECTION_STATE PacketInjectionState = FwpsQueryPacketInjectionState(g_InjectionHandle, layerData, NULL);

    classifyOut->actionType = FWP_ACTION_PERMIT;
    classifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;

    if ((Direction == FWP_DIRECTION_INBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == DNS_PORT) && (RemoteAddress == DNS_HOST_REDIRECT))
    {
        UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
        UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;

        PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB((PNET_BUFFER_LIST)layerData);
        NdisRetreatNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, 0, NULL);

        PNET_BUFFER_LIST NetBufferList = NULL;
        NTSTATUS Status = FwpsAllocateCloneNetBufferList(layerData, NULL, NULL, 0, &NetBufferList);
        if (!NT_SUCCESS(Status))
        {
            DoTraceMessage(Default, "FwpsAllocateCloneNetBufferList() Status=%!STATUS!", Status);
        }

        NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);

        if (!NetBufferList)
        {
            return;
        }

        DoTraceMessage(Default, "Modify DNS response");

        NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);

        PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);
        IpHeader->SourceAddress = DNS_HOST_ORIGINAL;
        UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));

        Status = FwpsInjectTransportReceiveAsync(g_InjectionHandle, NULL, NULL, 0, AF_INET, inMetaValues->compartmentId, InterfaceIndex, SubInterfaceIndex, NetBufferList, DriverDatagramDataInjectComplete, NULL);
        if (!NT_SUCCESS(Status))
        {
            DoTraceMessage(Default, "FwpsInjectTransportReceiveAsync() Status=%!STATUS!", Status);
            FwpsFreeCloneNetBufferList(NetBufferList, 0);
        }

        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
    }
}