【问题标题】:Redirect per-app DNS requests at the ALE_CONNECT_REDIRECT_V4 layer using WFP使用 WFP 在 ALE_CONNECT_REDIRECT_V4 层重定向每个应用的 DNS 请求
【发布时间】:2020-07-16 07:10:11
【问题描述】:

我正在尝试使用 WFP(Windows 过滤平台)在每个应用程序的基础上重定向 DNS 请求。我想重定向到公共 DNS 服务器 - 而不是本地代理。我在ALE_CONNECT_REDIRECT_V4 层有一个标注驱动程序。当我在这一层跟踪 DNS 请求时,我可以看到它们正常运行。

但是,当我将 DNS 服务器 ip(使用 INETADDR_SET_ADDRESS)重写到另一个公共服务器,例如 1.1.1.1(我只是重写到公共服务器,而不是本地代理)时,我看到重写的 DNS 请求仍然存在wireshark 以及他们的响应成功进入 - 但我正在重写其 DNS 的应用程序没有收到这些 DNS 响应 - 它无法解析主机名。

我已禁用 DNS 缓存,以便 DNS 请求直接来自应用程序,而不是 svchost.exe 进程。

这是为什么?我是否还必须以某种方式挂钩传入的数据包并将 DNS 服务器恢复到应用程序期望的服务器?我很茫然。

【问题讨论】:

  • 你的分类功能是什么样的?我刚刚编写了一个最小的测试驱动程序,它在 Windows 10 x64 上运行良好。
  • @luke 你是什么意思它工作正常?应用接受 DNS 请求?我刚刚从 Microsoft 得到确认,我需要使用数据报包层,因为应用程序确实会拒绝 DNS,因为它不是预期的服务器 ip(尽管 tcp/ip 堆栈确实接受 dns 请求/响应)。所以他们建议我在数据包层本身重写传出和传入......你是说它只适用于你使用连接重定向?!该应用程序实际上接受它吗?
  • Doh,我只针对 nslookup 进行了测试。我想这取决于特定应用程序中 DNS 查找的编码方式;大概 nslookup 没有验证响应是否来自它(认为它)发送到的地址。
  • @Luke 这很有趣,我用 'ping' 进行了检查,但它失败了,但我从来没有用 'nslookup' 测试过它——如果 ti 可以使用它,那就太好了! :) 我可以看看你的分类Fn 吗? (我也可以发布我的,但是提取代码需要做很多工作,因为它还可以做很多其他事情,呵呵,但是如果你真的想要它,我也可以努力做到这一点:D)
  • pastebin.com/khGcGd52 我假设对于 UDP DNS 请求,默认解析器会检查响应的地址是否与请求的地址匹配。 nslookup 可能会自己处理 DNS 内容,但不会检查这一点。用 TCP DNS 请求对此进行测试会很有趣;我希望它能够与那些人一起工作,因为不需要将请求与响应匹配,因为它发生在专用的通信渠道上。

标签: windows winapi windows-firewall wfp


【解决方案1】:

在自己尝试过之后,是的,对于通过 UDP 的 DNS 流量,您必须拦截入站数据报并使用 clone-drop-reinject 方法修改源地址。 TCP 上的 DNS 流量不受影响,因为它是基于连接的协议,而 UDP 是无连接的。

完整的最小工作驱动程序代码:https://pastebin.com/tCHqNnJH

相关摘录:

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;
    }
}

【讨论】:

  • 哇!这将非常有用,但是您是如何设法仅修改与给定应用程序上的 DNS 关联的入站数据包的?
  • 我没有,我修改了来自端口 53 的所有入站 DNS 流量。要将其限制为特定应用程序,您可以将一些上下文与数据流相关联,并在数据报标注中检查以找出要做什么做。
  • 哦,这很有趣,所以如果我将上下文与 connect_redirect 层分类中的数据流相关联,那么在查看传入数据包时,该上下文也将在 datagram_packet 层中可用?
猜你喜欢
  • 2012-06-15
  • 2023-01-25
  • 1970-01-01
  • 2012-02-18
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多