scapy是python中一个可用于网络嗅探的非常强大的第三方库,可以用它来做 packet 嗅探和伪造 packet。 scapy已经在内部实现了大量的网络协议。如DNS、ARP、IP、TCP、UDP等等,可以用它来编写非常灵活实用的工具。
换言之,Scapy 是一个强大的操纵报文的交互程序。它可以伪造或者解析多种协议的报文,还具有发送、捕获、匹配请求和响应这些报文以及更多的功能。Scapy 可以轻松地做到像扫描(scanning)、路由跟踪(tracerouting)、探测(probing)、单元测试(unit tests)、攻击(attacks)和发现网络(network discorvery)这样的传统任务。它可以代替 hping 、arpspoof 、arp-sk、arping,p0f 甚至是部分的Namp、tcpdump 和 tshark 的功能。
安装命令:pip install scapy
scapy抓包使用 sniff() 函数,这个函数有很多参数
def sniff(count=0, store=1, offline=None, prn=None,filter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, iface=None,*args,**kargs)
count:抓包的数量,0表示无限制;
store:保存抓取的数据包或者丢弃,1保存,0丢弃(如果长时间运行需要将值设置为0,否则会导致内存用尽后宕机)
offline:从 pcap 文件读取数据包,而不进行嗅探,默认为None
prn:为每一个数据包定义一个函数,如果返回了什么,则显示。例如:prn = lambda x: x.summary(); ( packct.summar()函数返回的是对包的统计性信息 )(钩子函数,如果要一直监听某个网卡的消息并立即处理需要用此入参,而不是接收返回值后再处理)
filter:过滤规则,使用wireshark里面的过滤语法
L2socket:使用给定的 L2socket
timeout:在给定的时间后停止嗅探,默认为 None
opened_socket:对指定的对象使用 .recv() 进行读取;
stop_filter:定义一个函数,决定在抓到指定数据包后停止抓包,如:stop_filter = lambda x: x.haslayer(TCP);
iface:指定抓包的网卡名称
将抓取到的数据包保存
from scapy.all import * package=sniff(iface='eth0',count=10)
#扫描eth0网卡的数据包,总数为10个 wrpcap(“test.pcap”,package) #将抓取到的包保存为test.pcap文件 如果我们以后想查看这个包的话,可以这样使用 package = sniff(offline=‘test.pcap’) 或 package= rdpcap(‘test.pcap’)
查看抓取到的数据包
from scapy.all import *
package=sniff(iface='eth0',count=10) #扫描eth0网卡的数据包,总数为10个,不知道网卡的可以为空
print(package)
从上面可以看到,我们抓取到了五个UDP的数据包,五个其他数据包,然后我们可以查看第一个数据包:package[0]是查看第一个数据包的数据,package[0].show()是查看第一个数据包的详细信息,scapy是按照按照 TCP/IP 四层参考模型显示详细包信息的,即:链路层 [Ethernet]、网络层[IP]、传输层[TCP/UDP]、应用层[RAW] 。我们还可以通过协议来查看指定的包:
package[UDP][0].show() ,因为我们这里只有UDP的数据包,所以就没有这样使用。,而我们也可以直接只获取指定层的数据,如: pcap[UDP][1][Ether].dst 这个包里面是等于ff:ff:ff:ff:ff:ff
from scapy.all import *
package=sniff(iface='eth0',count=10) #扫描eth0网卡的数据包,总数为10个
print(package)
print(package[0]) #查看第一个数据包的数据
print(package[0].show()) #查看第一个数据包的详情
######################################################################
package=sniff(iface='',count=10)
print(package)
<Sniffed: TCP:1 UDP:2 ICMP:0 Other:7>
print(package[0])
WARNING: Calling str(pkt) on Python 3 makes no sense!
b'\x01\x80\xc2\x00\x00\nl\xeb\xb6\x16iP\x00.\xaa\xaa\x03\x00\x18\x82 \x04\x01\x00\x01\x00%\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
print(package[0].show())###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = 6c:4b:90:8e:c4:54
type = ARP
###[ ARP ]###
hwtype = 0x1
ptype = IPv4
hwlen = 6
plen = 4
op = who-has
hwsrc = 6c:4b:90:8e:c4:54
psrc = 192.168.7.126
hwdst = 00:00:00:00:00:00
pdst = 169.254.71.186
###[ Padding ]###
load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
格式化输出
如果我们要对抓取到的数据包进行格式化输出,我们可以使用 packet.sprintf() 函数进行格式化输出
sprintf()读数据格式:IP:%IP.src% 代表读取的是IP字段的源地址
比如要读取IP包的源地址和目的地址: IP:%IP.src% -> %IP.dst%
要读取UDP中的源端口和目的端口: UDP:%UDP.sport% -> %UDP.sport%
过滤抓包
如果我们想抓指定类型的数据包,就需要使用 filter 进行过滤,而 filter 使用的是 Berkeley Packet Filter (BPF)语法,也就是我们在 wireshark 中可以使用的过滤语法
比如说我们只抓取 icmp 的包,并且按照 源ip-> 目的ip 的格式打印出来 。我在一直ping百度,下面是抓包的
sniff(filter="icmp",count=5,prn=lambda x : x.sprintf("{IP:%IP.src%-> %IP.dst%}"))
from scapy.all import *
sniff(filter="icmp",count=5,prn=lambda x : x.sprintf("{IP:%IP.src%-> %IP.dst%}"))
123.58.182.249-> 192.168.6.56
192.168.6.56-> 110.242.68.3
110.242.68.3-> 192.168.6.56
192.168.6.56-> 110.242.68.3
110.242.68.3-> 192.168.6.56
<Sniffed: TCP:0 UDP:0 ICMP:5 Other:0>
示例:
from scapy.all import *
def Callback(packet):
print('src:%s----->dst:%s'%(packet[IP].src, packet[IP].dst))
print('TTL:%s'%packet[IP].ttl)
print(packet.show()) #内置的show()函数打印数据包内容
sniff(filter='dst port 80', prn=Callback)
执行结果:python3 scapy_test.py
src:192.168.6.57----->dst:180.163.237.176
TTL:64
###[ Ethernet ]###
dst = c4:b8:b4:64:20:77
src = 6c:4b:90:d4:17:e1
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 41
id = 16868
flags = DF
frag = 0
ttl = 64
proto = tcp
chksum = 0x8fb5
src = 192.168.6.57
dst = 180.163.237.176
\options
###[ TCP ]###
sport = 57790
dport = http
seq = 3384538577
ack = 2221061731
dataofs = 5
reserved = 0
flags = A
window = 1028
chksum = 0x6637
urgptr = 0
options = []
###[ Raw ]###
load = ‘\x00’
###[ Padding ]###
load = ‘\x00\x00\x00\x00\x00’
None
prn的使用
class Demo():
def start(self):
sniff(iface="eth0", prn=self.process, store=0)
def process(self):
pass
这样可以一直监听某个网卡数据并实时处理,store=0保证一直运行不会出现内存泄露