WindivertDotnet快速发Ping

1 前言WindivertDotnet是面向对象的WinDivert的dotnet异步封装,其提供如下的发送数据方法:
ValueTask<int> SendAsync(WinDivertPacket packet,WinDivertAddress addr,CancellationToken cancellationToken)在修改包的场景,我们通过RecvAsync()方法获取具有内容的WinDivertPacketWinDivertAddress对象实例,简单修改这两个对象的一些值之后,就可以发送出去 。
但在注入的场景,我们需要无中生成WinDivertPacketWinDivertAddress两个对象,前者是IP包的完整数据,后者主要指示数据要经过的网络适配器的索引、数据是入口还是出口方向、是否为loopback等信息,下面我将使用WindivertDotnet来开发一个批量Ping功能的示例来教大家怎么注入数据包 。
2 发出Ping包2.1 路由计算在发Ping的场景中,我们只知道目的地IP地址,WinDivertRouter对象可以帮们提前算出路由信息,得到以下表格的内容:
属性说明IPAddress DstAddress目的地IP地址IPAddress SrcAddress源IP地址int InterfaceIndex经过的网络适配器的索引bool IsOutbound是否为出口方向// 使用dstAddr创建routervar router = new WinDivertRouter(dstAddr);2.2 创建WinDivertAddress【WindivertDotnet快速发Ping】WinDivertAddress的如下属性必须要设置正确,它是IP数据包构建链路数据包必须的项:
属性说明WinDivertAddress.NetWork->IfIdx发包的网络适配器的索引WinDivertAddress.Flags.OutboundFlag是否为出口方向WinDivertAddress.Flags.LoopbackFlag是否为回环// 使用router创建WinDivertAddressusing WinDivertAddress addr = router.CreateAddress();2.3 创建WinDivertPacket因为从router里知道了源IP和目标IP,所以创建ICMP ping功能的WinDivertPacket就比较容易 。
/// <summary>/// 创建icmp的echo包/// </summary>/// <param name="srcAddr"></param>/// <param name="dstAddr"></param>/// <returns></returns>private unsafe WinDivertPacket CreateIPV4EchoPacket(IPAddress srcAddr, IPAddress dstAddr){// ipv4头var ipHeader = new IPV4Header{TTL = 128,Version = 4,DstAddr = dstAddr,SrcAddr = srcAddr,Protocol = ProtocolType.Icmp,HdrLength = (byte)(sizeof(IPV4Header) / 4),Id = ++this.id,Length = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header))};// icmp头var icmpHeader = new IcmpV4Header{Type = IcmpV4MessageType.EchoRequest,Code = default,Identifier = ipHeader.Id,SequenceNumber = ++this.sequenceNumber,};// 将数据写到packet缓冲区var packet = new WinDivertPacket(ipHeader.Length);var writer = packet.GetWriter();writer.Write(ipHeader);writer.Write(icmpHeader);return packet;}2.4 发出数据包现在我们可使用Windivert对象,将为每个目的地IP创建的WinDivertPacketWinDivertAddress两个对象发送出去:
/// <summary>/// 发送icmp的echo请求包/// </summary>/// <param name="dstAddrs"></param>/// <returns></returns>private async Task SendEchoRequestAsync(IEnumerable<IPAddress> dstAddrs){foreach (var address in dstAddrs){// 使用router计算将进行通讯的本机地址var router = new WinDivertRouter(address);using var addr = router.CreateAddress();using var packet = this.CreateIPV4EchoPacket(router.SrcAddress, router.DstAddress);packet.CalcChecksums(addr);// 计算checksums,因为创建包时没有计算await this.divert.SendAsync(packet, addr);}}3 接收回复包3.1 Filter我们可以使用过滤器,将接收的内容过滤为icmp,并且数据是入口方向,必要不必要的数据到达我们的应用层而增加了处理负担:
// 只接受进入系统的icmpvar filter = Filter.True.And(f => f.IsIcmp && f.Network.Inbound);this.divert = new WinDivert(filter, WinDivertLayer.Network);3.2 接收数据接收数据这个就简单了,这是WindivertDotnet最擅长的技能:

经验总结扩展阅读