python怎样读取pcap文件

Python028

python怎样读取pcap文件,第1张

程序如下:

#!/usr/bin/env python

#coding=utf-8

#读取pcap文件,解析相应的信息,为了在记事本中显示的方便,把二进制的信息

import struct

fpcap = open('test.pcap','rb')

ftxt = open('result.txt','w')

string_data = fpcap.read()

#pcap文件包头解析

pcap_header = {}

pcap_header['magic_number'] = string_data[0:4]

pcap_header['version_major'] = string_data[4:6]

pcap_header['version_minor'] = string_data[6:8]

pcap_header['thiszone'] = string_data[8:12]

pcap_header['sigfigs'] = string_data[12:16]

pcap_header['snaplen'] = string_data[16:20]

pcap_header['linktype'] = string_data[20:24]

#把pacp文件头信息写入result.txt

ftxt.write("Pcap文件的包头内容如下: \n")

for key in ['magic_number','version_major','version_minor','thiszone',

'sigfigs','snaplen','linktype']:

ftxt.write(key+ " : " + repr(pcap_header[key])+'\n')

#pcap文件的数据包解析

step = 0

packet_num = 0

packet_data = []

pcap_packet_header = {}

i =24

while(i<len(string_data)):

#数据包头各个字段

pcap_packet_header['GMTtime'] = string_data[i:i+4]

pcap_packet_header['MicroTime'] = string_data[i+4:i+8]

pcap_packet_header['caplen'] = string_data[i+8:i+12]

pcap_packet_header['len'] = string_data[i+12:i+16]

#求出此包的包长len

packet_len = struct.unpack('I',pcap_packet_header['len'])[0]

#写入此包数据

packet_data.append(string_data[i+16:i+16+packet_len])

i = i+ packet_len+16

packet_num+=1

#把pacp文件里的数据包信息写入result.txt

for i in range(packet_num):

#先写每一包的包头

ftxt.write("这是第"+str(i)+"包数据的包头和数据:"+'\n')

for key in ['GMTtime','MicroTime','caplen','len']:

ftxt.write(key+' : '+repr(pcap_packet_header[key])+'\n')

#再写数据部分

ftxt.write('此包的数据内容'+repr(packet_data[i])+'\n')

ftxt.write('一共有'+str(packet_num)+"包数据"+'\n')

ftxt.close()

fpcap.close()

最终得到的result文件如下所示:字段显示的都是十六进制,其中的数据部分和wireshark打开,显示的十六进制窗口一样。

一提到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工具,具体命令如下:

tcpdump dst 10.13.202.116 and tcp dst port 80 -s 0 -i eth1 -w ../pcap/tcpdump.pcap -C 1k -W 5

我们首先需要对pcap文件格式有所了解,具体信息大家可以参考其他资料文档,我这里只说其重要的结构体组成,如下:

sturct pcap_file_header

{

DWORD magic

WORDversion_major

WORDversion_minor

DWORD thiszone

DWORD sigfigs

DWORD snaplen

DWORD linktype

}

struct pcap_pkthdr

{

struct timeval ts

DWORD caplen

DWORD len

}

struct timeval

{

DWORD GMTtime

DWORD microTime

}

这里需要说明的一点是,因为在Python的世界里一切都是对象,所以往往Python在处理数据包的时候感觉让人比较麻烦。Python提供了几个libpcapbind,http://monkey.org/~dugsong/pypcap/这里有 一个最简单的。在windows平台上,你需要先安装winpcap,如果你已经安装了Ethereal非常好用。一个规范的抓包过程:

import pcap

import dpkt

pc=pcap.pcap()#注,参数可为网卡名,如eth0

pc.setfilter('tcp port 80')#设置监听过滤器

for ptime,pdata in pc:#ptime为收到时间,pdata为收到数据

print ptime,pdata#...

对抓到的以太网V2数据包(raw packet)进行解包:

p=dpkt.ethernet.Ethernet(pdata)

if p.data.__class__.__name__=='IP':

ip='%d.%d.%d.%d'%tuple(map(ord,list(p.data.dst)))

if p.data.data.__class__.__name__=='TCP':

if data.dport==80:

print p.data.data.data

一些显示参数nrecv,ndrop,nifdrop=pc.stats()返回的元组中,第一个参数为接收到的数据包,第二个参数为被核心丢弃的数据包。

至于对于如何监控tcpdump生成的pcap文件数据,大家可以通过pyinotify软件包来实现,如下:

class Packer(pyinotify.ProcessEvent):

def __init__(self, product):

self.product = product

self.process = None

def process_IN_CREATE(self, event):

logger.debug("create file: %s in queue" % self.process_IF_START_THREAD(event))

