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 DNS traffic using WFP

cfallas02cfallas02 Member Posts: 1

Hi!
I am new to WFP and driver development in general and OSR have been so helpful through this learning journey. Right now I am facing a similar problem described on this post: Cisco AnyConnect VPN compatibility woes. I have tried to apply the solution posted there, but I can't get it to work, so I was hoping to the get some pointer of what could be happening and how could I get this to work. Let me described what I am trying to do:


I am trying to implement the same scenario that Jason_Stephenson described here , a local DNS proxy and a callout driver that redirects the DNS queries to this proxy. I managed to get the redirection to the DNS proxy working by registering a callout filter at the FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer, and then re-inject the packet using FwpsInjectTransportSendAsync. My problem now is that I can't get the "de-proxy" of the response from the DNS proxy to work, as I understand for the Windows DNS client to accept the response the packet must come from the original IP and port, so the DNS proxy response's source address and port should be restored to the original DNS query destination address and port.


Supposedly the "de-proxy" should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can't get the packet on any of the discard layers?


The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4 layer, so I am modifying the packet on this layer. My code looks like this:

void classifyFunc(...)
{
  NET_BUFFER_LIST * netBufferList = (NET_BUFFER_LIST *)layerData;
  // Verify that the packet wasn't injected before
  FWPS_PACKET_INJECTION_STATE packetState = FwpsQueryPacketInjectionState(injectionNetworkHandle, netBufferList, NULL);
  if (packetState == FWPS_PACKET_INJECTED_BY_SELF || packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) 
  {
    classifyOut->actionType = FWP_ACTION_PERMIT;
    if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
    {
        classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    }
    return;
  }

  MetadataValues* metadataValues = ExAllocatePoolWithTag(NonPagedPool, sizeof(MetadataValues), SOME_TAG);
 // Get all information needed for redirection (compartmentId, interfaceIndex, subInterfaceIndex, IpHeaderSize, etc) and stored it on metadataValues

 // Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread
  FwpsReferenceNetBufferList(netBufferList, TRUE);
  classifyOut->actionType = FWP_ACTION_BLOCK;
  classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
  classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;

 // Queue the packet to be modified out of bounds by a worker thread.
  addPacketToWorkerQueue(netBufferList, metadataValues); 
}

void workerThreadFunc(NET_BUFFER_LIST * netBufferList, MetadataValues * metadataValues) //this Is the function that the worker thread executes in order to modify and re-inject the packet
{
   //Retread the NET_BUFFER_LIST by the amount specified in inMetadataValues->ipHeaderSize
   // Cloned it and advance the original NET_BUFFER_LIST to its original position.
   NET_BUFFER_LIST * clonedBufferList;
   NDIS_STATUS ndis_status = NdisRetreatNetBufferListDataStart(netBufferList, metadataValues->ipHeaderSize, 0, NULL, NULL);
   status = FwpsAllocateCloneNetBufferList(netBufferList, NULL, NULL, 0, &clonedBufferList);
   NdisAdvanceNetBufferListDataStart(netBufferList, metadataValues->ipHeaderSize, FALSE, NULL);

  // Advance the cloned NET_BUFFER_LIST to the transport header
  NET_BUFFER * netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedBufferList);
  NdisAdvanceNetBufferDataStart(netBuffer, metadataValues->ipHeaderSize, FALSE, NULL); 
  // Get the UDP header and modify the source port
  UDP_HEADER * udpHeader = (UDP_HEADER *)NdisGetDataBuffer(netBuffer, sizeof(UDP_HEADER), NULL, sizeof(UINT16), 0);
  udpHeader->srcPort = RtlUshortByteSwap(53);
   // Go back to the beginning of the IP Header
  ndis_status = NdisRetreatNetBufferDataStart(netBuffer, metadataValues->ipHeaderSize, 0, NULL); 

  // Get the IP header and modify the source and destination ip addresses
  UINT32 source = RtlUlongByteSwap(originalDNS);
  UINT32 destiny = RtlUlongByteSwap(nicIpAddress);
  IP_HEADER_V4 * ipHeader = (IP_HEADER_V4 *)NdisGetDataBuffer(netBuffer, metadataValues->ipHeaderSize, NULL, sizeof(UINT16), 0);
  RtlCopyMemory(ipHeader->pSourceAddress, &source, sizeof(UINT32));
  RtlCopyMemory(ipHeader->pDestinationAddress, &destiny, sizeof(UINT32));

  // Finally fix the IpHeader Checksum
  fixIpChecksum(ipHeader, metadataValues->ipHeaderSize); 

  //Inject the modified NET_BUFFER_LIST

  FwpsInjectNetworkReceiveAsync(
         injectionNetworkHandle, //This injection handle was created using the FWPS_INJECTION_TYPE_NETWORK flag 
         NULL,
         0,
         metadataValues->compartmentId,
         metadataValues->interfaceIndex, 
         metadataValues->subInterfaceIndex, 
         clonedBuffer, 
         InjectToDNSClientCompleteFunc, 
         NULL); 
}

