python scapy 捕获与解析数据包的源码解析

scapy 是一个python编写的功能强大的网络数据包操作库,可以仿造,捕获和解析大量不同协议类型的数据包。

1 scapy 安装与交互使用

可参考 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

2 sniff 捕获数据包

使用 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

3 PacketList 常用接口与源码分析

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.178

conversations():绘制捕获后数据的会话图;

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

  • 7
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值