神奇的发包工具scapy
之前就写过好几次 scapy 。 每次需要自己构造报文的时候都会发现: 它好强大 。
调试VLAN
测试协议vlan的时候,需要构造一些二层报文。刚开始同事推荐了一下ipop。试用了一下老是发不出报来。于是在网上搜到了 scapy 。
原来在测试igmp的时候,我就用过这货。当时还记录在了 2018-03-26-Linux下的工具 。
当时完成写在python文件中的,这次我查了一下文档 https://scapy.readthedocs.io 。可以有好几种方式来使用它。
直接运行scapy,然后一行一行地运行其实是很方便的,比如文档的中的例子:
>>> a=IP(ttl=10)
>>> a
< IP ttl=10 |>
>>> a.src
’127.0.0.1’
>>> a.dst="192.168.1.1"
>>> a
< IP ttl=10 dst=192.168.1.1 |>
>>> a.src
’192.168.8.14’
>>> del(a.ttl)
>>> a
< IP dst=192.168.1.1 |>
>>> a.ttl
64
发送报文也很简单:
>>> send(IP(dst="1.2.3.4")/ICMP()) . Sent 1 packets. >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1") .... Sent 4 packets. >>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2) ................^C Sent 16 packets. >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets. Returns packets sent by send() >>> send(IP(dst='127.0.0.1'), return_packets=True) . Sent 1 packets. <PacketList: TCP:0 UDP:0 ICMP:0 Other:1>
我写了一个简单地python的例子,这个例子中,主要是修改了ether_type字段,也修改了每个报文的mac地址,这是为了方便我调试协议VLAN:
#!/usr/bin/python from scapy.all import * from scapy.contrib.igmp import IGMP import random sendp(Ether(src="f8:09:23:00:00:01", dst="f8:07:24:00:00:01", type=0x0801)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:02", dst="f8:07:24:00:00:01", type=0x0802)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:03", dst="f8:07:24:00:00:01", type=0x0803)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:04", dst="f8:07:24:00:00:01", type=0x0804)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:05", dst="f8:07:24:00:00:01", type=0x0805)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:06", dst="f8:07:24:00:00:01", type=0x0806)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:07", dst="f8:07:24:00:00:01", type=0x0807)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:08", dst="f8:07:24:00:00:01", type=0x0808)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:09", dst="f8:07:24:00:00:01", type=0x0809)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0a", dst="f8:07:24:00:00:01", type=0x080a)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0b", dst="f8:07:24:00:00:01", type=0x080b)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0c", dst="f8:07:24:00:00:01", type=0x080c)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0d", dst="f8:07:24:00:00:01", type=0x080d)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0e", dst="f8:07:24:00:00:01", type=0x080e)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:0f", dst="f8:07:24:00:00:01", type=0x080f)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2) sendp(Ether(src="f8:09:23:00:00:10", dst="f8:07:24:00:00:01", type=0x0810)/IP(dst="192.168.30.2", ttl=(1, 1)), iface="eth0", inter=0.2)后面可以在这上面再做定制。
调试IGMP
发送report报文:
#!/usr/bin/python from scapy.all import * from scapy.contrib.igmp import IGMP import random src_ip = "192.168.1.1" multicast_ip = "239.255.12.45" interface = "eth0" def send_igmp_report(dst): a=Ether(src="94:c6:91:02:56:d6") b=IP(src=src_ip) c=IGMP(type=0x16, gaddr=dst) c.igmpize(b, a) print "Joining IP " + c.gaddr + " MAC " + a.dst sendp(a/b/c, iface=interface) send_igmp_report(multicast_ip)
发送leave报文:
#!/usr/bin/python from scapy.all import * from scapy.contrib.igmp import IGMP import random src_ip = "192.168.1.1" src_mac = "94:c6:91:02:56:d6" interface = "eth0" multicast_ip = "224.239.12.45" def send_igmp_leave(dst): a=Ether(src=src_mac) b=IP(src=src_ip) c=IGMP(type=0x17, gaddr=dst) c.igmpize(b, a) print "Joining IP " + c.gaddr + " MAC " + a.dst sendp(a/b/c, iface=interface) send_igmp_leave(multicast_ip);
发送普通查询:
#!/usr/bin/python from scapy.all import * from scapy.contrib.igmp import IGMP import random src_ip = "192.168.1.1" interface = "eth0" multicast_ip = "224.0.0.1" def send_igmp_ordinary_query(): a=Ether(src="94:c6:91:02:56:d6") b=IP(src=src_ip, dst=multicast_ip) c=IGMP(type=0x11) c.igmpize(b, a) sendp(a/b/c, iface=interface) send_igmp_ordinary_query()
发送特定组查询:
#!/usr/bin/python from scapy.all import * from scapy.contrib.igmp import IGMP import random src_ip = "192.168.1.1" interface = "eth0" multicast_ip = "239.255.12.45" def send_igmp_specified_query(dst): a=Ether(src="94:c6:91:02:56:d6") b=IP(src=src_ip) c=IGMP(type=0x11, gaddr=dst) c.igmpize(b, a) sendp(a/b/c, iface=interface) send_igmp_specified_query(multicast_ip)
自己定制协议
如果遇到一些“非常规”的协议,比如这次我需要试一下GVRP协议。可以直接自己定义对应的类。比如:
from scapy.all import * import random class Ether8023(Packet): name = "8023Ether packet" fields_desc = [ DestMACField("dst"), SourceMACField("src"), ShortField("len", 12) ] class GVRP(Packet): name = "gvrp packete" fields_desc=[ ShortField("protocol_id", 1), XByteField("type", 1), XByteField("len", 4), XByteField("event", 1), ShortField("value", 100), XByteField("endattr", 0), XByteField("endmessage", 0) ] # Join Empty sendp(Ether(src="f8:09:23:00:00:01", dst="01:80:c2:00:00:21")/LLC(dsap=0x42, ssap=0x42, ctrl=0x03)/GVRP(event=1), iface="eth0") # Join In # sendp(Ether8023(src="f8:09:23:00:00:01", dst="01:80:c2:00:00:21")/LLC(dsap=0x42, ssap=0x42, ctrl=0x03)/GVRP(event=2), iface="eth0") # sendp(Ether8023(src="f8:09:23:00:00:01", dst="01:80:c2:00:00:21")/LLC(dsap=0x42, ssap=0x42, ctrl=0x03)/GVRP(event=2), iface="eth0")
Ether8023这个类我是按照Ether类直接写的。GVRP里面,主要就是填fields_desc。 ShortField("protocol_id", 1)表式两个字节的数据,使用protocol_id标识,默认值为1。具体的文档在 这里。