1 前言

WindivertDotnet是面向对象的WinDivert的dotnet异步封装,其提供如下的发送数据方法:

  1. ValueTask<int> SendAsync(
  2. WinDivertPacket packet,
  3. WinDivertAddress addr,
  4. 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 是否为出口方向
  1. // 使用dstAddr创建router
  2. var router = new WinDivertRouter(dstAddr);

2.2 创建WinDivertAddress

WinDivertAddress的如下属性必须要设置正确,它是IP数据包构建链路数据包必须的项:

属性 说明
WinDivertAddress.NetWork->IfIdx 发包的网络适配器的索引
WinDivertAddress.Flags.OutboundFlag 是否为出口方向
WinDivertAddress.Flags.LoopbackFlag 是否为回环
  1. // 使用router创建WinDivertAddress
  2. using WinDivertAddress addr = router.CreateAddress();

2.3 创建WinDivertPacket

因为从router里知道了源IP和目标IP,所以创建ICMP ping功能的WinDivertPacket就比较容易。

  1. /// <summary>
  2. /// 创建icmp的echo包
  3. /// </summary>
  4. /// <param name="srcAddr"></param>
  5. /// <param name="dstAddr"></param>
  6. /// <returns></returns>
  7. private unsafe WinDivertPacket CreateIPV4EchoPacket(IPAddress srcAddr, IPAddress dstAddr)
  8. {
  9. // ipv4头
  10. var ipHeader = new IPV4Header
  11. {
  12. TTL = 128,
  13. Version = 4,
  14. DstAddr = dstAddr,
  15. SrcAddr = srcAddr,
  16. Protocol = ProtocolType.Icmp,
  17. HdrLength = (byte)(sizeof(IPV4Header) / 4),
  18. Id = ++this.id,
  19. Length = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header))
  20. };
  21. // icmp头
  22. var icmpHeader = new IcmpV4Header
  23. {
  24. Type = IcmpV4MessageType.EchoRequest,
  25. Code = default,
  26. Identifier = ipHeader.Id,
  27. SequenceNumber = ++this.sequenceNumber,
  28. };
  29. // 将数据写到packet缓冲区
  30. var packet = new WinDivertPacket(ipHeader.Length);
  31. var writer = packet.GetWriter();
  32. writer.Write(ipHeader);
  33. writer.Write(icmpHeader);
  34. return packet;
  35. }

2.4 发出数据包

现在我们可使用Windivert对象,将为每个目的地IP创建的WinDivertPacketWinDivertAddress两个对象发送出去:

  1. /// <summary>
  2. /// 发送icmp的echo请求包
  3. /// </summary>
  4. /// <param name="dstAddrs"></param>
  5. /// <returns></returns>
  6. private async Task SendEchoRequestAsync(IEnumerable<IPAddress> dstAddrs)
  7. {
  8. foreach (var address in dstAddrs)
  9. {
  10. // 使用router计算将进行通讯的本机地址
  11. var router = new WinDivertRouter(address);
  12. using var addr = router.CreateAddress();
  13. using var packet = this.CreateIPV4EchoPacket(router.SrcAddress, router.DstAddress);
  14. packet.CalcChecksums(addr); // 计算checksums,因为创建包时没有计算
  15. await this.divert.SendAsync(packet, addr);
  16. }
  17. }

3 接收回复包

3.1 Filter

我们可以使用过滤器,将接收的内容过滤为icmp,并且数据是入口方向,必要不必要的数据到达我们的应用层而增加了处理负担:

  1. // 只接受进入系统的icmp
  2. var filter = Filter.True.And(f => f.IsIcmp && f.Network.Inbound);
  3. this.divert = new WinDivert(filter, WinDivertLayer.Network);

3.2 接收数据

