ZMap扫描机制剖析 – 作者:Murkfox

*本文作者:Murkfox,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

前言

随着企业内网的网络资产不断增加,对实时监控网络资产变化的要求不断提升。以 Nmap 为代表的一代端口扫描器的在扫描速率上已经无法满足日常需要。随之以 Zmap 为代表的全网级快速端口扫描器被大家所熟知并加以应用。本文主要通过分析 ZMap 的工作机制以及发包机制来探讨如何构造全网级快速端口扫描器。

Zmap介绍

首先我们看一下 Zmap 的系统结构图:

ZMap 将各个功能模块化,这增加了 ZMap 的灵活性,并可以轻松的集成其他的工具,以便构造自己的网络资产探测系统。

如图所示,扫描器的核心部分由命令、配置文件解析模块、目标 IP 列表生成模块、程序监控模块以及数据包发送接收模块共同组成。

ZMap针对于不同的通信协议,采用不同的探针模块去验证,目标设备的存活。我们也扩展探针模块可以针对不同类型的探针进行定制,同时负责生成探测包并解释传入的包是否是有效响应。

模块化输出处理程序允许将扫描结果通过管道传输到另一个进程,或直接添加到数据库,或传递给用户代码以进行进一步操作,例如 Zgrab。

在 ZMap 的设计理念中,最重要的一点是做到了发送和接收数据包的线程独立化。这尽可能的减少了带宽的共享损耗,同时提高了线程的效率。

通过上述分析得知,ZMap 框架设计中,异步处理以及功能模块化,大大的提高了程序的运行速度以及灵活性。但这并不是 ZMap 可以进行全网级设备发现的核心功能(但这是基础)。下面我通过翻译 ZMap 的论文,给大家阐述 ZMap 等全网级端口扫描器,在扫描机制上所作出的改变和突破。

Zmap扫描机制解析

地址生成分片

ZMap 使用整数模 n 乘法群算法建立覆盖 IPv4 地址空间的全排列。

IPv4 地址为 128 bit 换算成十进制即 1~2^32 整数 所以 ZMap 通过选择 p 为 2^32 + 15,再从需要遍历的数列中选择一个原始根 g ,通过将待排序的数字 n 于 g 相乘之后再与p相除。(原理如图所示)

通过建立十进制 1^32 整数的全排列,再将每个整数转换成128 bit 的 IPv4 地址,就可以得到覆盖 IPv4 地址空间的排列。

为了使每次扫描任务,生成的地址空间均是不同的序列, ZMap 要为每次扫描先生成一个新的随机的原始根 g。虽然此过程在扫描开始时增加了复杂性,但它只增加了少量的一次性的开销。一旦生成了原始根 g,ZMap 就可以轻松完成地址空间序列的为随机排序。

黑白名单

ZMap 目标地址约束用于限制扫描到网络的特定区域(白名单)或排除特定地址范围(黑名单),例如IANA保留分配。 黑名单还可用于满足希望被排除在接收探测流量之外的网络运营商的请求。 良好的互联网公民身份要求 ZMap 用户支持这些请求,但是后来人们可能会延长时间,而用户的黑名单可能包含数百个被排除的前提条件。 即使有复杂的地址约束,ZMap 也能够有效地确定任何给定的IP地址是否应该是扫描的一部分。 – 为了支持 10 GigE linepeed ,我们实现了基于三维和数组的数据结构,可以有效地操作和查询允许的地址。

NIC零拷贝

尽管ZMap可以直接生成原始以太网套接字(减少了一系列的数据包校验过程如arp 缓存查询和 Netfilter 检验,并且不会保留TCP 会话状态),但是发送探测数据包本身就是一项昂贵的操作,因为它涉及到一个操作系统,需要将扫描数据包通过用户空间传到内核空间,再将扫描数据包通过内核空间传输到NIC 。

测试过多种传统的减少开支的方案后,这些内核操作的高成本也使 ZMap 无法达到 2Gbps 以上的发包速度。为了减少这样的限制,加快数据包的发送速度, ZMap 使用 PF_RING™ZC 接口实现 ZMap 的数据包发送功能。

PF_RING™ZC 允许用户空间代码绕过内核并对NIC进行直接的“零拷贝”访问,从而可以在没有任何上下文切换或浪费内存带宽的情况下发送数据包。

但由于 PF_RING™ZC 的发包速度极快,若数据包创建与发送的线程相关联,则数据包创建的速度无法达到发包速度,但同时开启多个 PF_RING™ZC 的工作线程又会降低ZMap的工作效率。因此 ZMap 又在 PF_RING 的基础上将数据包创建与发送解耦,创建了新的发包机制。并使ZMap能够将IP段分片的并行化优势与 PF_RING™ZC 的速度相结合,使 ZMap 拥有了支持 10 G NIC 的功能。

在ZMap的原始版本中,创建的每个发包线程都会通过特定于该线程的数据包生成线程,生成并发送数据包。

