1、前言
本想借助dpkt解析mail、dns、http来辅助分析pcap包进行分析,查阅资料学习却发现并不如使用scapy那么方便。
dpkt是一个python模块,可以对简单的数据包创建/解析,以及基本TCP / IP协议的解析,速度很快。
dpkt 手册
看官方手册发现DPKT是读取每个pcap包里的内容,用isinstance判断是不是有IP的包,再判断是属于哪个协议,对应的协议已经封装好API如果发现可以匹配某个协议API就输出来相关值。
想要扩展这个源码还需要去学习一下协议相关的字段含义。
API调用:
在手册中找到了在Github中部分API的示例代码,具备参考价值。
2、手册例子
以下代码是手册中的例子,通过查询发现inet_pton无法直接使用,按照网络上的解决方法修改了一下。
打印数据包
使用DPKT读取pcap文件并打印出数据包的内容。打印出以太网帧和IP数据包中的字段。
python2测试代码:
#!/usr/bin/env python"""Use DPKT to read in a pcap file and print out the contents of the packetsThis example is focused on the fields in the Ethernet Frame and IP packet"""import dpktimport datetimeimport socketfrom dpkt.compat import compat_ordimport ctypesimport osdef mac_addr(address): """Convert a MAC address to a readable/printable string Args: address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06') Returns: str: Printable/readable MAC address """ return ':'.join('%02x' % compat_ord(b) for b in address)class sockaddr(ctypes.Structure): _fields_ = [("sa_family", ctypes.c_short), ("__pad1", ctypes.c_ushort), ("ipv4_addr", ctypes.c_byte * 4), ("ipv6_addr", ctypes.c_byte * 16), ("__pad2", ctypes.c_ulong)]if hasattr(ctypes, 'windll'): WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringAelse: def not_windows(): raise SystemError( "Invalid platform. ctypes.windll must be available." ) WSAStringToAddressA = not_windows WSAAddressToStringA = not_windowsdef inet_pton(address_family, ip_string): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) if WSAStringToAddressA( ip_string, address_family, None, ctypes.byref(addr), ctypes.byref(addr_size) ) != 0: raise socket.error(ctypes.FormatError()) if address_family == socket.AF_INET: return ctypes.string_at(addr.ipv4_addr, 4) if address_family == socket.AF_INET6: return ctypes.string_at(addr.ipv6_addr, 16) raise socket.error('unknown address family')def inet_ntop(address_family, packed_ip): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) ip_string = ctypes.create_string_buffer(128) ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) if address_family == socket.AF_INET: if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv4_addr, packed_ip, 4) elif address_family == socket.AF_INET6: if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv6_addr, packed_ip, 16) else: raise socket.error('unknown address family') if WSAAddressToStringA( ctypes.byref(addr), addr_size, None, ip_string, ctypes.byref(ip_string_size) ) != 0: raise socket.error(ctypes.FormatError()) return ip_string[:ip_string_size.value - 1]# Adding our two functions to the socket libraryif os.name == 'nt': socket.inet_pton = inet_pton socket.inet_ntop = inet_ntopdef inet_to_str(inet): return socket.inet_ntop(socket.AF_INET, inet)def print_packets(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # packet num count r_num = 0 # For each packet in the pcap process the contents for timestamp, buf in pcap: r_num=r_num+1 print ('packet num count :' , r_num ) # Print out the timestamp in UTC print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) print('Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now unpack the data within the Ethernet frame (the IP packet) # Pulling out src, dst, length, fragment info, TTL, and Protocol ip = eth.data # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n' % \ (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset))def test(): """Open up a test pcap file and print out the packets""" with open('pcap222.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_packets(pcap)if __name__ == '__main__': test()
输出:
('packet num count :', 4474)('Timestamp: ', '2017-08-01 03:55:03.314832')('Ethernet Frame: ', '9c:5c:8e:76:bf:24', 'ec:88:8f:86:14:5c', 2048)IP: 192.168.1.103 -> 211.90.25.31 (len=52 ttl=64 DF=1 MF=0 offset=0)('packet num count :', 4475)('Timestamp: ', '2017-08-01 03:55:03.485679')('Ethernet Frame: ', '9c:5c:8e:76:bf:24', 'ec:88:8f:86:14:5c', 2048)IP: 192.168.1.103 -> 180.97.33.12 (len=114 ttl=64 DF=0 MF=0 offset=0)('packet num count :', 4476)('Timestamp: ', '2017-08-01 03:55:03.486141')('Ethernet Frame: ', '9c:5c:8e:76:bf:24', 'ec:88:8f:86:14:5c', 2048)IP: 192.168.1.103 -> 119.75.222.122 (len=52 ttl=64 DF=1 MF=0 offset=0)
打印ICMP
检查ICMP数据包并显示ICMP内容。
#!/usr/bin/env python"""Use DPKT to read in a pcap file and print out the contents of the packetsThis example is focused on the fields in the Ethernet Frame and IP packet"""import dpktimport datetimeimport socketfrom dpkt.compat import compat_ordimport ctypesimport osdef mac_addr(address): """Convert a MAC address to a readable/printable string Args: address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06') Returns: str: Printable/readable MAC address """ return ':'.join('%02x' % compat_ord(b) for b in address)class sockaddr(ctypes.Structure): _fields_ = [("sa_family", ctypes.c_short), ("__pad1", ctypes.c_ushort), ("ipv4_addr", ctypes.c_byte * 4), ("ipv6_addr", ctypes.c_byte * 16), ("__pad2", ctypes.c_ulong)]if hasattr(ctypes, 'windll'): WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringAelse: def not_windows(): raise SystemError( "Invalid platform. ctypes.windll must be available." ) WSAStringToAddressA = not_windows WSAAddressToStringA = not_windowsdef inet_pton(address_family, ip_string): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) if WSAStringToAddressA( ip_string, address_family, None, ctypes.byref(addr), ctypes.byref(addr_size) ) != 0: raise socket.error(ctypes.FormatError()) if address_family == socket.AF_INET: return ctypes.string_at(addr.ipv4_addr, 4) if address_family == socket.AF_INET6: return ctypes.string_at(addr.ipv6_addr, 16) raise socket.error('unknown address family')def inet_ntop(address_family, packed_ip): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) ip_string = ctypes.create_string_buffer(128) ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) if address_family == socket.AF_INET: if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv4_addr, packed_ip, 4) elif address_family == socket.AF_INET6: if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv6_addr, packed_ip, 16) else: raise socket.error('unknown address family') if WSAAddressToStringA( ctypes.byref(addr), addr_size, None, ip_string, ctypes.byref(ip_string_size) ) != 0: raise socket.error(ctypes.FormatError()) return ip_string[:ip_string_size.value - 1]# Adding our two functions to the socket libraryif os.name == 'nt': socket.inet_pton = inet_pton socket.inet_ntop = inet_ntopdef inet_to_str(inet): return socket.inet_ntop(socket.AF_INET, inet)def print_icmp(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # packet num count r_num = 0 # For each packet in the pcap process the contents for timestamp, buf in pcap: r_num=r_num+1 print ('packet num count :' , r_num ) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Now check if this is an ICMP packet if isinstance(ip.data, dpkt.icmp.ICMP): icmp = ip.data # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) print( 'Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type) print( 'IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % \ (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset)) print('ICMP: type:%d code:%d checksum:%d data: %s\n' % (icmp.type, icmp.code, icmp.sum, repr(icmp.data)))def test(): """Open up a test pcap file and print out the packets""" with open('pcap222.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_icmp(pcap)if __name__ == '__main__': test()
输出:
('packet num count :', 377)('Timestamp: ', '2017-08-01 03:45:56.403640')('Ethernet Frame: ', 'ec:88:8f:86:14:5c', '9c:5c:8e:76:bf:24', 2048)IP: 202.118.168.73 -> 192.168.1.103 (len=56 ttl=253 DF=0 MF=0 offset=0)ICMP: type:3 code:13 checksum:52074 data: Unreach(data=IP(len=28, id=2556, off=16384, ttl=61, p=6, sum=36831, src='\xc0\xa8\x01g', dst='\xcal\x17q', opts='', data='n\xb1\x00P\x85)=]'))
打印HTTP请求
#!/usr/bin/env python"""Use DPKT to read in a pcap file and print out the contents of the packetsThis example is focused on the fields in the Ethernet Frame and IP packet"""import dpktimport datetimeimport socketfrom dpkt.compat import compat_ordimport ctypesimport osdef mac_addr(address): """Convert a MAC address to a readable/printable string Args: address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06') Returns: str: Printable/readable MAC address """ return ':'.join('%02x' % compat_ord(b) for b in address)class sockaddr(ctypes.Structure): _fields_ = [("sa_family", ctypes.c_short), ("__pad1", ctypes.c_ushort), ("ipv4_addr", ctypes.c_byte * 4), ("ipv6_addr", ctypes.c_byte * 16), ("__pad2", ctypes.c_ulong)]if hasattr(ctypes, 'windll'): WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringAelse: def not_windows(): raise SystemError( "Invalid platform. ctypes.windll must be available." ) WSAStringToAddressA = not_windows WSAAddressToStringA = not_windowsdef inet_pton(address_family, ip_string): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) if WSAStringToAddressA( ip_string, address_family, None, ctypes.byref(addr), ctypes.byref(addr_size) ) != 0: raise socket.error(ctypes.FormatError()) if address_family == socket.AF_INET: return ctypes.string_at(addr.ipv4_addr, 4) if address_family == socket.AF_INET6: return ctypes.string_at(addr.ipv6_addr, 16) raise socket.error('unknown address family')def inet_ntop(address_family, packed_ip): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) ip_string = ctypes.create_string_buffer(128) ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) if address_family == socket.AF_INET: if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv4_addr, packed_ip, 4) elif address_family == socket.AF_INET6: if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv6_addr, packed_ip, 16) else: raise socket.error('unknown address family') if WSAAddressToStringA( ctypes.byref(addr), addr_size, None, ip_string, ctypes.byref(ip_string_size) ) != 0: raise socket.error(ctypes.FormatError()) return ip_string[:ip_string_size.value - 1]# Adding our two functions to the socket libraryif os.name == 'nt': socket.inet_pton = inet_pton socket.inet_ntop = inet_ntopdef inet_to_str(inet): return socket.inet_ntop(socket.AF_INET, inet)def print_http_requests(pcap): """Print out information about each packet in a pcap Args: pcap: dpkt pcap reader object (dpkt.pcap.Reader) """ # packet num count r_num = 0 # For each packet in the pcap process the contents for timestamp, buf in pcap: r_num=r_num+1 print ('packet num count :' , r_num ) # Unpack the Ethernet frame (mac src/dst, ethertype) eth = dpkt.ethernet.Ethernet(buf) # Make sure the Ethernet data contains an IP packet if not isinstance(eth.data, dpkt.ip.IP): print('Non IP Packet type not supported %s\n' % eth.data.__class__.__name__) continue # Now grab the data within the Ethernet frame (the IP packet) ip = eth.data # Check for TCP in the transport layer if isinstance(ip.data, dpkt.tcp.TCP): # Set the TCP data tcp = ip.data # Now see if we can parse the contents as a HTTP request try: request = dpkt.http.Request(tcp.data) except (dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError): continue # Pull out fragment information (flags and offset all packed into off field, so use bitmasks) do_not_fragment = bool(ip.off & dpkt.ip.IP_DF) more_fragments = bool(ip.off & dpkt.ip.IP_MF) fragment_offset = ip.off & dpkt.ip.IP_OFFMASK # Print out the info print('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))) print('Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type) print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset)) print('HTTP request: %s\n' % repr(request)) # Check for Header spanning acrossed TCP segments if not tcp.data.endswith(b'\r\n'): print('\nHEADER TRUNCATED! Reassemble TCP segments!\n')def test(): """Open up a test pcap file and print out the packets""" with open('pcap222.pcap', 'rb') as f: pcap = dpkt.pcap.Reader(f) print_http_requests(pcap)if __name__ == '__main__': test()
输出:
Timestamp: 2004-05-13 10:17:08.222534Ethernet Frame: 00:00:01:00:00:00 fe:ff:20:00:01:00 2048IP: 145.254.160.237 -> 65.208.228.223 (len=519 ttl=128 DF=1 MF=0 offset=0)HTTP request: Request(body='', uri='/download.html', headers={'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip,deflate', 'connection': 'keep-alive', 'keep-alive': '300', 'accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1', 'user-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113', 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'host': 'www.ethereal.com', 'referer': 'http://www.ethereal.com/development.html'}, version='1.1', data='', method='GET')Timestamp: 2004-05-13 10:17:10.295515Ethernet Frame: 00:00:01:00:00:00 fe:ff:20:00:01:00 2048IP: 145.254.160.237 -> 216.239.59.99 (len=761 ttl=128 DF=1 MF=0 offset=0)HTTP request: Request(body='', uri='/pagead/ads?client=ca-pub-2309191948673629&random=1084443430285&lmt=1082467020&format=468x60_as&output=html&url=http%3A%2F%2Fwww.ethereal.com%2Fdownload.html&color_bg=FFFFFF&color_text=333333&color_link=000000&color_url=666633&color_border=666633', headers={'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip,deflate', 'connection': 'keep-alive', 'keep-alive': '300', 'accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1', 'user-agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.6) Gecko/20040113', 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'host': 'pagead2.googlesyndication.com', 'referer': 'http://www.ethereal.com/download.html'}, version='1.1', data='', method='GET')...
打印出以太网IP
594 MB的pcap解析速度是127秒。
# coding=utf-8import dpktimport socketimport timeimport ctypesimport osimport datetime# 测试dpkt获取IP运行时间# 使用dpkt获取时间戳、源IP、目的IPclass sockaddr(ctypes.Structure): _fields_ = [("sa_family", ctypes.c_short), ("__pad1", ctypes.c_ushort), ("ipv4_addr", ctypes.c_byte * 4), ("ipv6_addr", ctypes.c_byte * 16), ("__pad2", ctypes.c_ulong)]if hasattr(ctypes, 'windll'): WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringAelse: def not_windows(): raise SystemError( "Invalid platform. ctypes.windll must be available." ) WSAStringToAddressA = not_windows WSAAddressToStringA = not_windowsdef inet_pton(address_family, ip_string): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) if WSAStringToAddressA( ip_string, address_family, None, ctypes.byref(addr), ctypes.byref(addr_size) ) != 0: raise socket.error(ctypes.FormatError()) if address_family == socket.AF_INET: return ctypes.string_at(addr.ipv4_addr, 4) if address_family == socket.AF_INET6: return ctypes.string_at(addr.ipv6_addr, 16) raise socket.error('unknown address family')def inet_ntop(address_family, packed_ip): addr = sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) ip_string = ctypes.create_string_buffer(128) ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) if address_family == socket.AF_INET: if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv4_addr, packed_ip, 4) elif address_family == socket.AF_INET6: if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): raise socket.error('packed IP wrong length for inet_ntoa') ctypes.memmove(addr.ipv6_addr, packed_ip, 16) else: raise socket.error('unknown address family') if WSAAddressToStringA( ctypes.byref(addr), addr_size, None, ip_string, ctypes.byref(ip_string_size) ) != 0: raise socket.error(ctypes.FormatError()) return ip_string[:ip_string_size.value - 1]# Adding our two functions to the socket libraryif os.name == 'nt': socket.inet_pton = inet_pton socket.inet_ntop = inet_ntopdef inet_to_str(inet): return socket.inet_ntop(socket.AF_INET, inet)def getip(pcap): Num = 0 for timestamp, buf in pcap: eth = dpkt.ethernet.Ethernet(buf) # 对没有IP段的包过滤掉 if eth.type != dpkt.ethernet.ETH_TYPE_IP: continue ip = eth.data ip_src = inet_to_str(ip.src) ip_dst = inet_to_str(ip.dst) # 打印时间戳,源->目标 #print(ts + " " + ip_src + "-->" + ip_dst) Num= Num+1 print ('{0}\ttime:{1}\tsrc:{2}-->dst:{3} '.format(Num,timestamp,ip_src ,ip_dst)) if eth.data.__class__.__name__ == 'IP': ip = '%d.%d.%d.%d' % tuple(map(ord, list(eth.data.dst))) if eth.data.data.__class__.__name__ == 'TCP': if eth.data.data.dport == 80: print eth.data.data.data # http 请求的数据if __name__ == '__main__': starttime = datetime.datetime.now() f = open('pcap222.pcap', 'rb') # 要以rb方式打开,用r方式打开会报错 pcap = dpkt.pcap.Reader(f) getip(pcap) endtime = datetime.datetime.now() print ('time : {0} seconds '.format((endtime - starttime).seconds))
输出:
1290064 time:1501562988.75 src:113.142.85.151-->dst:192.168.1.1031290065 time:1501562988.75 src:192.168.1.103-->dst:113.142.85.151 1290066 time:1501562988.75 src:192.168.1.103-->dst:113.142.85.151 1290067 time:1501562988.75 src:113.142.85.151-->dst:192.168.1.1031290068 time:1501562988.75 src:192.168.1.103-->dst:113.142.85.151 1290069 time:1501562988.76 src:192.168.1.103-->dst:113.142.85.151 1290070 time:1501562988.76 src:122.228.91.14-->dst:192.168.1.1031290071 time:1501562988.76 src:192.168.1.103-->dst:113.142.85.151 1290072 time:1501562988.76 src:113.142.85.151-->dst:192.168.1.1031290073 time:1501562988.76 src:192.168.1.103-->dst:113.142.85.151 1290074 time:1501562988.76 src:192.168.1.103-->dst:113.142.85.151GET / HTTP/1.1Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*Accept-Language: zh-cnUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Mac_PowerPC; en) Opera 9.24Referer: -Connection: Keep-AliveHost: win7.shangshai-qibao.cn