Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


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

FunGuy77FunGuy77 Member Posts: 11
edited July 16 in NTDEV

Hi,

I am trying to redirect DNS requests on a per-app basis. 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.

Post edited by FunGuy77 on

Comments

  • Jason_StephensonJason_Stephenson Member Posts: 73

    Do I have to somehow also hook incoming packets and restore the DNS server to the one the application expects?

    Yes I'm afraid so. You may find that this restriction changes your proposed solution away from ALE_CONNECT_REDIRECT to DATAGRAM_DATA since you'll need to be doing per packet processing in the receive path anyway

  • FunGuy77FunGuy77 Member Posts: 11

    Thanks! I'm not familiar with that layer and can't find much docs about it - can i also add a filter by appId to this layer (like i do with ALE_CONNECT_REDIRECT) so that i know that incoming packet is associated with a given app? or must i use different techniques to associate that flow with a given app so i know to rewrite the incoming packet?

  • MBond2MBond2 Member Posts: 145

    Is there a valid reason for this?

  • Jason_StephensonJason_Stephenson Member Posts: 73

    The application id is an ALE level identifier, you'll be unable to get this at the DATAGRAM_DATA / TRANSPORT / IP layers. You can however:
    1. Register a callout at an ALE layer that contains the app id
    2. Use fwpsflowassociatecontext to add the application id to a context
    3. Retreive said context in the DATAGRAM / other packet processing layers

    J

  • FunGuy77FunGuy77 Member Posts: 11

    Thanks man, i'll run with that and see where i get, you're a great help so far :)

  • FunGuy77FunGuy77 Member Posts: 11

    Hi,

    I've been trying to get this to work, but i can't seem to figure it out. I first tried to modify the destination IP via the CONNECT_REDIRECT layer and then restore the incoming source IP via the DATAGRAM_DATA layer, but that didn't appear to work, the packet was still being rejected.

    So i then tried to modify both the outgoing and incoming packet via the DATAGRAM_DATA layer, however i blue screen errors when rewriting the destination ip in the outgoing packet, any idea what i'm doing wrong? here's the code (below). I admit i found the parameters for FwpsInjectTransportSendAsync a bit confusing, and was unsure exactly what to put in for the sendParams arg - though i think what i have looks right.

    RtlIpv4StringToAddressExW(
        L"1.1.1.1",        // hard-coding the new (rewritten) dns server for now
        FALSE,
        &sin4.sin_addr,
        &sin4.sin_port);
    
    RtlIpv4StringToAddressExW(
        L"8.8.8.8",        // hard-coding the original dns server for now
        FALSE,
        &origSin4.sin_addr,
        &origSin4.sin_port);
    
    if ((Direction == FWP_DIRECTION_OUTBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == 53) && (RemoteAddress == origSin4.sin_addr.S_un.S_addr))
    {
    
        UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
        UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;
        UINT64 endpointHandle = inMetaValues->transportEndpointHandle;
    
        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))
        {
            return;
        }
    
        NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);
    
        if (!NetBufferList)
        {
            return;
        }
    
        NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
    
        PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);
        IpHeader->DestinationAddress = sin4.sin_addr.S_un.S_addr;
        UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));
    
        FWPS_TRANSPORT_SEND_PARAMS sendParams = {
            .remoteAddress = (UCHAR*)IpHeader->DestinationAddress,
            .remoteScopeId = inMetaValues->remoteScopeId,
            .controlData = inMetaValues->controlData,
            .controlDataLength = inMetaValues->controlDataLength,
            .headerIncludeHeader = inMetaValues->headerIncludeHeader,
            .headerIncludeHeaderLength = inMetaValues->headerIncludeHeaderLength
        };
    
        Status = FwpsInjectTransportSendAsync(g_InjectionHandle, NULL, endpointHandle, 0, &sendParams, AF_INET, inMetaValues->compartmentId, NetBufferList, DriverDatagramDataInjectComplete, NULL);
        if (!NT_SUCCESS(Status))
        {
            FwpsFreeCloneNetBufferList(NetBufferList, 0);
        }
    
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
    }
    
  • FunGuy77FunGuy77 Member Posts: 11

    Hi,

    I've been trying to get this to work, but i can't seem to figure it out. I first tried to modify the destination IP via the CONNECT_REDIRECT layer and then restore the incoming source IP via the DATAGRAM_DATA layer, but that didn't appear to work, the packet was still being rejected.

    So i then tried to modify both the outgoing and incoming packet via the DATAGRAM_DATA layer, however i blue screen errors when rewriting the destination ip in the outgoing packet, any idea what i'm doing wrong? here's the code (below). I admit i found the parameters for FwpsInjectTransportSendAsync a bit confusing, and was unsure exactly what to put in for the sendParams arg - though i think what i have looks right.

    RtlIpv4StringToAddressExW(
    L"1.1.1.1", // hard-coding the new (rewritten) dns server for now
    FALSE,
    &sin4.sin_addr,
    &sin4.sin_port);
    
    RtlIpv4StringToAddressExW(
        L"8.8.8.8",        // hard-coding the original dns server for now
        FALSE,
        &origSin4.sin_addr,
        &origSin4.sin_port);
    
    if ((Direction == FWP_DIRECTION_OUTBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == 53) && (RemoteAddress == origSin4.sin_addr.S_un.S_addr))
    {
    
    UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
    UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;
    UINT64 endpointHandle = inMetaValues->transportEndpointHandle;
    
    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))
    {
        return;
    }
    
    NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);
    
    if (!NetBufferList)
    {
        return;
    }
    
    NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
    
    PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);
    IpHeader->DestinationAddress = sin4.sin_addr.S_un.S_addr;
    UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));
    
    FWPS_TRANSPORT_SEND_PARAMS sendParams = {
        .remoteAddress = (UCHAR*)IpHeader->DestinationAddress,
        .remoteScopeId = inMetaValues->remoteScopeId,
        .controlData = inMetaValues->controlData,
        .controlDataLength = inMetaValues->controlDataLength,
        .headerIncludeHeader = inMetaValues->headerIncludeHeader,
        .headerIncludeHeaderLength = inMetaValues->headerIncludeHeaderLength
    };
    
    Status = FwpsInjectTransportSendAsync(g_InjectionHandle, NULL, endpointHandle, 0, &sendParams, AF_INET, inMetaValues->compartmentId, NetBufferList, DriverDatagramDataInjectComplete, NULL);
    if (!NT_SUCCESS(Status))
    {
        FwpsFreeCloneNetBufferList(NetBufferList, 0);
    }
    
    classifyOut->actionType = FWP_ACTION_BLOCK;
    classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
    

    }

  • FunGuy77FunGuy77 Member Posts: 11

    Hi,

    I've been trying to get this to work, but i can't seem to figure it out. I first tried to modify the destination IP via the CONNECT_REDIRECT layer and then restore the incoming source IP via the DATAGRAM_DATA layer, but that didn't appear to work, the packet was still being rejected.

    So i then tried to modify both the outgoing and incoming packet via the DATAGRAM_DATA layer, however i blue screen errors when rewriting the destination ip in the outgoing packet, any idea what i'm doing wrong? here's the code (below). I admit i found the parameters for FwpsInjectTransportSendAsync a bit confusing, and was unsure exactly what to put in for the sendParams arg - though i think what i have looks right.

    RtlIpv4StringToAddressExW(
    L"1.1.1.1", // hard-coding the new (rewritten) dns server for now
    FALSE,
    &sin4.sin_addr,
    &sin4.sin_port);
    
    RtlIpv4StringToAddressExW(
        L"8.8.8.8",        // hard-coding the original dns server for now
        FALSE,
        &origSin4.sin_addr,
        &origSin4.sin_port);
    
    if ((Direction == FWP_DIRECTION_OUTBOUND) && (PacketInjectionState == FWPS_PACKET_NOT_INJECTED) && (RemotePort == 53) && (RemoteAddress == origSin4.sin_addr.S_un.S_addr))
    {
    
        UINT32 IpHeaderSize = inMetaValues->ipHeaderSize;
        UINT32 TransportHeaderSize = inMetaValues->transportHeaderSize;
        UINT64 endpointHandle = inMetaValues->transportEndpointHandle;
    
        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))
        {
            return;
        }
    
        NdisAdvanceNetBufferDataStart(NetBuffer, IpHeaderSize + TransportHeaderSize, FALSE, NULL);
    
        if (!NetBufferList)
        {
            return;
        }
    
        NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
    
        PIPV4_HEADER IpHeader = NdisGetDataBuffer(NetBuffer, sizeof(IPV4_HEADER), NULL, 1, 0);
        IpHeader->DestinationAddress = sin4.sin_addr.S_un.S_addr;
        UpdateIpv4HeaderChecksum(IpHeader, sizeof(IPV4_HEADER));
    
        FWPS_TRANSPORT_SEND_PARAMS sendParams = {
            .remoteAddress = (UCHAR*)IpHeader->DestinationAddress,
            .remoteScopeId = inMetaValues->remoteScopeId,
            .controlData = inMetaValues->controlData,
            .controlDataLength = inMetaValues->controlDataLength,
            .headerIncludeHeader = inMetaValues->headerIncludeHeader,
            .headerIncludeHeaderLength = inMetaValues->headerIncludeHeaderLength
        };
    
        Status = FwpsInjectTransportSendAsync(g_InjectionHandle, NULL, endpointHandle, 0, &sendParams, AF_INET, inMetaValues->compartmentId, NetBufferList, DriverDatagramDataInjectComplete, NULL);
        if (!NT_SUCCESS(Status))
        {
            FwpsFreeCloneNetBufferList(NetBufferList, 0);
        }
    
        classifyOut->actionType = FWP_ACTION_BLOCK;
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
    }
    
  • MBond2MBond2 Member Posts: 145

    Again, is there a valid reason why you want to make this work?

  • FunGuy77FunGuy77 Member Posts: 11

    @MBond2 sure :) I have some apps whose traffic is rrouted differently (some go via VPN, some go via physical interface) - and i want different DNS servers for each class of app. Apps that go via VPN use special smart DNS servers, apps that bypass VPN should use the existing system-configured DNS.

  • Jason_StephensonJason_Stephenson Member Posts: 73

    I've been trying to get this to work, but i can't seem to figure it out. I first tried to modify the destination IP via the CONNECT_REDIRECT layer and then restore the incoming source IP via the DATAGRAM_DATA layer, but that didn't appear to work, the packet was still being rejected.

    I can categorically tell you that this approach works when implemented correctly.

    however i blue screen errors when rewriting the destination ip in the outgoing packet, any idea what i'm doing wrong?

    Taking what you said literally. Is your IPHeader variable NULL? Are you aware how much you should be advancing the NB? (https://docs.microsoft.com/en-us/windows-hardware/drivers/network/data-offset-positions?redirectedfrom=MSDN)

    Also, you had two NetBufferList variables. This is at best confusing and at worst wrong.

  • FunGuy77FunGuy77 Member Posts: 11

    @Jason_Stephenson Ah. So i shouldn't need to rewrite both the outgoing and incoming packets at the DATAGRAM_DATA layer, i should be able to just rely on the CONNECT_REDIRECT layer to rewrite the outgoing destination IP, and then only rewrite the incoming source ip for the incoming packet?

    I swear i've been doing just that, and the incoming rewrite appears to succeed but the dns client still rejects it.

    What i want is:

    • Most apps send their traffic over the VPN
    • Some apps (specified in a list) send their traffic outside the VPN - achieved by rewriting the source ip in the BIND_REDIRECT layer
    • For the apps that send their traffic outside the VPN i also want to change the DNS servers they use, so they use DNS servers i specify

    I have the first two working FINE, i can have some apps on the VPN and some apps outside the VPN - but i cannot seem to get the bypass apps using their own DNS servers

    What i tried:

    • in the CONNECT_REDIRECT layer I rewrite the destination IP to the DNS server i want
    • in the DATAGRAM_DATA layer (incoming) i rewrite the source IP to the old DNS server

    DNS client just rejects the packets. Could this be due to the fact that i have a VPN interface? is that somehow interfering with things?

    Because the above approach wasn't working, i switched to using per-packet rewriting in both directions at the DATAGRAM_DATA layer; but at this point i'm a bit stuck. But if you're really saying that rewriting at the CONNECT_REDIRECT layer should work (even with my VPN interface), i should re-explore that approach rather than continuing to investigate per-packet rewriting at the DATAGRAM_DATA layer?

  • MBond2MBond2 Member Posts: 145

    is the VPN interface an interface that Windows knows about?

  • FunGuy77FunGuy77 Member Posts: 11

    @MBond2 yes it knows about it :) But for some reason, the approach originally rrecommended does not appear to work - rewriting destination ip for the dns request using connect_redirect and then rewriting the incoming using per-packet rewriting at DATAGRAM_DATA just does not seem to work.

    I'm using the exact approach recommended here: https://stackoverflow.com/a/62998891/66725

  • MBond2MBond2 Member Posts: 145

    Well, I don't know anything about this article, but consider what you are tring to do. Standard DNS operates on tiers of cached data. Some server is registered as authoritive for a certian domain, and all the other servers in the chain between the client and that server (including the local OS) are designed to cache an authoritive reponse until its TTL expires.

    Now you want to split the DNS namespace between some apps that see space A, and other apps on the same OS that see space B. And only some times do they see space B because it depends on the link state of an interface (VPN). Now that's going to be impossible to implement completely reliably in the general case

  • FunGuy77FunGuy77 Member Posts: 11

    @MBond2 thanks :) However i got this working on linux so i think i can get it working here too (i have also turned off dns caching)

    @Jason_Stephenson you were absolutely correct the IpHeader was NULL! My next question is why is it NULL? You were also correct about the Retreat - i only needed to Retreat the size of the IpHeader as for outbound packets we get a pointer to the transport header -- but i want to modify the ipheader. So i updated that code. IpHeader is still NULL though, any idea? the code is extraordinarily simple -- i just want to modify the ipheader for an outbound UDP packet..

  • Jason_StephensonJason_Stephenson Member Posts: 73

    It's been a while since I've been working with this in detail so I can't immediately tell. Use ndiskd.nbl (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-ndiskd-nbl) on the original NBL and cloned NBL and see if you can figure it out for yourself.

    I also find the Retreat --> Clone --> Advance bit somewhat confusing but it might be ok, like I said.. It's been a while.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 30 Mar 2020 OSR Seminar Space
Developing Minifilters 15 Jun 2020 LIVE ONLINE
Writing WDF Drivers 22 June 2020 LIVE ONLINE
Internals & Software Drivers 28 Sept 2020 Dulles, VA