但 ZMap 在接下来的版本修改了线程职责,使每个数据包生成线程迭代一个 IP 地址列表的分片并生成数据包对其进行排队。 在紧密循环中,每个数据包生成循环计算分片中的下一个索引,使用地址约束树找到相应的允许 IP 地址,并在 PF_RING™ZC 驱动程序的内存中创建一个数据包。 数据包将添加到数据包队列中。 发送线程在数据包可用时,从每个数据包队列中读取数据,并使用 PF_RING™ZC 通过线路发送它们。

让我们简单来看一下 ZMap 中 有关于 PF_RING™ZC 的应用./src/send-prfing.h

int send_packet(sock_t sock, void *buf, int len, uint32_t idx)
{
	sock.pf.buffers[idx]->len = len;
	memcpy(pfring_zc_pkt_buff_data(sock.pf.buffers[idx], sock.pf.queue),
	       buf, len);
	int ret;
	do {
		ret =
		    pfring_zc_send_pkt(sock.pf.queue, &sock.pf.buffers[idx], 0);
	} while (ret < 0);
	return ret;
}

在发包的过程中,ZMap 首先调用 memcpy()复制由 pfring_zc_pkt_buff_data()获得的指向实际数据包数据的指针。 再调用 pfring_zc_send_pkt ()将数据包送入发送队列,并将指针指向待发送的缓冲区句柄,只有发送了数据包,才可以重用缓冲区句柄。

为了确定最佳数据包生成线程数,ZMap 执行了一系列测试,使用 1-6 个数据包创建线程扫描 50 秒,并测量发送速率。 找到最佳线程数,即对为每个物理核分配一个数据包生成线程。

当然还要提到的一点就是,ZMap 发送的请求是无状态请求,发送之后就忽略了,它不需要记录未回应请求的名单,而是在发送的数据包中编码身份信息去识别回应,从而降低开销。(大家就比较了解,我就不一一赘述了)

我们发现,ZMap 在实现超高速率发包时,使用了一个新的数据包处理框架 PF_RING™ZC 这一数据包处理框架,实现了 数据包生成后直接传入 NIC 的功能(Zero Copy)。这大大减少了对计算资源的消耗,同时大大提升了网卡的工作效率。接下来,我将通过简单地翻译 PF_RING™ZC 官方文档来给大家介绍一下 PF_RING™ZC

PF_RING™ZC介绍

PF_RING™ZC(零拷贝)是一种灵活的数据包处理框架,允许您以任何数据包大小实现1/10 Gbit 线速数据包处理( RX 和 TX )。 它实现零拷贝操作,包括进程间和 VM 间(KVM)通信的模式。 它可以被视为 DNA / LibZero 的继承者,它基于过去几年的经验教训提供单一且一致的 API 。

它具有灵活的API,可实现可从线程、应用程序和虚拟机,使用的简单构建模块(队列,工作线程和池)创建复杂的应用程序。 例如通过六行代码创建一个简单的设备应用:

zc = pfring_zc_create_cluster(ID, MTU, MAX_BUFFERS, NULL);

for (i = 0; i < num_devices; i++)

  inzq[i] = pfring_zc_open_device(zc, devices[i], rx_only);

for (i = 0; i < num_slaves; i++)

  outzq[i] = pfring_zc_create_queue(zc, QUEUE_LEN);

 zw = pfring_zc_run_balancer(inzq, outzq, num_devices, num_slaves,NULL, NULL, !wait_for_packet, core_id)

PF_RING™ZC 附带新一代 PF_RING™ 感知驱动程序,可在内核或旁路模式下使用。 安装后,驱动程序作为标准 Linux 驱动程序运行,您可以在其中进行正常的网络连接(例如 ping 或 SSH )。 当从 PF_RING™ 使用时,它们比其他的驱动更快,因为它们直接与它相互作用。 如果您使用零拷贝中的 PF_RING™ 感知驱动程序打开设备(例如 pfcount -i zc:eth1),则设备将无法用于标准网络,因为它通过内核旁路以零拷贝方式访问,就像之前的 DNA 一样。 关闭访问设备的应用程序后,可以再次进行标准网络活动。

有兴趣想深入了解的童鞋,可以度娘一下以及查阅下文的参考链接。

总结

本文通过对 ZMap 工作机制进行初步的剖析,并未深入每个函数进行讲解,心力、能力有限,望各位海涵。

综上所述,若想自己打造一个全网级端口扫描器,其实还是有(nan)些(yu)许(shang)难(qing)度(tian)的。但好在,ZMap、Masscan 均为开源程序,并提供了一系列的 API 供大家进行扩展,这大大的降低了我们对扫描器的改造成本。PF_RING™ZC 同样也是开源的,想深入了解学习的同学,也可以根据参考文档进行深入的研究和探讨。(附上本人整理的参考链接)

ZMap at GitHub

ZMap: Fast Internet-Wide Scanning and its Security Applications

Masscan at GitHub

PR_RING at GitHub

PF_RING™ZC

pfring_zc.h File Reference

*本文作者:Murkfox,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

来源:freebuf.com 2019-04-19 15:00:43 by: Murkfox

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论