【问题标题】:How to intercept DNS queries in Windows如何在 Windows 中拦截 DNS 查询
【发布时间】:2020-02-21 13:14:59
【问题描述】:

我正在研究如何在 Windows 中拦截 DNS 查询, 以一种不需要将 DLL 注入每个进程的方式,并且理想情况下能够根据发出查询的进程做出决策,因此简单的 DNS 代理服务器是不够的。

从表面上看,DNS 查询所采用的路径如下所示, 一些应用程序从 ws2_32.dll(或 dnsapi.dll 中的 DnsQuery)调用 gethostbyname

[dll 做了什么]

最终调用了 dnsrslvr.dll 中的某些内容,该内容在 svshost 进程中作为名为“Dnscache”的服务运行,该服务根据 TaskExplorer 在端口 53 上从 DNS 服务器发送和重新发送 UDP 数据包

所以我想做的是在“Dnscache”服务之前拦截一些 dns 查询,或者用自己的服务替换该服务。

所以我的问题是:这里的一位聪明人是否知道公共 DNS 查询 API 和 Dnscache 服务之间发生了什么,以及两者之间的最佳连接位置?

【问题讨论】:

  • 没有什么可以阻止应用程序进行自己的 DNS 查询,而无需让系统 API 代表应用程序执行实际查询。
  • 查询可以在进程中完成,无需在 svshost 进程中远程调用名为“Dnscache”的服务
  • 确保您可以实现自己的 dns 客户端并使其访问硬编码的 dns 服务器,但大多数应用程序使用操作系统工具来完成这些任务。这就是我关心的程序。
  • 即使在操作系统设施的情况下 - 所有都可以在单个进程中,无需服务调用
  • 所以你的意思是我应该使用本地 dns 代理服务器,仅此而已?

标签: windows winapi networking dns kernel


【解决方案1】:

您可以通过Callout Driver 进行操作。 您可以在 FWPM_LAYER_DATAGRAM_DATA_V4 的标注中捕获、解析和编辑 DNS 数据包。

这里是 FWPM_LAYER_DATAGRAM_DATA_V4 回调的代码,用于获取 DNS 查询名称。

void NTAPI
CoDatagramDataV4(
IN  const FWPS_INCOMING_VALUES* inFixedValues,
IN  const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
IN  VOID*                       packet,
IN  const void*                 classifyContext,
IN  const FWPS_FILTER*          filter,
IN  UINT64                      flowContext,
OUT FWPS_CLASSIFY_OUT*          classifyOut
){
    classifyOut->actionType = FWP_ACTION_PERMIT;

    if (inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8 != IPPROTO_UDP
        inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_PORT].value.uint16 != 53 ||
        inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32 != FWP_DIRECTION_INBOUND)
    {
        // This can be filtered by FwpmFilterAdd in ring3.
        return;
    }
    PNET_BUFFER pNetBuffer = NET_BUFFER_LIST_FIRST_NB((PNET_BUFFER_LIST)packet);
    if (pNetBuffer == NULL)
    {
        PVOID pDataBuffer0 = ExAllocatePoolWithTag(NonPagedPool, nDataLength, TAG_NAME_CALLOUT);
        if (pDataBuffer0)
        {
            PVOID pDataBuffer = NdisGetDataBuffer(pNetBuffer, nDataLength, pDataBuffer0, 1, 0);
            if (pDataBuffer)
            {
                PCHAR  pszQuery = (PCHAR)pDataBuffer + 12/*sizeof DNS header*/;
                CHAR   szQueryName[MAX_DOMAIN_NAME_LEN] = { 0 };
                CHAR   cbLabel = pQuery[0];
                UINT32 nLen = 1;
                while (cbLabel != 0)
                {
                    for (CHAR i = 0; i < cbLabel; i++)
                    {
                        szQueryName[nLen - 1] = pszQuery[nLen]; nLen++;
                    }
                    cbLabel = pszQuery[nLen];
                    szQueryName[nLen - 1] = cbLabel != 0 ? '.' : 0;
                    nLen++;
                }
                // Here szQueryName is DNS query name such as 'www.google.com'.
            }
        }
        ExFreePoolWithTag(pDataBuffer0, 'Tsnd');
    }
}

您可以像这样使用wireshark检查DNS结构。

【讨论】:

  • 提供一个最小的工作示例会很棒
  • 增加了FWPM_LAYER_DATAGRAM_DATA_V4回调的示例代码。
  • 当然,这仅适用于UDP查询,但DNS也可以使用TCP,这是DNS协议实现的必需部分,我们现在也有加密DNS。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2011-07-12
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 2021-10-05
  • 2016-05-19
相关资源
最近更新 更多