To recalculate the Ip Header checksum I am using this function:

void fixIpChecksum(IP_HEADER_V4* ipHeader, UINT32 size)
{
    UINT32            sum = 0;
    UINT32            words = size / 2;
    UINT16 UNALIGNED* pStart = (UINT16*)ipHeader;
    ipHeader->checksum = 0;
    for (UINT8 i = 0; i < words; i++)
    {
          sum += pStart[i];
    }
    sum = (sum & 0x0000ffff) + (sum >> 16);
    sum += (sum >> 16);
    ipHeader->checksum = (UINT16)~sum;
}

The injection function returns STATUS_SUCCESS and on the completion function the status of the injected NET_BUFFER_LIST is STATUS_SUCCESS. But the DNS client is timing out and the packet is not hitting any of the filters that I previously placed on the TRANSPORT_V4/TRANSPORT_V4_DISCARD, IPPACKET_V4/IPPACKET_V4_DISCARD nor DATAGRAMA_DATA_V4/DATAGRAM_DATA_V4_DISCARD layers.


I also tried to modify and re inject the DNS proxy response packet on the FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer using the FwpsInjectTransportSendAsync using the following steps:
1. Verify that the packet hasn't being re injected before:
2. Retread the NET_BUFFER_LIST by the amount specified on inMetadataValues->ipHeaderSize + inMetadataValues->ipHeaderSize
3. Clone the NET_BUFFER_LIST
4. Restore the original NET_BUFFER_LIST (advance the packet by the amount specified on inMetadataValues->ipHeaderSize + inMetadataValues->ipHeaderSize)
5. Block-adsorb the original NET_BUFFER_LIST
6. Advance the cloned NET_BUFFER_LIST to the transport
7. Get UDP header and modify the source port
8. Retread the cloned NET_BUFFER_LIST to the ip header
9. Use FwpsConstructIpHeaderForTransportPacket to create the new Ip Header, using the original DNS ip as source and the machines NIC's ip as the destination ip
11. Re-inject the cloned NET_BUFFER_LIST using FwpsInjectTransportSendAsyc function


As before the injection function returns STATUS_SUCCESS, but the completion function returns STATUS_DATA_NOT_ACCEPTED.

Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn't seem work with DNS Queries, my test filter did hit the packet and performed the redirection successfully, but the packet didn't made it to the DNS proxy, It was again swallowed by the TCP/IP stack.

Do you have any idea of what could I be doing wrong? or any pointers on what should I do to get this to work? or Should I use another type of driver to do this?

Carlos

