如何利用Python嗅探(Sniffer)数据包


一提到Python获取数据包的方式,相信很多Python爱好者会利用Linux的libpcap软件包或利用Windows下的WinPcap可移植版的方式进行抓取数据包,然后再利用dpkt软件包进行协议分析,我们这里想换一个角度去思考:
    1. Python版本的pcap存储内存数据过小,也就是说缓存不够,在高并发下容易发生丢包现象,其实C版本的也同样存在这样的问题,只不过Python版本的缓存实在是过低,让人很郁闷。
    2. dpkt协议分析并非必须,如果你对RFC 791和RFC 793等协议熟悉的话,完全可以使用struct.unpack的方式进行分析。
    如果你平常习惯使用tcpdump抓取数据包的话,完全可以使用它来代替pcap软件包,只不过我们需要利用tcpdump将抓取的数据以pcap格式进行保存,说道这里大家一定会想到Wireshark工具,具体命令如下:
1. tcpdump dst 10.13.202.116 and tcp dst port 80 -s 0 -i eth1 -w ../pcap/tcpdump.pcap -C 1k -W 5
    我们首先需要对pcap文件格式有所了解,具体信息大家可以参考其他资料文档,我这里只说其重要的结构体组成,如下:
1. sturct pcap_file_header
2. {
3.     DWORD   magic;
4.     WORD    version_major;
5.     WORD    version_minor;
6.     DWORD   thiszone;
7.     DWORD   sigfigs;
8.     DWORD   snaplen;
9.     DWORD   linktype;
10. }
11. 
12. struct pcap_pkthdr
13. {
14.     struct timeval  ts;
15.     DWORD           caplen;
16.     DWORD           len;
17. }
18. 
19. struct timeval
20. {
21.     DWORD   GMTtime;
22.     DWORD   microTime;
23. }
    这里需要说明的一点是,因为在Python的世界里一切都是对象,所以往往Python在处理数据包的时候感觉让人比较麻烦。
    至于对于如何监控tcpdump生成的pcap文件数据,大家可以通过pyinotify软件包来实现(相信大家一定对于rsync+inotify的组合比较熟悉),如下:
1. class Packer(pyinotify.ProcessEvent):
2.     def __init__(self, product):
3.         self.product = product
4.         self.process = None
5. 
6.     def process_IN_CREATE(self, event):
7.         logger.debug("create file: %s in queue" % self.process_IF_START_THREAD(event))
8. 
9.     def process_IN_MODIFY(self, event):
10.         self.process_IF_START_THREAD(event)
11.         logger.debug("modify file: %s in queue" % self.process_IF_START_THREAD(event))
12. 
13.     def process_IN_DELETE(self, event):
14.         filename = os.path.join(event.path, event.name)
15.         logger.debug("delete file: %s" % filename)
16. 
17.     def process_IF_START_THREAD(self, event):
18.         filename = os.path.join(event.path, event.name)
19. 
20.         if filename != self.process:
21.             self.process = filename
22.             self.product.put(filename)
23. 
24.             if self.product.qsize() > 1:
25.                 try:
26.                     logger.debug("create consumer product.qsize: %s" % self.product.qsize())
27.                     consumer = Consumer(self.product)
28.                     consumer.start()
29.                 except Exception, errmsg:
30.                     logger.error("create consumer failed: %s" % errmsg)
31. 
32.         return filename
33. 
34. class Factory(object):
35.     def __init__(self, product):
36.         self.product = product
37.         self.manager = pyinotify.WatchManager()
38.         self.mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY
39. 
40.     def work(self):
41.         try:
42.             try:
43.                 notifier = pyinotify.ThreadedNotifier(self.manager, Packer(self.product))
44.                 notifier.start()
45.                 self.manager.add_watch("../pcap", self.mask, rec = True)
46.                 notifier.join()
47.             except Exception, errmsg:
48.                 logger.error("create notifier failed: %s" % errmsg)
49.         except KeyboardInterrupt, errmsg:
50.             logger.error("factory has been terminated: %s" % errmsg)
    在获得要分析的pcap文件数据之后,就要对其分析了,只要你足够了解pcap文件格式就可以了,对于我们来讲只需要获得TCP数据段的数据即可,如下:
1. class Writer(threading.Thread):
2.     def __init__(self, product, stack):
3.         threading.Thread.__init__(self)
4.         self.product = product
5.         self.stack = stack
6.  
7.         self.pcap_pkthdr = {} 
8. 
9.     def run(self):
10.         while True:
11.             filename = self.product.get()
12.             try:
13.                 f = open(filename, "rb")
14.                 readlines = f.read()
15.                 f.close()
16.                 offset = 24
17.                 while len(readlines) > offset:
18.                     self.pcap_pkthdr["len"] = readlines[offset+12:offset+16]
19.                     try:
20.                         length = struct.unpack("I", self.pcap_pkthdr["len"])[0]
21.                         self.stack.put(readlines[offset+16:offset+16+length])
22.                         offset += length + 16
23.                     except Exception, errmsg:
24.                         logger.error("unpack pcap_pkthdr failed: %s" % errmsg)
25.             except IOError, errmsg:
26.                 logger.error("open file failed: %s" % errmsg)
    在获得TCP数据段的数据包之后,问题就简单多了,根据大家的具体需求就可以进行相应的分析了,我这里是想分析其HTTP协议数据,同样也借助了dpkt软件包进行分析,如下:
1. def worker(memcache, packet, local_address, remote_address):
2.     try:
3.         p = dpkt.ethernet.Ethernet(packet)
4.         if p.data.__class__.__name__ == "IP":
5.             srcip = "%d.%d.%d.%d" % tuple(map(ord, list(p.data.src)))
6.             dstip = "%d.%d.%d.%d" % tuple(map(ord, list(p.data.dst)))
7.             if p.data.data.__class__.__name__ == "TCP":
8.                 tcpacket = p.data.data
9.                 if tcpacket.dport == 80 and dstip == local_address:
10.                     srcport = tcpacket.sport
11.                     key = srcip + ":" + str(srcport)
12.                     if tcpacket.data:
13.                         if not memcache.has_key(key):
14.                             memcache[key] = {}
15. 
16.                         if not memcache[key].has_key("response"):
17.                             memcache[key]["response"] = None
18. 
19.                         if memcache[key].has_key("data"):
20.                             memcache[key]["data"] += tcpacket.data
21.                         else:
22.                             memcache[key]["data"] = tcpacket.data
23.                     else:
24.                         if memcache.has_key(key):
25.                             memcache[key]["response"] = dpkt.http.Request(memcache[key]["data"])
26.                             try:
27.                                 stackless.tasklet(connection)(memcache[key]["response"], local_address, remote_address)
28.                                 stackless.run()
29.                             except Exception, errmsg:
30.                                 logger.error("connect remote remote_address failed: %s", errmsg)
31.                             logger.debug("old headers(none content-length): %s", memcache[key]["response"])
32.                             memcache.pop(key)
33.     except Exception, errmsg:
34.         logger.error("dpkt.ethernet.Ethernet failed in worker: %s", errmsg)
    如果大家只是想单纯的获取IP地址、端口、流量信息,那么问题就更简单了,这里只是抛砖引玉。

 

摘自  放飞翅膀,追求梦想 

相关内容

    暂无相关文章

评论关闭