概述
pcap2har: 把.pcap网络抓包文件转换为HTTP Archive file(HAR),是一个开源项目,使用python开发,依赖dpkt库( http://code.google.com/p/dpkt/),githup地址:https://github.com/andrewf/pcap2har。
可以通过简单的运行如下命令来使用该程序:
main.py my.pcap my.har
第一个参数是pcap文件名,第二个参数是输出的har文件的名称。
HTTP Archive (HAR)文件格式说明文档:
http://groups.google.com/group/http-archive-specification/web/har-1-1-spec?hl=en
安装dpkt
下载解压dpkt后,执行其中的setup.py(python setup.py install)
结构分析
main.py
主要部分
# parse pcap file
dispatcher = pcap.EasyParsePcap(filename=inputfile)
# parse HAR stuff
session = httpsession.HttpSession(dispatcher)
logging.info('Flows=%d. HTTP pairs=%d' % (len(session.flows), len(session.entries)))
#write the HAR file
with open(outputfile, 'w') as f:
json.dump(session, f, cls=har.JsonReprEncoder, indent=2, encoding='utf8', sort_keys=True)
主要步骤
1 调用pcap模块解析pcap文件
def EasyParsePcap(filename=None, reader=None):
'''
Like ParsePcap, but makes and returns a PacketDispatcher for you.
'''
dispatcher = PacketDispatcher()
ParsePcap(dispatcher, filename=filename, reader=reader)
dispatcher.finish()
return dispatcher
该函数首先创建一个PacketDispatcher(),它的作用如其名,就是一个分包器,即按照协议把包分发给不同的模块进行处理,比如TCP包分发给TCP模块进行处理,UDP包分发给UDP模块进行处理。接着调用ParsePcap函数,该函数通过dpkt解析pcap文件,读入pcap文件,输出一个个的Packet,每个packet的格式如下:
ts = packet[0] # 时间戳
buf = packet[1] # 帧数据
hdr = packet[2] # pcap文件头
实际的包数据在packet[1]中,包括了IP头、TCP/IP头、包数据。根据链路层协议的不同,读取帧数据的方式不同。
# handle SLL packets, thanks Libo
dltoff = dpkt.pcap.dltoff
if pcap.dloff == dltoff[dpkt.pcap.DLT_LINUX_SLL]:
eth = dpkt.sll.SLL(buf)
# otherwise, for now, assume Ethernet
else:
eth = dpkt.ethernet.Ethernet(buf)
dispatcher.add(ts, buf, eth)
如果是UDP包,可以这样取得包数据:
ip = eth.data
udppkt = ip.data
udpdata = udppkt.data
我们关心的实际上不是一个个单个的包,而是资源的加载时间等信息。单个资源的加载过程由发起请求开始,到接收完响应,这称之为一个flow,一个flow就可以抽象为几个部分,request、response、以及其它一些描述流的属性,称之为一个Entry。流是由一个个packet组成的,解析pcap文件,首先解析为一个个的packet,然后再组装为flow,最终形成一个Entry集。entries是HAR文件的重要组成部分。根据packet组成为流的过程,比较复杂,涉及到包的重组、数据解压等。
HAR文件的输出是由httpsession.py中json_repr完成的:
def json_repr(self):
'''
return a JSON serializable python object representation of self.
'''
d = {
'log': {
'version': '1.1',
'creator': {
'name': 'pcap2har',
'version': '0.1'
},
'browser': {
'name': self.user_agent,
'version': 'mumble'
},
'entries': sorted(self.entries, key=lambda x: x.ts_start)
}
}
if self.page_tracker:
d['log']['pages'] = self.page_tracker
return d
一个问题
dpkt_http_replacement.py中
def parse_headers(f):
"""Return dict of HTTP headers parsed from a file object."""
d = {}
while 1:
line = f.readline()
# regular dpkt checks for premature end of headers
# but that's too picky
line = line.strip()
if not line:
break
l = line.split(None, 1)
if not l[0].endswith(':'):
raise dpkt.UnpackError('invalid header: %r' % line)
k = l[0][:-1].lower()
v = len(l) != 1 and l[1] or ''
if k in d:
d[k] += ','+v
else:
d[k] = v
return d
对于一些不是严格遵从标准的响应头,会抛出dpkt.UnpackError。比如冒号“:”后面没有空白分隔符
SINA-LB:aGEuMzkuZzEuc2h4LmxiLnNpbmFub2RlLmNvbQ==
可以改为
def parse_headers(f):
"""Return dict of HTTP headers parsed from a file object."""
d = {}
while 1:
line = f.readline()
# regular dpkt checks for premature end of headers
# but that's too picky
line = line.strip()
if not line:
break
#to fix a bug of "raise error when line like this: SINA-TS:YzVmMjk0Y2UgMTMgMTUgMSAxMSA3Cg==\r\n"
pos = line.find(':')
if pos == -1:
raise dpkt.UnpackError('invalid header: %r' % line)
k = line[0:pos].lower()
v = line[pos+1:].strip()
if k in d:
d[k] += ','+v
else:
d[k] = v
return d
log信息
该模块执行过程中的log信息会输出到pcap2har.log中
response.msg.header结构
{'content-encoding': 'gzip', 'transfer-encoding': 'chunked', 'set-cookie': 'if_mid=1XgDcC; expires=Sun, 21-Sep-2014 09:42:53 GMT; path=/; domain=ifeng.com', 'vary': 'Accept-Encoding', 'server': 'IfengWeb/2.4.6', 'load-balancing': 'bjhh78', 'connection': 'close', 'date': 'Thu, 18 Sep 2014 09:42:53 GMT', 'content-type': 'text/html'}
request.msg结构
GET / HTTP/1.1
x-ucbrowser-ua: dv(H30-T10);pr(UCBrowser/9.9.4.484);ov(Android 4.2.2);ss(720*1230);bt(UC);pm(1);bv(1);nm(0);im(0);sr(0);nt(2);
accept-language: zh-CN
accept-encoding: gzip
connection: keep-alive
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,UC/145,plugin/1,alipay/un
user-agent: Mozilla/5.0 (Linux; U; Android 4.2.2; zh-CN; H30-T10 Build/JDQ39) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.9.4.484 U3/0.8.0 Mobile Safari/533.1
host: i.ifeng.com
cookie: if_sn818=3; if_mid=1XgDcC
referer: http://183.61.109.210:30003/c/c.php?url=http://i.ifeng.com
不确定的结构,可以在运行过程中通过print打印出来查看。