原始以太网数据包发送






4.92/5 (84投票s)
2003 年 10 月 25 日
5分钟阅读

600213

10669
本文解释了如何使用 C# 和 NDIS 协议驱动程序发送原始以太网数据包。
引言
本文的目的是解释如何在 Microsoft 平台上的 C# 中发送原始以太网数据包。原始以太网数据包是发送到物理线路的完整第 2 层网络帧。发送此类帧允许您操作目标和源 MAC 地址以及第 3 层协议字段。
背景
您可能会想:“谁会想这样做呢?”。好吧,我正在尝试创建一个应用程序(使用 C#),它可以让一台典型的带有 2 个网卡的 Windows 计算机充当第 2 层网络设备。我的目标是在一个网络接口上侦听数据包,然后将**完全相同的数据包**从另一个接口发送出去,基本上就是一个数据包中继器。要做到这一点,我需要能够读取原始以太网数据包(这很容易),然后编写相同的原始以太网数据包(这很难)。发送的数据包需要**完全**与读取的数据包一样,包括以太网头。我在网上做了大量的研究,但没有找到太多信息,只是零星的线索。
第一个问题是 Windows 没有提供以编程方式发送原始以太网数据包的方法。经过一些研究,我意识到我需要创建一个 NDIS 协议驱动程序(PassThru 和 Intermediate 驱动程序也可以)来与网络适配器进行非常底层的交互。幸运的是,Windows Driver Development Kits (DDK) 提供了可以为我完成此任务的示例。太好了,最难的部分解决了……嗯,我也是这么想的。现在我需要从托管的 C# 代码与驱动程序进行交互。
好了,背景介绍就到这里……让我们开始编写代码……
第一部分 - NDIS 协议驱动程序
所以,就像我说的,DDK 提供了一个合适的 NDIS 驱动程序用于发送原始数据包。我编译了它,创建了驱动程序的 .inf 和 .sys 文件(我在附件的 zip 文件中包含了经过修改以满足我需求的已编译驱动程序)。经过一些测试,我发现我
- 只能接收发往我的数据包,并且
- 我只能发送源地址是我的适配器的数据包。
嗯,这是不可接受的。我需要接收我 LAN 网段上的任何数据包,并发送相同的数据包,而不考虑源地址。因此,在查看了驱动程序代码后,我找到了完成此任务的方法。
为了接收任何数据包,驱动程序必须设置为混杂模式。以下代码段就是为实现此目的而修改的。
// ndisprot.h // line 177 // Add NDIS_PACKET_TYPE_PROMISCUOUS to support promiscuous mode reading #define NUIOO_PACKET_FILTER (NDIS_PACKET_TYPE_DIRECTED| \ NDIS_PACKET_TYPE_MULTICAST| \ NDIS_PACKET_TYPE_BROADCAST| \ NDIS_PACKET_TYPE_PROMISCUOUS) // **Added**
为了发送任何数据包,必须注释掉以下代码段
// send.c // line 136 // Comment out to support sending packets from any MAC source address // To prevent applications from sending packets with spoofed // mac address, we will do the following check to make sure the source // address in the packet is same as the current MAC address of the NIC. // if ((pIrp->RequestorMode == UserMode) && !NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) { DEBUGP(DL_WARN, ("Write: Failing with invalid Source address")); NtStatus = STATUS_INVALID_PARAMETER; break; }
做出这些更改后,NDIS 驱动程序对我所需的功能来说表现完美。
第二部分 - C# RawEthernet 应用程序
RawEthernet 应用程序的代码注释相当详细,所以我在这里不会过多地深入代码细节。我只会重点介绍代码中的重要步骤。
向设备驱动程序写入信息在某种程度上类似于向文件写入。我们通过调用 CreateFile
API 打开驱动程序文件。这会返回一个句柄,我们可以使用该句柄从驱动程序读取和写入。接下来,我们可以使用 DeviceIoControl
API 将驱动程序句柄绑定到特定的适配器。绑定适配器允许我们访问特定网络适配器上的 NDIS 驱动程序。完成所有这些之后,写入就变得很简单了。我们使用 WriteFile
API。ReadFile
API 也可以以类似的方式用于读取传入的网络数据。
要发送数据包,我们必须创建要发送的数据包的字节表示。以下显示了以字节格式的以太网头(数据包的前 14 个字节)
DD DD DD DD DD DD SS SS SS SS SS SS PP PP <data follows>
- D = 目标 MAC 地址
- S = 源 MAC 地址
- P = 下一层协议 (0800 = IP)
您可以使用数据包嗅探器(Ethereal、Snoop、EtherPeeks)来验证您是否在网络介质上发送了原始数据包。此应用程序当前发送的数据包是一个非常简单的数据包,除了展示概念外,没有其他任何用途。可以轻松地将其更改为反映真实数据包,例如 ping 或您可以想到的任何其他内容。
运行示例
NDIS 驱动程序
您可以通过打开网络适配器属性,然后单击“安装”按钮,选择“协议”,然后选择“从磁盘安装”。然后浏览到 .inf 文件并单击“确定”来安装 NDIS 驱动程序。这将把驱动程序加载到您系统中的每个适配器上。
重要 - 确保它已启用,下一个“原始数据包 NDIS 协议驱动程序”旁边的框应该打了勾。
重要 - 打开命令提示符并键入“net start ndisprot”来启动驱动程序服务。
注意 - 此驱动程序的优点在于,您可以禁用适配器协议列表中的所有其他协议(例如 Internet 协议),而您仍然可以发送和接收数据包。您的计算机甚至不会有一个地址,但由于我们工作在第 2 层,所以您不需要地址。(即使您保留所有其他协议启用,此驱动程序也可以正常工作)
RawEthernet 应用程序
zip 文件包含 RawEthernet 应用程序的源代码和已编译的二进制文件。驱动程序安装并启用后,只需运行 EXE 即可看到数据包正在发送。
其他事项
我一直在努力使它能够异步工作,以便我可以在同一适配器上同时发送和接收。当我有多余的时间时,我将发布一篇关于异步文件如何工作的文章。
目前就这些……如果您有任何问题或意见,请随时与我联系。