scapy 是一个python编写的功能强大的网络数据包操作库,可以仿造,捕获和解析大量不同协议类型的数据包。
可参考 http://scapy.readthedocs.io/en/latest/usage.html
注意事项:
安装 graphviz 和 ImageMagick >>> yum install graphviz >>> pip install graphviz >>> yum install ImageMagick p = rdpcap('test.cap') # 手册中使用了 readpcap 接口没有定义; p.conversations(type='jpg', target="> test.jpg")详细请参考 http://blog.csdn.net/chirebingxue/article/details/50393755
使用 scapy,可以很容易的捕获特定数据包,达到 tcpdump 和 tshark 在数据捕获方面的效果。可以按照端口,捕获一个或多个端口的数据包。如果不给定端口,默认会捕获所有端口的数据。
sniff(count=0, store=1, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, iface=None, *arg, **karg) 返回PacketList类型的数据包对象; count: 要捕获数据包的总数. 0 表示无限制; store: 是否要保存捕获的数据包; prn: 回调函数,会作用于每个数据包 ex: prn = lambda x: x.summary() lfilter: 过滤函数,不满足条件的数据包会被丢弃; ex: lfilter = lambda x: x.haslayer(Padding) offline: 从pcap文件中读取数据包; timeout: 捕获指定时间内的数据包; L2socket: 通过给定的 L2socket 进行数据捕获; opened_socket: 通过给定的 socket 进行数据捕获; stop_filter: 过滤函数,满足条件后将结束数据捕获; ex: stop_filter = lambda x: x.haslayer(TCP) iface: 指定端口或端口数组注意: lfiter 是回调函数,filter 是BPF 字符串,不要混淆!
sniff 接口源码: /python/site-packages/scapy/sendrecv.py 可阅读源码理解sniff的工作逻辑。
sniff 使用示例:
>>> sniff(filter="icmp and host 66.35.250.151",count=2) >>> sniff(iface="wifi0",prn=lambdax:x.summary()) >>> sniff(iface="eth1",prn=lambdax:x.show()) >>> sniff(iface=["eth1","eth2"],prn=lambdax:x.sniffed_on+": "+x.summary()) >>> sniff(filter="icmp and host 66.35.250.151", count=2) >>> pkts = sniff(offline="temp.cap")使用自定义回调函数;
#! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0)filter 的规则使用 Berkeley Packet Filter(BPF)语法! 过滤规则有三种类型的限定词,分别为 type,dir,和proto 1 type: 可以是host,net,port
host foo net 128.3 port 20用以限定——主机,网络,端口
2 dir 方向限定词:src,dst
’src foo’, ’dst net 128.3’, ’src or dst port ftp-data’限定数据流的方向;src 192.168.10.11,表示所有从主机192.168.10.11发出的数据包。
3 proto 协议限定词:ether,fddi,ip,arp,rarp,decnet,tcp,udp等等
’ether src foo’, ’arp net 128.3’, ’tcp port 21’4 逻辑连接符: and(&&), or(|), not(!)
详细请参考 http://www.cnblogs.com/JohnABC/p/5914543.html
PacketList类源码: /python/site-packages/scapy/plist.py
>>> pkts = sniff(filter='ip src host 200.200.200.44', count=10) >>> pkts.summary() Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "15.9.0.20.in-addr.arpa." Ether / IP / UDP / DNS Qry "www.baidu.com." Ether / IP / ICMP 200.200.200.44 > 61.135.169.125 echo-request 0 / Raw Ether / IP / UDP / DNS Qry "125.169.135.61.in-addr.arpa." Ether / IP / ICMP 200.200.200.44 > 61.135.169.125 echo-request 0 / Raw如上所示,pkts是一个PacketList对象,pkts.res 是一个由packet组成的list。在对数据包组进行遍历时,会用到。
summary 接口:
91 def summary(self, prn=None, lfilter=None): 92 """prints a summary of each packet 93 prn: function to apply to each packet instead of lambda x:x.summary() 94 lfilter: truth function to apply to each packet to decide whether it will be displayed""" 95 for r in self.res: 96 if lfilter is not None: 97 if not lfilter(r): 98 continue 99 if prn is None: 100 print self._elt2sum(r) 101 else: 102 print prn(r)默认会调用私有方法 _elt2sum() 打印出包信息,也可以自定义回调处理函数,summary会打印自定义函数的返回结果,也可以添加过滤函数。
filter() : 根据返回过滤后的数据包 list。 make_table(): 将数据包信息按照定义的格式打印为数据表;
>>> ans.make_table( lambda (s,r): (s.dst, s.ttl, r.src) ) 216.15.189.192 216.15.189.193 216.15.189.194 216.15.189.195 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 81.57.239.254 81.57.239.254 81.57.239.254 81.57.239.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 213.228.3.3 213.228.3.3 213.228.3.3 213.228.3.3 5 193.251.254.1 193.251.251.69 193.251.254.1 193.251.251.69 6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178conversations():绘制捕获后数据的会话图;
284 def conversations(self, getsrcdst=None,**kargs): 285 """Graphes a conversations between sources and destinations and display it 286 (using graphviz and imagemagick) 287 getsrcdst: a function that takes an element of the list and 288 returns the source, the destination and optionally 289 a label. By default, returns the IP source and 290 destination from IP and ARP layers 291 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option 292 target: filename or redirect. Defaults pipe to Imagemagick's display program 293 prog: which graphviz program to use""" 294 if getsrcdst is None: 295 def getsrcdst(pkt): 296 if 'IP' in pkt: 297 return (pkt['IP'].src, pkt['IP'].dst) 298 if 'ARP' in pkt: 299 return (pkt['ARP'].psrc, pkt['ARP'].pdst) 300 raise TypeError() 301 conv = {} 302 for p in self.res: 303 p = self._elt2pkt(p) 304 try: 305 c = getsrcdst(p) 306 except: 307 # No warning here: it's OK that getsrcdst() raises an 308 # exception, since it might be, for example, a 309 # function that expects a specific layer in each 310 # packet. The try/except approach is faster and 311 # considered more Pythonic than adding tests. 312 continue 313 if len(c) == 3: 314 conv.setdefault(c[:2], set()).add(c[2]) 315 else: 316 conv[c] = conv.get(c, 0) + 1 317 gr = 'digraph "conv" {\n' 318 for (s, d), l in conv.iteritems(): 319 gr += '\t "%s" -> "%s" [label="%s"]\n' % ( 320 s, d, ', '.join(str(x) for x in l) if isinstance(l, set) else l 321 ) 322 gr += "}\n" 323 return do_graph(gr, **kargs)getsrcdst 是提取会话的源点与目的点的函数,如上所示默认情况是使用以下函数:
294 if getsrcdst is None: 295 def getsrcdst(pkt): 296 if 'IP' in pkt: 297 return (pkt['IP'].src, pkt['IP'].dst) 298 if 'ARP' in pkt: 299 return (pkt['ARP'].psrc, pkt['ARP'].pdst) 300 raise TypeError()以 IP 源地址与目的地址为结果!graphviz的详细介绍请参考: http://blog.csdn.net/chirebingxue/article/details/50393755