如何捕获数据包(NPF; WinPcap)?

问题描述 投票:0回答:1

我读到了WinPcap如何过滤数据包here并从GitHub(Microsoft / Windows-driver-samples)检查ndis/filter project。我提供了WinPcap页面的主要数据,因为它们与问题有关。

enter image description here


enter image description here

我的主要问题:如果NPF丢弃一个数据包(数据包),这意味着不会捕获数据包或者不会发送/接收数据包?例如(正如我所见):

  1. dumpcap开始在eth0上侦听数据包。
  2. Chrome会发送DNS请求。
  3. NDIS驱动程序处理此数据包。 (确切地说 - NetBufferList;通过SendNetBufferListsHandler and SendNetBufferListsCompleteHandler函数)
  4. 解析NBL:解析每个缓冲区并检查数据包特征;
  5. 如果我们想丢弃一个数据包,我们需要组装新的NBL(没有不需要的数据包)并用新的NBList调用SendNetBufferListsCompleteHandler;
  6. 如果我们想要从捕获中删除此数据包,我们需要组装新的NBL(没有不需要的数据包)并使用新的NBList调用NdisFIndicateReceiveNetBufferLists;

接收数据包的情况相同(通过ReceiveNetBufferListsHandler and ReturnNetBufferListsHandler函数)。

我是否正确理解有机会通过NPF丢弃数据包,向/从网络发送/接收数据包,并将其从“捕获数据包列表”中删除?

如果是,如何正确实现丢包?

我没有找到通过SendNetBufferListsHandler / SendNetBufferListsCompleteHandler和ReceiveNetBufferListsHandler / ReturnNetBufferListsHandler函数丢弃数据包的代码示例。

c windows driver packet-capture ndis
1个回答
2
投票

如果NPF丢弃一个数据包(数据包),这意味着不会捕获数据包或者不会发送/接收数据包?

数据包将不会被捕获,但数据包仍将被传递到网络堆栈的其余部分。我想这是出于两个原因:

  1. 数据包捕获工具往往用于诊断,因此他们倾向于采用“不要让事情变得更糟”的理念。我知道的所有数据包捕获工具都会选择让数据包继续流过它们,即使它们无法跟上。
  2. 特别是NPF(又名winpcap / wireshark)的构建方式可以防止阻塞/丢弃流量。即使您愿意对NPF进行一些修改,也无法做到。原因是NPF是作为协议驱动程序实现的。作为协议驱动程序,它是TCPIP的对等体,不能直接干扰TCPIP的作用。 (这是一个奇迹,NPF甚至可以看到TCPIP传输的内容 - 这是通过第2层环回的魔力完成的。[与第3层环回无关,如:: 1和127.0.0.1等])

nmap项目有一个NPF分支,它将其实现为NDIS过滤器驱动程序。这种驱动程序能够阻止,延迟,重写或注入流量。因此,如果你有兴趣改变上面的哲学#1,你应该从nmap的fork而不是“官方”winpcap开始。

(而且,一般来说,我个人会建议使用nmap的fork,即使你不需要丢弃流量。过滤驱动程序会比将网络适配器置于二层环回模式的协议驱动程序快得多。)

一旦你看了nmap-npf,你就能从NDIS样本过滤器驱动程序中找到回调,比如FilterReceiveNetBufferLists。

丢包真的很容易;)但是有一些问题,所以让我们看看一些例子。

在传输路径上,我们有一个NBL的链表,我们希望将它分成两个列表,一个是丢弃,另一个是继续发送。单个NBL可以包含多个分组,但是保证每个分组具有相同的“流”(例如,TCP套接字)。所以通常你可以做出简化的假设,即NBL中的每个数据包总是以相同的方式处理:如果你想丢弃一个,你想要将它们全部丢弃。

如果这种假设不正确,即如果你确实想要从TCP套接字中选择性地丢弃一些数据包,而不是所有数据包,那么你需要做一些更复杂的事情。您不能直接从NET_BUFFER_LIST中删除单个N​​ET_BUFFER;相反,您必须克隆NET_BUFFER_LIST并复制您要保留的NET_BUFFER。

