ZMap被设计用来针对整个IPv4地址空间或其中的大部分实施综合扫描的工具。ZMap是研究者手中的利器,但在运行ZMap时,请注意,您颇有 可能正在以每秒140万个包的速度扫描整个IPv4地址空间 。咱们建议用户即便在实施小范围扫描以前,也联系一下本地网络的管理员并参考咱们列举的最佳扫描体验。html
默认状况下,ZMap会对于指定端口实施尽量大速率的TCP SYN扫描。较为保守的状况下,对10,000个随机的地址的80端口以10Mbps的速度扫描,以下所示:linux
$ zmap --bandwidth=10M --target-port=80 --max-targets=10000 --output-file=results.csv
或者更加简洁地写成:ios
$ zmap -B 10M -p 80 -n 10000 -o results.csv
ZMap也可用于扫描特定子网或CIDR地址块。例如,仅扫描10.0.0.0/8和192.168.0.0/16的80端口,运行指令以下:git
zmap -p 80 -o results.csv 10.0.0.0/8 192.168.0.0/16
若是扫描进行的顺利,ZMap会每秒输出相似如下内容的状态更新:github
0% (1h51m left); send: 28777 562 Kp/s (560 Kp/s avg); recv: 1192 248 p/s (231 p/s avg);hits: 0.04%
0% (1h51m left); send: 34320 554 Kp/s (559 Kp/s avg); recv: 1442 249 p/s (234 p/s avg);hits: 0.04%
0% (1h50m left); send: 39676 535 Kp/s (555 Kp/s avg); recv: 1663 220 p/s (232 p/s avg);hits: 0.04%
0% (1h50m left); send: 45372 570 Kp/s (557 Kp/s avg); recv: 1890 226 p/s (232 p/s avg);hits: 0.04%
这些更新信息提供了扫描的即时状态并表示成:redis
完成进度% (剩余时间); send: 发出包的数量 即时速率 (平均发送速率); recv: 接收包的数量 接收率 (平均接收率); hits: 命中率
若是您不知道您所在网络能支持的扫描速率,您可能要尝试不一样的扫描速率和带宽限制直到扫描效果开始降低,借此找出当前网络可以支持的最快速度。json
默认状况下,ZMap会输出不一样IP地址的列表(例如,根据SYN ACK数据包的状况),像下面这样。其输出结果还有几种附加的格式(如,JSON和Redis),能够用做生成程序可解析的扫描统计。 一样,能够指定附加的输出字段并使用输出过滤来过滤输出的结果。缓存
115.237.116.119
23.9.117.80
207.118.204.141
217.120.143.111
50.195.22.82
咱们强烈建议您使用黑名单文件,以排除预留的/未分配的IP地址空间(如,RFC1918 规定的私有地址、组播地址),以及网络中须要排除在您扫描以外的地址。默认状况下,ZMap将采用位于 /etc/zmap/blacklist.conf
的这个简单的黑名单文件中所包含的预留和未分配地址。若是您须要某些特定设置,好比每次运行ZMap时的最大带宽或黑名单文件,您能够在文件/etc/zmap/zmap.conf
中指定或使用自定义配置文件。服务器
若是您正试图解决扫描的相关问题,有几个选项能够帮助您调试。首先,您能够经过添加--dryrun
实施预扫,以此来分析包可能会发送到网络的何处。此外,还能够经过设置'--verbosity=n`来更改日志详细程度。网络
咱们为针对互联网进行扫描的研究者提供了一些建议,以此来引导养成良好的互联网合做氛围。
即便不声明,使用扫描的研究者也应该避免利用漏洞或访问受保护的资源,并遵照其辖区内任何特殊的法律规定。
这些选项是实施简单扫描时最经常使用的选项。咱们注意到某些选项取决于所使用的探测模块或输出模块(如,在实施ICMP Echo扫描时是不须要使用目的端口的)。
-p, --target-port=port
要扫描的目标TCP端口号(例如,443)
-o, --output-file=name
将结果写入该文件,使用-
表明输出到标准输出。
-b, --blacklist-file=path
文件中被排除的子网使用CIDR表示法(如192.168.0.0/16),一个一行。建议您使用此方法排除RFC 1918地址、组播地址、IANA预留空间等IANA专用地址。在conf/blacklist.example中提供了一个以此为目的示例黑名单文件。
-n, --max-targets=n
限制探测目标的数量。后面跟的能够是一个数字(例如'-n 1000),或可扫描地址空间的百分比(例如,
-n 0.1%`,不包括黑名单)
-N, --max-results=n
收到多少结果后退出
-t, --max-runtime=secs
限制发送报文的时间
-r, --rate=pps
设置发包速率,以包/秒为单位
-B, --bandwidth=bps
以比特/秒设置传输速率(支持使用后缀G,M或K(如-B 10M
就是速度10 mbps)的。设置会覆盖--rate
。
-c, --cooldown-time=secs
发送完成后等待多久继续接收回包(默认值= 8)
-e, --seed=n
地址排序种子。若是要用多个ZMap以相同的顺序扫描地址,那么就可使用这个参数。
--shards=n
将扫描分片/区,使其可多个ZMap中执行(默认值= 1)。启用分片时,--seed
参数是必需的。
--shard=n
选择扫描的分片(默认值= 0)。n的范围在[0,N),其中N为碎片的总数。启用分片时,--seed
参数是必需的。
-T, --sender-threads=n
用于发送数据包的线程数(默认值= 1)
-P, --probes=n
发送到每一个IP的探测数(默认值= 1)
-d, --dryrun
用标准输出打印出每一个包,而不是将其发送(用于调试)
-s, --source-port=port|range
发送数据包的源端口
-S, --source-ip=ip|range
发送数据包的源地址。能够仅仅是一个IP,也能够是一个范围(如,10.0.0.1-10.0.0.9)
-G, --gateway-mac=addr
数据包发送到的网关MAC地址(用以防止自动检测不工做的状况)
-i, --interface=name
使用的网络接口
ZMap容许用户指定并添加本身所须要的探测模块。 探测模块的职责就是生成要发送的探测包,并处理主机回复的响应包。
--list-probe-modules
列出可用探测模块(如tcp_synscan)
-M, --probe-module=name
选择探测模块(默认值= tcp_synscan)
--probe-args=args
向模块传递参数
--list-output-fields
列出可用的输出模块
ZMap容许用户指定和编写他们本身的输出模块。输出模块负责处理由探测模块返回的字段,并将它们输出给用户。用户能够指定输出的字段,并过滤相应字段。
--list-output-modules
列出可用输出模块(如tcp_synscan)
-O, --output-module=name
选择输出模块(默认值为csv)
--output-args=args
传递给输出模块的参数
-f, --output-fields=fields
输出的字段列表,以逗号分割
--output-filter
-C, --config=filename
加载配置文件,能够指定其余路径。
-q, --quiet
没必要每秒刷新输出
-g, --summary
在扫描结束后打印配置和结果汇总信息
-v, --verbosity=n
日志详细程度(0-5,默认值= 3)
-h, --help
打印帮助并退出
-V, --version
打印版本并退出
在执行TCP SYN扫描时,ZMap须要指定一个目标端口,也支持指定发起扫描的源端口范围。
-p, --target-port=port
扫描的TCP端口(例如 443)
-s, --source-port=port|range
发送扫描数据包的源端口(例如 40000-50000)
警示! ZMap基于Linux内核使用RST包来应答SYN/ACK包响应,以关闭扫描器打开的链接。ZMap是在Ethernet层完成包的发送的,这样作是为了减小跟踪打开的TCP链接和路由寻路带来的内核开销。所以,若是您有跟踪链接创建的防火墙规则,如相似于-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
的netfilter规则,将阻止SYN/ACK包到达内核。这不会妨碍到ZMap记录应答,但它会阻止RST包被送回,最终被扫描主机的链接会一直打开,直到超时后断开。咱们强烈建议您在执行ZMap时,选择一组主机上未使用且防火墙容许访问的端口,加在-s
后(如 -s '50000-60000'
)。
虽然在默认状况下ZMap执行的是TCP SYN扫描,但它也支持使用ICMP echo请求扫描。在这种扫描方式下ICMP echo请求包被发送到每一个主机,并以收到ICMP应答包做为答复。实施ICMP扫描能够经过选择icmp_echoscan扫描模块来执行,以下:
$ zmap --probe-module=icmp_echoscan
ZMap还额外支持UDP探测,它会发出任意UDP数据报给每一个主机,并接收UDP或ICMP不可达的应答。ZMap能够经过使用--probe- args命令行选项来设置四种不一样的UDP载荷。这些是:可在命令行设置可打印的ASCII 码的‘text’载荷和十六进制载荷的‘hex’,外部文件中包含载荷的‘file’,和经过动态字段生成的载荷的‘template’。为了获得UDP 响应,请使用-f参数确保您指定的“data”字段处于输出范围。
下面的例子将发送两个字节'ST',即PCAnwywhere的'status'请求,到UDP端口5632。
$ zmap -M udp -p 5632 --probe-args=text:ST -N 100 -f saddr,data -o -
下面的例子将发送字节“0X02”,即SQL Server的'client broadcast'请求,到UDP端口1434。
$ zmap -M udp -p 1434 --probe-args=hex:02 -N 100 -f saddr,data -o -
下面的例子将发送一个NetBIOS状态请求到UDP端口137。使用一个ZMap自带的载荷文件。
$ zmap -M udp -p 1434 --probe-args=file:netbios_137.pkt -N 100 -f saddr,data -o -
下面的例子将发送SIP的'OPTIONS'请求到UDP端口5060。使用附ZMap自带的模板文件。
$ zmap -M udp -p 1434 --probe-args=file:sip_options.tpl -N 100 -f saddr,data -o -
UDP载荷模板仍处于实验阶段。当您在更多的使用一个以上的发送线程(-T)时可能会遇到崩溃和一个明显的相比静态载荷性能下降的表现。模板仅仅是 一个由一个或多个使用${}将字段说明封装成序列构成的载荷文件。某些协议,特别是SIP,须要载荷来反射包中的源和目的包。其余协议,如 portmapper和DNS,每一个请求包含的字段应该是随机的,或下降被Zamp扫描的多宿主系统的风险。
如下的载荷模板将发送SIP OPTIONS请求到每个目的地:
OPTIONS sip:${RAND_ALPHA=8}@${DADDR} SIP/2.0
Via: SIP/2.0/UDP ${SADDR}:${SPORT};branch=${RAND_ALPHA=6}.${RAND_DIGIT=10};rport;alias
From: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT};tag=${RAND_DIGIT=8}
To: sip:${RAND_ALPHA=8}@${DADDR}
Call-ID: ${RAND_DIGIT=10}@${SADDR}
CSeq: 1 OPTIONS
Contact: sip:${RAND_ALPHA=8}@${SADDR}:${SPORT}
Content-Length: 0
Max-Forwards: 20
User-Agent: ${RAND_ALPHA=8}
Accept: text/plain
就像在上面的例子中展现的那样,注意每行行末以\r\n结尾,请求以\r\n\r\n结尾,大多数SIP实现均可以正确处理它。一个能够工做的例子放在ZMap的examples/udp-payloads目录下 (sip_options.tpl).
当前实现了下面的模板字段:
ZMap支持使用配置文件来代替在命令行上指定全部要求的选项。配置中能够经过每行指定一个长名称的选项和对应的值来建立:
interface "eth1"
source-ip 1.1.1.4-1.1.1.8
gateway-mac b4:23:f9:28:fa:2d # upstream gateway
cooldown-time 300 # seconds
blacklist-file /etc/zmap/blacklist.conf
output-file ~/zmap-output
quiet
summary
而后ZMap就能够按照配置文件并指定一些必要的附加参数运行了:
$ zmap --config=~/.zmap.conf --target-port=443
ZMap能够在屏幕上生成多种类型的输出。默认状况下,Zmap将每隔1秒打印出类似的基本进度信息。能够经过设置--quiet
来禁用。
0:01 12%; send: 10000 done (15.1 Kp/s avg); recv: 144 143 p/s (141 p/s avg); hits:1.44%
ZMap一样也能够根据扫描配置打印以下消息,能够经过'--verbosity`参数加以控制。
Aug 11 16:16:12.813 [INFO] zmap: started
Aug 11 16:16:12.817 [DEBUG] zmap: no interface provided. will use eth0
Aug 11 16:17:03.971 [DEBUG] cyclic: primitive root: 3489180582
Aug 11 16:17:03.971 [DEBUG] cyclic: starting point: 46588
Aug 11 16:17:03.975 [DEBUG] blacklist: 3717595507 addresses allowed to be scanned
Aug 11 16:17:03.975 [DEBUG] send: will send from 1 address on 28233 source ports
Aug 11 16:17:03.975 [DEBUG] send: using bandwidth 10000000 bits/s, rate set to 14880pkt/s
Aug 11 16:17:03.985 [DEBUG] recv: thread started
ZMap还支持在扫描以后打印出一个的可grep的汇总信息,相似于下面这样,能够经过调用--summary
来实现。
cnf target-port 443
cnf source-port-range-begin 32768
cnf source-port-range-end 61000
cnf source-addr-range-begin 1.1.1.4
cnf source-addr-range-end 1.1.1.8
cnf maximum-packets 4294967295
cnf maximum-runtime 0
cnf permutation-seed 0
cnf cooldown-period 300
cnf send-interface eth1
cnf rate 45000
env nprocessors 16
exc send-start-time Fri Jan 18 01:47:35 2013
exc send-end-time Sat Jan 19 00:47:07 2013
exc recv-start-time Fri Jan 18 01:47:35 2013
exc recv-end-time Sat Jan 19 00:52:07 2013
exc sent 3722335150
exc blacklisted 572632145
exc first-scanned 1318129262
exc hit-rate 0.874102
exc synack-received-unique 32537000
exc synack-received-total 36689941
exc synack-cooldown-received-unique 193
exc synack-cooldown-received-total 1543
exc rst-received-unique 141901021
exc rst-received-total 166779002
adv source-port-secret 37952
adv permutation-gen 4215763218
ZMap能够经过输出模块生成不一样格式的结果。默认状况下,ZMap只支持csv的输出,可是能够经过编译支持redis和json 。可使用输出过滤来过滤这些发送到输出模块上的结果。输出模块输出的字段由用户指定。默认状况若是没有指定输出文件,ZMap将以csv格式返回结果,而不会生成特定结果。也能够编写本身的输出模块;请参阅编写输出模块。
-o, --output-file=p
输出写入文件地址
-O, --output-module=p
调用自定义输出模块
-f, --output-fields=p
以逗号分隔的输出的字段列表
--output-filter=filter
对给定的探测指定字段输出过滤
--list-output-modules
列出可用输出模块
--list-output-fields
列出给定的探测的可用输出字段
除了IP地址以外,ZMap有不少字段。这些字段能够经过在给定探测模块上运行--list-output-fields
来查看。
$ zmap --probe-module="tcp_synscan" --list-output-fields
saddr string: 应答包中的源IP地址
saddr-raw int: 网络字节格式的源IP地址
daddr string: 应答包中的目的IP地址
daddr-raw int: 网络字节格式的目的IP地址
ipid int: 应答包中的IP识别号
ttl int: 应答包中的ttl(存活时间)值
sport int: TCP 源端口
dport int: TCP 目的端口
seqnum int: TCP 序列号
acknum int: TCP Ack号
window int: TCP 窗口
classification string: 包类型
success int: 是应答包成功
repeat int: 是不是来自主机的重复响应
cooldown int: 是不是在冷却时间内收到的响应
timestamp-str string: 响应抵达时的时间戳使用ISO8601格式
timestamp-ts int: 响应抵达时的时间戳使用UNIX纪元开始的秒数
timestamp-us int: 时间戳的微秒部分(例如 从'timestamp-ts'的几微秒)
能够经过使用--output-fields=fields
或-f
来选择选择输出字段,任意组合的输出字段能够被指定为逗号分隔的列表。例如:
$ zmap -p 80 -f "response,saddr,daddr,sport,seq,ack,in_cooldown,is_repeat,timestamp" -o output.csv
在传到输出模块以前,探测模块生成的结果能够先过滤。过滤是针对探测模块的输出字段的。过滤使用相似于SQL的简单过滤语法写成,经过ZMap的--output-filter选项来指定。输出过滤一般用于过滤掉重复的结果,或仅传输成功的响应到输出模块。
过滤表达式的形式为<字段名> <操做符> <值>
。<值>
的类型必须是一个字符串或一串无符号整数而且匹配<字段名>
类型。对于整数比较有效的操做符是= !=, <, >, <=, >=
。字符串比较的操做是=,!=。--list-output-fields
能够打印那些可供探测模块选择的字段和类型,而后退出。
复合型的过滤操做,能够经过使用&&
(逻辑与)和||
(逻辑或)这样的运算符来组合出特殊的过滤操做。
示例
书写一则过滤仅显示成功的、不重复的应答
--output-filter="success = 1 && repeat = 0"
过滤出RST分类而且TTL大于10的包,或者SYNACK分类的包
--output-filter="(classification = rst && ttl > 10) || classification = synack"
csv模块将会生成以逗号分隔各个要求输出的字段的文件。例如,如下的指令将生成名为output.csv
的CSV文件。
$ zmap -p 80 -f "response,saddr,daddr,sport,seq,ack,in_cooldown,is_repeat,timestamp" -o output.csv
#响应, 源地址, 目的地址, 源端口, 目的端口, 序列号, 应答, 是不是冷却模式, 是否重复, 时间戳
response, saddr, daddr, sport, dport, seq, ack, in_cooldown, is_repeat, timestamp
synack, 159.174.153.144, 10.0.0.9, 80, 40555, 3050964427, 3515084203, 0, 0,2013-08-1518:55:47.681
rst, 141.209.175.1, 10.0.0.9, 80, 40136, 0, 3272553764, 0, 0,2013-08-15 18:55:47.683
rst, 72.36.213.231, 10.0.0.9, 80, 56642, 0, 2037447916, 0, 0,2013-08-15 18:55:47.691
rst, 148.8.49.150, 10.0.0.9, 80, 41672, 0, 1135824975, 0, 0,2013-08-15 18:55:47.692
rst, 50.165.166.206, 10.0.0.9, 80, 38858, 0, 535206863, 0, 0,2013-08-15 18:55:47.694
rst, 65.55.203.135, 10.0.0.9, 80, 50008, 0, 4071709905, 0, 0,2013-08-15 18:55:47.700
synack, 50.57.166.186, 10.0.0.9, 80, 60650, 2813653162, 993314545, 0, 0,2013-08-1518:55:47.704
synack, 152.75.208.114, 10.0.0.9, 80, 52498, 460383682, 4040786862, 0, 0,2013-08-1518:55:47.707
synack, 23.72.138.74, 10.0.0.9, 80, 33480, 810393698, 486476355, 0, 0,2013-08-1518:55:47.710
Redis的输出模块容许地址被添加到一个Redis的队列,而不是保存到文件,容许ZMap将它与以后的处理工具结合使用。
注意! ZMap默认不会编译Redis功能。若是你从源码构建ZMap,能够在CMake的时候加上-DWITH_REDIS=ON
来增长Redis支持。
JSON输出模块用起来相似于CSV模块,只是以JSON格式写入到文件。JSON文件能轻松地导入到其它能够读取JSON的程序中。
注意!,ZMap默认不会编译JSON功能。若是你从源码构建ZMap,能够在CMake的时候加上-DWITH_JSON=ON
来增长JSON支持。
ZMap同时支持对网络前缀作黑名单和白名单。若是ZMap不加黑名单和白名单参数,他将会扫描全部的IPv4地址(包括本地的,保留的以及组播地 址)。若是指定了黑名单文件,那么在黑名单中的网络前缀将再也不扫描;若是指定了白名单文件,只有那些网络前缀在白名单内的才会扫描。白名单和黑名单文件可 以协同使用;黑名单优先于白名单(例如:若是您在白名单中指定了10.0.0.0/8并在黑名单中指定了10.1.0.0/16,那么10.1.0.0 /16将不会扫描)。白名单和黑名单文件能够在命令行中指定,以下所示:
-b, --blacklist-file=path
文件用于记录黑名单子网,以CIDR(无类域间路由)的表示法,例如192.168.0.0/16
-w, --whitelist-file=path
文件用于记录限制扫描的子网,以CIDR的表示法,例如192.168.0.0/16
黑名单文件的每行都须要以CIDR的表示格式书写,一行单一的网络前缀。容许使用#
加以备注。例如:
# IANA(英特网编号管理局)记录的用于特殊目的的IPv4地址
# http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
# 更新于2013-05-22
0.0.0.0/8 # RFC1122: 网络中的全部主机
10.0.0.0/8 # RFC1918: 私有地址
100.64.0.0/10 # RFC6598: 共享地址空间
127.0.0.0/8 # RFC1122: 回环地址
169.254.0.0/16 # RFC3927: 本地链路地址
172.16.0.0/12 # RFC1918: 私有地址
192.0.0.0/24 # RFC6890: IETF协议预留
192.0.2.0/24 # RFC5737: 测试地址1
192.88.99.0/24 # RFC3068: IPv6转换到IPv4的任播
192.168.0.0/16 # RFC1918: 私有地址
192.18.0.0/15 # RFC2544: 检测地址
198.51.100.0/24 # RFC5737: 测试地址2
203.0.113.0/24 # RFC5737: 测试地址3
240.0.0.0/4 # RFC1112: 预留地址
255.255.255.255/32 # RFC0919: 限制广播地址
# IANA记录的用于组播的地址空间
# http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
# 更新于2013-06-25
224.0.0.0/4 # RFC5771: 组播/预留地址ed
若是您只是想扫描因特网中随机的一部分地址,使用抽样检出,来代替使用白名单和黑名单。
注意!ZMap默认设置使用/etc/zmap/blacklist.conf
做为黑名单文件,其中包含有本地的地址空间和预留的IP空间。经过编辑/etc/zmap/zmap.conf
能够改变默认的配置。
默认状况下,ZMap将以您当前网卡所能支持的最快速度扫描。以咱们对于经常使用硬件的经验,这一般是理论上Gbit以太网速度的95-98%,这可能 比您的上游提供商可处理的速度还要快。ZMap是不会自动的根据您的上游提供商来调整发送速率的。您可能须要手动的调整发送速率来减小丢包和错误结果。
-r, --rate=pps
设置最大发送速率以包/秒为单位
-B, --bandwidth=bps
设置发送速率以比特/秒(支持G,M和K后缀)。这会覆盖--rate参数。
ZMap一样支持对IPv4地址空间进行指定最大目标数和/或最长运行时间的随机采样。因为每次对主机的扫描是经过随机排序生成的,限制扫描的主机个数为N就会随机抽选N个主机。命令选项以下:
-n, --max-targets=n
探测目标上限数量
-N, --max-results=n
结果上限数量(累积收到这么多结果后退出)
-t, --max-runtime=s
发送数据包时间长度上限(以秒为单位)
-s, --seed=n
种子用以选择地址的排列方式。使用不一样ZMap执行扫描操做时将种子设成相同的值能够保证相同的扫描顺序。
举个例子,若是您想要屡次扫描一样的一百万个互联网主机,您能够设定排序种子和扫描主机的上限数量,大体以下所示:
zmap -p 443 -s 3 -n 1000000 -o results
为了肯定哪一百万主机将要被扫描,您能够执行预扫,只打印数据包而非发送,并不是真的实施扫描。
zmap -p 443 -s 3 -n 1000000 --dryrun | grep daddr
| awk -F'daddr: ' '{print $2}' | sed 's/ |.*//;'
ZMap支持向每一个主机发送多个探测。增长这个数量既增长了扫描时间又增长了到达的主机数量。然而,咱们发现,增长的扫描时间(每一个额外扫描的增长近100%)远远大于到达的主机数量(每一个额外扫描的增长近1%)。
-P, --probes=n
向每一个IP发出的独立探测个数(默认值=1)
ZMap专为向大量主机发起链接并寻找那些正确响应而设计。然而,咱们意识到许多用户须要执行一些后续处理,如执行应用程序级别的握手。例如,用户在80端口实施TCP SYN扫描也许想要实施一个简单的GET请求,还有用户扫描443端口可能但愿完成TLS握手。
咱们收录了一个示例程序,banner-grab,伴随ZMap使用可让用户从监听状态的TCP服务器上接收到消息。Banner-grab链接 到提供的服务器上,发送一个可选的消息,而后打印出收到的第一个消息。这个工具能够用来获取banner,例如HTTP服务的回复的具体指 令,telnet登录提示,或SSH服务的字符串。
下面的例子寻找了1000个监听80端口的服务器,并向每一个发送一个简单的GET请求,存储他们的64位编码响应至http-banners.out
$ zmap -p 80 -N 1000 -B 10M -o - | ./banner-grab-tcp -p 80 -c 500 -d ./http-req > out
若是想知道更多使用banner-grab
的细节,能够参考examples/banner-grab
中的README文件。
注意! ZMap和banner-grab(如例子中)同时运行可能会比较显著的影响对方的表现和精度。确保不让ZMap占满banner-grab-tcp的并 发链接,否则banner-grab将会落后于标准输入的读入,致使阻塞ZMap的输出写入。咱们推荐使用较慢扫描速率的ZMap,同时提高 banner-grab-tcp的并发性至3000之内(注意 并发链接>1000须要您使用ulimit -SHn 100000
和ulimit -HHn 100000
来增长每一个进程的最大文件描述符数量)。固然,这些参数取决于您服务器的性能、链接成功率(hit-rate);咱们鼓励开发者在运行大型扫描以前先进行小样本的试验。
咱们也收录了另外一种形式的banner-grab,就是forge-socket, 重复利用服务器发出的SYN-ACK,链接并最终取得banner。在banner-grab-tcp
中,ZMap向每一个服务器发送一个SYN,并监听服务器发回的带有SYN+ACK的应答。运行ZMap主机的内核接受应答后发送RST,这样就没有与该包关联活动链接。程序banner-grab必须在这以后建立一个新的TCP链接到从服务器获取数据。
在forge-socket中,咱们利用内核中同名的模块,使咱们能够建立任意参数的TCP链接。能够经过抑制内核的RST包,并重用SYN+ACK的参数取代该包而建立套接字,经过这个套接字收发数据和咱们平时使用的链接套接字并无什么不一样。
要使用forge-socket,您须要forge-socket内核模块,从github上能够得到。您须要git clone git@github.com:ewust/forge_socket.git
至ZMap源码根目录,而后cd进入forge_socket目录,运行make。以root身份运行insmod forge_socket.ko
来安装该内核模块。
您也须要告知内核不要发送RST包。一个简单的在全系统禁用RST包的方法是使用iptables。以root身份运行iptables -A OUTPUT -p tcp -m tcp --tcp-flgas RST,RST RST,RST -j DROP
便可,固然您也能够加上一项--dport X
将禁用局限于所扫描的端口(X)上。扫描完成后移除这项设置,以root身份运行iptables -D OUTPUT -p tcp -m tcp --tcp-flags RST,RST RST,RST -j DROP
便可。
如今应该能够创建forge-socket的ZMap示例程序了。运行须要使用extended_fileZMap输出模块:
$ zmap -p 80 -N 1000 -B 10M -O extended_file -o - | \
./forge-socket -c 500 -d ./http-req > ./http-banners.out
详细内容能够参考examples/forge-socket
目录下的README。
ZMap能够经过探测模块来扩展支持不一样类型的扫描,经过输出模块增长不一样类型的输出结果。注册过的探测和输出模块能够在命令行中列出:
--list-probe-modules
列出安装过的探测模块
--list-output-modules
列出安装过的输出模块
ZMap的输出和输出后处理能够经过实现和注册扫描器的输出模块来扩展。输出模块在接收每个应答包时都会收到一个回调。然而默认提供的模块仅提供简单的输出,这些模块一样支持更多的输出后处理(例如:重复跟踪或输出AS号码来代替IP地址)。
经过定义一个新的output_module结构来建立输出模块,并在output_modules.c中注册:
typedef struct output_module {
const char *name; // 在命令行如何引用输出模块
unsigned update_interval; // 以秒为单位的更新间隔
output_init_cb init; // 在扫描器初始化的时候调用
output_update_cb start; // 在扫描器开始的时候调用
output_update_cb update; // 每次更新间隔调用,秒为单位
output_update_cb close; // 扫描终止后调用
output_packet_cb process_ip; // 接收到应答时调用
const char *helptext; // 会在--list-output-modules时打印在屏幕上
} output_module_t;
输出模块必须有名称,经过名称能够在命令行调用,而且一般会实现success_ip
和常见的other_ip
回调。process_ip的回调由每一个收到并经由probe module过滤的应答包调用。应答是否被认定为成功并不肯定(好比,它能够是一个TCP的RST)。这些回调必须定义匹配output_packet_cb
定义的函数:
int (*output_packet_cb) (
ipaddr_n_t saddr, // 网络字节格式的发起扫描主机IP地址
ipaddr_n_t daddr, // 网络字节格式的目的IP地址
const char* response_type, // 发送模块的数据包分类
int is_repeat, // {0: 主机的第一个应答, 1: 后续的应答}
int in_cooldown, // {0: 非冷却状态, 1: 扫描器处于冷却中}
const u_char* packet, // 指向IP包的iphdr结构体的指针
size_t packet_len // 包的长度,以字节为单位
);
输出模块还能够经过注册回调,执行在扫描初始化的时候(诸如打开输出文件的任务)、在扫描开始阶段(诸如记录黑名单的任务)、在扫描的常规间隔(诸如状态更新的任务)、在关闭的时候(诸如关掉全部打开的文件描述符)。提供的这些回调能够完整的访问扫描配置和当前状态:
int (*output_update_cb)(struct state_conf*, struct state_send*, struct state_recv*);
这些定义在output_modules.h中。在src/outputmodules/modulecsv.c中有可用示例。
数据包由探测模块构造,它能够建立各类包和不一样类型的响应。ZMap默认拥有两个扫描模块:tcp_synscan
和icmp_echoscan
。默认状况下,ZMap使用tcp_synscan
来发送TCP SYN包并对每一个主机的响应分类,如打开时(收到SYN+ACK)或关闭时(收到RST)。ZMap容许开发者编写本身的ZMap探测模块,使用以下的API:
任何类型的扫描都必须经过开发和注册send_module_t
结构中的回调来实现:
typedef struct probe_module {
const char *name; // 如何在命令行调用扫描
size_t packet_length; // 探测包有多长(必须是静态的)
const char *pcap_filter; // 对收到的响应实施PCAP过滤
size_t pcap_snaplen; // libpcap 捕获的最大字节数
uint8_t port_args; // 设为1,若是ZMap须要用户指定--target-port
probe_global_init_cb global_initialize; // 在扫描初始化会时被调用一次
probe_thread_init_cb thread_initialize; // 每一个包缓存区的线程中被调用一次
probe_make_packet_cb make_packet; // 每一个主机更新包的时候被调用一次
probe_validate_packet_cb validate_packet; // 每收到一个包被调用一次,
// 若是包无效返回0,
// 非零则有效。
probe_print_packet_cb print_packet; // 若是在预扫模式下被每一个包都调用
probe_classify_packet_cb process_packet; // 由区分响应的接收器调用
probe_close_cb close; // 扫描终止后被调用
fielddef_t *fields // 该模块指定的字段的定义
int numfields // 字段的数量
} probe_module_t;
在扫描操做初始化时会调用一次global_initialize
,能够用来实施一些必要的全局配置和初始化操做。然而,global_initialize
并不能访问包缓冲区,那里是线程特定的。代替的,thread_initialize
在 每一个发送线程初始化的时候被调用,提供对于缓冲区的访问,能够用来构建探测包和全局的源和目的值。此回调应用于构建主机不可知的包结构,甚至只有特定值 (如:目的主机和校验和),须要随着每一个主机更新。例如,以太网头部信息在交换时不会变动(减去校验和是由NIC硬件计算的)所以能够事先定义以减小扫描 时间开销。
调用回调参数make_packet
是为了让被扫描的主机容许探测模块更 新主机指定的值,同时提供IP地址、一个非透明的验证字符串和探测数目(以下所示)。探测模块负责在探测中放置尽量多的验证字符串,即使当服务器返回的 应答为空时,探测模块也能验证它的当前状态。例如,针对TCP SYN扫描,tcp_synscan探测模块会使用TCP源端口和序列号的格式存储验证字符串。响应包(SYN+ACK)将包含目的端口和确认号的预期 值。
int make_packet(
void *packetbuf, // 包的缓冲区
ipaddr_n_t src_ip, // 网络字节格式源IP
ipaddr_n_t dst_ip, // 网络字节格式目的IP
uint32_t *validation, // 探测中的有效字符串
int probe_num // 若是向每一个主机发送多重探测,
// 该值为咱们对于该主机
// 正在发送的探测数目
);
扫描模块也应该定义pcap_filter
、validate_packet
和process_packet
。只有符合PCAP过滤器的包才会被扫描。举个例子,在一个TCP SYN扫描的状况下,咱们只想要调查TCP SYN / ACK或RST TCP数据包,并利用相似tcp && tcp[13] & 4 != 0 || tcp[13] == 18
的过滤方法。validate_packet
函数将会被每一个知足PCAP过滤条件的包调用。若是验证返回的值非零,将会调用process_packet
函数,并使用fields
定义的字段和包中的数据填充字段集。举个例子,以下代码为TCP synscan探测模块处理了一个数据包。
void synscan_process_packet(const u_char *packet, uint32_t len, fieldset_t *fs)
{
struct iphdr *ip_hdr = (struct iphdr *)&packet[sizeof(struct ethhdr)];
struct tcphdr *tcp = (struct tcphdr*)((char *)ip_hdr
+ (sizeof(struct iphdr)));
fs_add_uint64(fs, "sport", (uint64_t) ntohs(tcp->source));
fs_add_uint64(fs, "dport", (uint64_t) ntohs(tcp->dest));
fs_add_uint64(fs, "seqnum", (uint64_t) ntohl(tcp->seq));
fs_add_uint64(fs, "acknum", (uint64_t) ntohl(tcp->ack_seq));
fs_add_uint64(fs, "window", (uint64_t) ntohs(tcp->window));
if (tcp->rst) { // RST packet
fs_add_string(fs, "classification", (char*) "rst", 0);
fs_add_uint64(fs, "success", 0);
} else { // SYNACK packet
fs_add_string(fs, "classification", (char*) "synack", 0);
fs_add_uint64(fs, "success", 1);
}
}