接收数据这个就简单了,这是WindivertDotnet最擅长的技能:

  1. /// <summary>
  2. /// 监听ping的回复
  3. /// </summary>
  4. /// <param name="cancellationToken">取消令牌</param>
  5. /// <returns></returns>
  6. private async Task<HashSet<IPAddress>> RecvEchoReplyAsync(CancellationToken cancellationToken)
  7. {
  8. var results = new HashSet<IPAddress>();
  9. using var packet = new WinDivertPacket();
  10. using var addr = new WinDivertAddress();
  11. while (cancellationToken.IsCancellationRequested == false)
  12. {
  13. try
  14. {
  15. await this.divert.RecvAsync(packet, addr, cancellationToken);
  16. if (TryGetEchoReplyAddr(packet, out var value))
  17. {
  18. results.Add(value);
  19. }
  20. // 把packet发出,避免系统其它软件此刻也有ping而收不到回复
  21. await this.divert.SendAsync(packet, addr, cancellationToken);
  22. }
  23. catch (OperationCanceledException)
  24. {
  25. break;
  26. }
  27. }
  28. return results;
  29. }

3.3 解析回复的IP

  1. /// <summary>
  2. /// 解析出icmp回复信息
  3. /// </summary>
  4. /// <param name="packet">数据包</param>
  5. /// <param name="value">回复的IP</param>
  6. /// <returns></returns>
  7. private unsafe static bool TryGetEchoReplyAddr(WinDivertPacket packet, [MaybeNullWhen(false)] out IPAddress value)
  8. {
  9. var result = packet.GetParseResult();
  10. if (result.IcmpV4Header != null &&
  11. result.IcmpV4Header->Type == IcmpV4MessageType.EchoReply)
  12. {
  13. value = result.IPV4Header->SrcAddr;
  14. return true;
  15. }
  16. else if (result.IcmpV6Header != null &&
  17. result.IcmpV6Header->Type == IcmpV6MessageType.EchoReply)
  18. {
  19. value = result.IPV6Header->SrcAddr;
  20. return true;
  21. }
  22. value = null;
  23. return false;
  24. }

4 整合数据

我们需要一个线程来开启接收ping回复,同时另一个线程把所有ping发出去,最后拿ping的所有IP和ping回复的所有IP求交集,就是我们需要的结果。


  1. /// <summary>
  2. /// Ping所有地址
  3. /// 占用两个线程
  4. /// </summary>
  5. /// <param name="dstAddrs">目标地址</param>
  6. /// <param name="delay">最后一个IP发出ping之后的等待回复时长</param>
  7. /// <returns></returns>
  8. public async Task<IPAddress[]> PingAllAsync(IEnumerable<IPAddress> dstAddrs, TimeSpan delay)
  9. {
  10. // 开始监听ping的回复
  11. using var cts = new CancellationTokenSource();
  12. var recvTask = this.RecvEchoReplyAsync(cts.Token);
  13. // 对所有ip发ping
  14. await this.SendEchoRequestAsync(dstAddrs);
  15. // 延时取消监听
  16. cts.CancelAfter(delay);
  17. var results = await recvTask;
  18. // 清洗数据
  19. return results.Intersect(dstAddrs).ToArray();
  20. }

后记

通过WindivertDotnet的路由,无中生有IP数据包,并可以将其正确的发送的指定的目的地IP地址。像本示例的这个Ping方式,10秒ping完1万个IP并拿到其回复的IP是非常轻松的。