由于这是一个免费的论坛,我只会给你一个简单和常见案例的例子;)

void
FilterSendNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG sendFlags)
{
    NET_BUFFER_LIST *drop = NULL;
    NET_BUFFER_LIST *keep = NULL;

    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;

        // If the first NB in the NBL is drop-worthy, then all NBs are
        if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
            nbl->Next = drop;
            drop = nbl;
            nbl->Status = NDIS_STATUS_FAILURE; // tell the protocol
        } else {
            nbl->Next = keep;
            keep = nbl;
        }
    }

    // Above would reverse the order of packets; let's undo that here.
    keep = ReverseNblChain(keep);

    . . . do something with the NBLs you want to keep. . .;

    // Send the keepers down the stack to be transmitted by the NIC.
    NdisFSendNetBufferLists(context, keep, portNumber, sendFlags);

    // Return the dropped packets back up to whoever tried to send them.
    NdisFSendCompleteNetBufferLists(context, drop, 0);
}

在接收路径上,您可以保证每个NET_BUFFER_LIST只有一个NET_BUFFER。 (NIC无法完全知道哪些数据包是同一个数据流的一部分,因此尚未进行任何分组。)所以这个小问题已经消失,但是有一个新问题:你必须检查NDIS_RECEIVE_FLAGS_RESOURCES标志。不检查此标志是在过滤器驱动程序中追逐错误的第一个原因,因此我必须对此做出重大决定。

void
FilterReceiveNetBufferLists(NET_BUFFER_LIST *nblChain, ULONG count, ULONG receiveFlags)
{
    NET_BUFFER_LIST *drop = NULL;
    NET_BUFFER_LIST *keep = NULL;

    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;

        // There's only one packet in the NBL
        if (MyShouldDropPacket(nbl->FirstNetBuffer)) {
            nbl->Next = drop;
            drop = nbl;
            count -= 1; // decrement the NumberOfNetBufferLists
        } else {
            nbl->Next = keep;
            keep = nbl;
        }
    }

    keep = ReverseNblChain(keep);

    . . . do something with the NBLs you want to keep. . .;

    // Pass the keepers up the stack to be processed by protocols.
    NdisFIndicateReceiveNetBufferLists(context, keep, portNumber, count, receiveFlags);

    // Checking this flag is critical; never ever call
    // NdisFReturnNetBufferLists if the flag is set.
    if (0 == (NDIS_RECEIVE_FLAGS_RESOURCES & receiveFlags)) {
        NdisFReturnNetBufferLists(context, keep, 0);
    }
}

请注意,我使用了一个名为ReverseNblChain的辅助函数。逆转数据包的顺序在技术上是合法的,但它会破坏性能。当数据包通常按顺序到达时,TCPIP只能达到最佳性能。示例代码中的链接列表操作循环具有反转NBL列表的副作用,因此我们使用ReverseNblChain撤消损坏。我们不需要反转丢弃链,因为没有人试图重新组合丢弃的数据包;你可以按任何顺序留下它们。

NET_BUFFER_LIST * ReverseNblChain(NET_BUFFER_LIST *nblChain)
{
    NET_BUFFER_LIST *head = NULL;
    NET_BUFFER_LIST *next = NULL;
    NET_BUFFER_LIST *nbl = NULL;

    for (nbl = nblChain; nbl != NULL; nbl = next) {
        next = nbl->Next;
        nbl->Next = head;
        head = nbl;
    }

    return head;
}

最后,如果您将来读取这些内容,我建议您查找Microsoft名为nblutil.h的示例头文件。 (我们还没有发表它,但我正在研究它。)它有一个非常好的例程,名为ndisClassifyNblChain,几乎可以为你完成所有的工作。它是为高可扩展性而设计的,并且使用了一些技巧来获得更好的性能,而不是你已经发现的已经过时的StackOverflow答案。

© www.soinside.com 2019 - 2024. All rights reserved.