Comments

  • Jason_StephensonJason_Stephenson Member Posts: 76
    edited November 2019

    Supposedly the "de-proxy" should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can't get the packet on any of the discard layers?

    Are you actually sending the response back to the "source application"? (The application that made the DNS request)
    Have you tried a filter with a condition of "protocol == UDP"?

    The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4 layer

    You can definitely get this packet at the transport layer. (Assuming correct implementation)

    // Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread

    I would suggest you trim your solution down. Focus on getting inline packet modification working before worry about worker threads.

    To recalculate the Ip Header checksum I am using this function: ...

    I'd use FwpsConstructIpHeaderForTransportPacket for this

    As before the injection function returns STATUS_SUCCESS, but the completion function returns STATUS_DATA_NOT_ACCEPTED.

    Stick a breakpoint in your completion function. What does!ndiskd.nbl <your_net_buffer_list_address> display? Anything interesting?

    Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn't seem work with DNS Queries

    It should. Indeed a few years ago I worked with MS to get a few bugs fixed in this area. Redirection at this layer is different than other layers though, you're not injecting on a per packet basis. https://docs.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection is a good doc for getting started.

    Hope this helps,
    J

  • Jason_StephensonJason_Stephenson Member Posts: 76

    Supposedly the "de-proxy" should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can't get the packet on any of the discard layers?

    Are you actually sending the response back to the "source application"? (The application that made the DNS request)
    Have you tried a filter with a condition of "protocol == UDP"?

    The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4 layer

    You can definitely get this packet at the transport layer. (Assuming correct implementation)

    // Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread

    I would suggest you trim your solution down. Focus on getting inline packet modification working before worry about worker threads.

    To recalculate the Ip Header checksum I am using this function: ...

    I'd use FwpsConstructIpHeaderForTransportPacket for this

    As before the injection function returns STATUS_SUCCESS, but the completion function returns STATUS_DATA_NOT_ACCEPTED.

    Stick a breakpoint in your completion function. What does !ndiskd.nbl your_net_buffer_list_address display? Anything interesting?

    Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn't seem work with DNS Queries

    It should. Indeed a few years ago I worked with MS to get a few bugs fixed in this area. Redirection at this layer is different than other layers though, you're not injecting on a per packet basis. https://docs.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection is a good doc for getting started.

    Hope this helps,
    J

  • Jason_StephensonJason_Stephenson Member Posts: 76

    Supposedly the "de-proxy" should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can't get the packet on any of the discard layers?

    Are you actually sending the response back to the "source application"? (The application that made the DNS request)
    Have you tried a filter with a condition of "protocol == UDP"?

    The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4 layer

    You can definitely get this packet at the transport layer. (Assuming correct implementation)

    // Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread

    I would suggest you trim your solution down. Focus on getting inline packet modification working before worry about worker threads.

    To recalculate the Ip Header checksum I am using this function: ...

    I'd use FwpsConstructIpHeaderForTransportPacket for this

    As before the injection function returns STATUS_SUCCESS, but the completion function returns STATUS_DATA_NOT_ACCEPTED.

    Stick a breakpoint in your completion function. What does !ndiskd.nbl your_net_buffer_list_address display? Anything interesting?

    Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn't seem work with DNS Queries

    It should. Indeed a few years ago I worked with MS to get a few bugs fixed in this area. Redirection at this layer is different than other layers though, you're not injecting on a per packet basis. https://docs.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection is a good doc for getting started.

    Hope this helps,
    J

  • Jason_StephensonJason_Stephenson Member Posts: 76

    Supposedly the "de-proxy" should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can't get the packet on any of the discard layers?

    Are you actually sending the response back to the "source application"? (The application that made the DNS request)
    Have you tried a filter with a condition of "protocol == UDP"?

    The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4 layer

    You can definitely get this packet at the transport layer. (Assuming correct implementation)

    // Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread

    I would suggest you trim your solution down. Focus on getting inline packet modification working before worry about worker threads.

    To recalculate the Ip Header checksum I am using this function: ...

    I'd use FwpsConstructIpHeaderForTransportPacket for this

    As before the injection function returns STATUS_SUCCESS, but the completion function returns STATUS_DATA_NOT_ACCEPTED.

    Stick a breakpoint in your completion function. What does !ndiskd.nbl your_net_buffer_list_address display? Anything interesting?

    Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn't seem work with DNS Queries

    It should. Indeed a few years ago I worked with MS to get a few bugs fixed in this area. Redirection at this layer is different than other layers though, you're not injecting on a per packet basis. https://docs.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection is a good doc for getting started.

    Hope this helps,
    J

  • FunGuy77FunGuy77 Member Posts: 11

    Did you solve this issue? I am having the same problem. I also tried using ALE_CONNECT_REDIRECT_V4 - checked for UDP and port 53 and rewrote the destination IP to (in my case) a different public DNS server (like 1.1.1.1) - but it didn't work... a packet sniffer does show the DNS packets going to 1.1.1.1 and even getting a response, but the application didn't notice the DNS request somehow and the DNS just failed. Do i also have to rewrite the incoming packet to restore the original DNS server (from 1.1.1.1) ?

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!
Writing WDF Drivers 7 Dec 2020 LIVE ONLINE
Internals & Software Drivers 25 Jan 2021 LIVE ONLINE
Developing Minifilters 8 March 2021 LIVE ONLINE