WindivertDotnet快速发Ping的更多相关文章

  1. linux ping 命令解析

    不管在windows平台,还是在linux平台,ping都是非常常用的网络命令:ping命令通过ICMP(Internet控制消息协议)工作:ping可以用来测试本机与目标主机是否联通.联通速度如何. ...

  2. Ubuntu CEPH快速安装

    一.CEPH简介 不管你是想为云平台提供Ceph 对象存储和/或 Ceph 块设备,还是想部署一个 Ceph 文件系统或者把 Ceph 作为他用,所有 Ceph 存储集群的部署都始于部署一个个 Cep ...

  3. Linux下ping命令参数详细解析

    -a Audible ping. #Audible ping. -A Adaptive ping. Interpacket interval adapts to round-trip time, so ...

  4. Python ping 模块

    使用socket模块也可以获得域名对应的ip,参考:https://blog.csdn.net/c465869935/article/details/50850598 print socket.get ...

  5. Linux命令ping

    原文 ping命令用来测试主机之间网络的连通性.执行ping指令会使用ICMP传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常. 语法 ping(选项 ...

  6. 阿里云移动研发平台 EMAS 助力银行业打造测试中台,提升发版效能

    随着移动互联网的发展,手机银行凭借低成本.操作简单.不受时间空间约束等优势,正逐步替代传统的网银交易方式.越来越多的银行开始了“业务移动化”转型之路,“手机APP”已经成为企业价值传递和关系维护的关键 ...

  7. 简单实现一个快速传输电子书到kindle的小项目

    前言 最近翻出来好久没有看的kindle,准备继续我的阅读之路.当然,也是因为发现了一个非常好的获取电子书资源的网站,又燃起了我的阅读兴趣. 然而,往kindle里传输电子书的方式一共有四种: 直接在 ...

  8. openfire极限优化

    日志优化   默认是 用info 级别,最好不用openfire原生的打日志方式.   离线消息用存储不打回方式,不要用打回方式   xmpp.offline.type=store_and_drop ...

  9. HTML5的Websocket(理论篇 I)

    HTML5的Websocket(理论篇 I) ** 先请来TA的邻居:** http:无状态.基于tcp请求/响应模式的应用层协议 (A:哎呀,上次你请我吃饭了么? B:我想想, 上次请你吃了么) t ...

随机推荐

  1. Blazor和Vue对比学习(进阶2.2.3):状态管理之状态共享,Blazor的依赖注入和第三方库Fluxor

    Blazor没有提供状态共享的方案,虽然依赖注入可以实现一个全局对象,这个对象可以拥有状态.计算属性.方法等特征,但并不具备响应式.比如,组件A和组件B,都注入了这个全局对象,并引用了全局对象上的数据 ...

  2. Thinhole类声明和实现

    Thinhole类说白了就是在眼睛处,放一个放大镜.就像我们平时用放大镜观察物体一样.这样实现的效果的是,周围会模糊.原理书上都说的很清楚了,我把算法截图下来了.这个应用我猜测是在竞技游戏比如csgo ...

  3. 多线程与高并发(五)—— 源码解析 ReentrantLock

    一.前言 ReentrantLock 是基于 AQS 实现的同步框架,关于 AQS 的源码在 这篇文章 已经讲解过,ReentrantLock 的主要实现都依赖AQS,因此在阅读本文前应该先了解 AQ ...

  4. 【安全通报】DolphinScheduler 漏洞情况说明

    Apache DolphinScheduler 社区邮件列表最近通告了 2 个漏洞,考虑到有很多用户并未订阅此邮件列表,我们特地在此进行情况说明: CVE-2020-11974[1] 漏洞 (CVE- ...

  5. DolphinScheduler 源码剖析之 Master 容错处理流程

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

  6. Luogu3243 [HNOI2015]菜肴制作 (拓扑排序)

    题面毒人,其实就是叫你反图跑拓扑 #include <iostream> #include <cstdio> #include <cstring> #include ...

  7. Redis 14 发布订阅

    参考源 https://www.bilibili.com/video/BV1S54y1R7SB?spm_id_from=333.999.0.0 版本 本文章基于 Redis 6.2.6 概述 Redi ...

  8. mac M1通过homebrew安装python3报错Error: Command failed with exit 128: git

    fatal: not in a git directoryError: Command failed with exit 128: git 只需要运行 git config --global --ad ...

  9. 并发编程二、CPU多级缓存架构与MESI协议的诞生

    ​前言: 文章内容:线程与进程.线程生命周期.线程中断.线程常见问题总结 本文章内容来源于笔者学习笔记,内容可能与相关书籍内容重合 偏向于知识核心总结,非零基础学习文章,可用于知识的体系建立,核心内容 ...

  10. 牛客小白月赛51-C-E

    C-零一题 题意: 每次可以选择两个相邻且相同的字符,将他们删除,在无数次操作后,字符串的长度变为n,问能否构造出原来的字符串,不能输出-1 题解: 很明显,最后无法再操作时,这个字符串一定是01相交 ...