def process_IN_MODIFY(self, event):

self.process_IF_START_THREAD(event)

logger.debug("modify file: %s in queue" % self.process_IF_START_THREAD(event))

def process_IN_DELETE(self, event):

filename = os.path.join(event.path, event.name)

logger.debug("delete file: %s" % filename)

def process_IF_START_THREAD(self, event):

filename = os.path.join(event.path, event.name)

if filename != self.process:

self.process = filename

self.product.put(filename)

if self.product.qsize() >1:

try:

logger.debug("create consumer product.qsize: %s" % self.product.qsize())

consumer = Consumer(self.product)

consumer.start()

except Exception, errmsg:

logger.error("create consumer failed: %s" % errmsg)

return filename

class Factory(object):

def __init__(self, product):

self.product = product

self.manager = pyinotify.WatchManager()

self.mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY

def work(self):

try:

try:

notifier = pyinotify.ThreadedNotifier(self.manager, Packer(self.product))

notifier.start()

self.manager.add_watch("../pcap", self.mask, rec = True)

notifier.join()

except Exception, errmsg:

logger.error("create notifier failed: %s" % errmsg)

except KeyboardInterrupt, errmsg:

logger.error("factory has been terminated: %s" % errmsg)

在获得要分析的pcap文件数据之后,就要对其分析了,只要你足够了解pcap文件格式就可以了,对于我们来讲只需要获得TCP数据段的数据即可,如下:

class Writer(threading.Thread):

def __init__(self, product, stack):

threading.Thread.__init__(self)

self.product = product

self.stack = stack

self.pcap_pkthdr = {}

def run(self):

while True:

filename = self.product.get()

try:

f = open(filename, "rb")

readlines = f.read()

f.close()

offset = 24

while len(readlines) >offset:

self.pcap_pkthdr["len"] = readlines[offset+12:offset+16]

try:

length = struct.unpack("I", self.pcap_pkthdr["len"])[0]

self.stack.put(readlines[offset+16:offset+16+length])

offset += length + 16

except Exception, errmsg:

logger.error("unpack pcap_pkthdr failed: %s" % errmsg)

except IOError, errmsg:

logger.error("open file failed: %s" % errmsg)

在获得TCP数据段的数据包之后,问题就简单多了,根据大家的具体需求就可以进行相应的分析了,我这里是想分析其HTTP协议数据,同样也借助了dpkt软件包进行分析,如下:

def worker(memcache, packet, local_address, remote_address):

try:

p = dpkt.ethernet.Ethernet(packet)

if p.data.__class__.__name__ == "IP":

srcip = "%d.%d.%d.%d" % tuple(map(ord, list(p.data.src)))

dstip = "%d.%d.%d.%d" % tuple(map(ord, list(p.data.dst)))

if p.data.data.__class__.__name__ == "TCP":

tcpacket = p.data.data

if tcpacket.dport == 80 and dstip == local_address:

srcport = tcpacket.sport

key = srcip + ":" + str(srcport)

if tcpacket.data:

if not memcache.has_key(key):

memcache[key] = {}

if not memcache[key].has_key("response"):

memcache[key]["response"] = None

if memcache[key].has_key("data"):

memcache[key]["data"] += tcpacket.data

else:

memcache[key]["data"] = tcpacket.data

else:

if memcache.has_key(key):

memcache[key]["response"] = dpkt.http.Request(memcache[key]["data"])

try:

stackless.tasklet(connection)(memcache[key]["response"], local_address, remote_address)

stackless.run()

except Exception, errmsg:

logger.error("connect remote remote_address failed: %s", errmsg)

logger.debug("old headers(none content-length): %s", memcache[key]["response"])

memcache.pop(key)

except Exception, errmsg:

logger.error("dpkt.ethernet.Ethernet failed in worker: %s", errmsg)

如果大家只是想单纯的获取IP地址、端口、流量信息,那么问题就更简单了,这里只是抛砖引玉。另外再提供一段代码供参考:

import pcap, dpkt, struct

import binascii

def main():

a = pcap.pcap()

a.setfilter('udp portrange 4000-4050')

try:

for i,pdata in a:

p=dpkt.ethernet.Ethernet(pdata)

src='%d.%d.%d.%d' % tuple(map(ord,list(p.data.src)))

dst='%d.%d.%d.%d' % tuple(map(ord,list(p.data.dst)))

sport = p.data.data.sport

dport = p.data.data.dport

qq = int( binascii.hexlify(p.data.data.data[7:11]) , 16 )

print 'QQ: %d, From: %s:%d , To: %s:%d' % (qq,src,sport,dst,dport)

except Exception,e:

print '%s' % e

n = raw_input()

if __name__ == '__main__':

main()