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

来源:互联网 发布:易到用车周航 知乎 编辑:程序博客网 时间:2024/05/11 23:07

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 pythonfrom 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 foonet 128.3port 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 / RawEther / 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.1951 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.12 81.57.239.254 81.57.239.254 81.57.239.254 81.57.239.2543 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.2544 213.228.3.3 213.228.3.3 213.228.3.3 213.228.3.35 193.251.254.1 193.251.251.69 193.251.254.1 193.251.251.696 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 it286         (using graphviz and imagemagick)287         getsrcdst: a function that takes an element of the list and288                    returns the source, the destination and optionally289                    a label. By default, returns the IP source and290                    destination from IP and ARP layers291         type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option292         target: filename or redirect. Defaults pipe to Imagemagick's display program293         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 an308                 # exception, since it might be, for example, a309                 # function that expects a specific layer in each310                 # packet. The try/except approach is faster and311                 # considered more Pythonic than adding tests.312                 continue313             if len(c) == 3:314                 conv.setdefault(c[:2], set()).add(c[2])315             else:316                 conv[c] = conv.get(c, 0) + 1317         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 l321             )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

原创粉丝点击