1. 程式人生 > >如何利用Python嗅探(Sniffer)資料包

如何利用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地址、埠、流量資訊,那麼問題就更簡單了,這裡只是拋磚引玉。

摘自  放飛翅膀,追求夢想