2021SC@SDUSC
PacketUtils.py
OSSIM-agent源代码分析(十二)
OSSIM Agent的主要职责是收集网络上存在的各种设备发送的所有数据,然后按照一种标准方式(standardized way)有序的发送给OSSIM Server,Agent收集到数据后在发送给Server之前要对这些数据进行标准化处理,这样Server就可以依一种统一的方式来处理这些信息,并且也简化了Server的处理过程。
PacketUtils是包处理函数,在对大量数据收集后,处理数据和检索包能力非常重要的一项工作。
OSSIM作为检测工具,基本的处理和检索功能的完备就是非常重要的一环
初始导报:
from binascii import hexlify
import socket, struct, sys
import time
from Logger import Logger
from Utils import dumphexdata
logger = Logger.logger
UDPPacket类
初始化方法,初始化数据,将payload和其他关键数据存入对象中
def __init__(self, data):
self._data = data
(self.sport, self.dport, self.length, self.checksum) = struct.unpack(">HHHH", data[0:8])
self._payload = data[8:]
payload get函数
def getpayload(self):
return self._payload
转存函数,打印相关信息,调用dumpexdata函数
def dump(self):
print "UDP Header"
print "SPORT:%u DPORT:%u LENGTH:%04x CHECKSUM:%04x" % (self.sport, self.dport, self.length, self.checksum)
print "Payload"
dumphexdata(self._payload)
str函数,通过正则表达式,匹配对应的字符串
def __str__(self):
st = """ udp_sport="%u" udp_dport="%u" udp_len="%u" udp_csum="%u" udp_payload="%s" """
st = st % (self.sport, self.dport, self.length, self.checksum, hexlify(self._payload))
return st
TCP包类
TCP格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
初始化函数: 数据中的TCP头可能是假的,必通过正则及匹配判断所有字段都是正确的或没有解码选项
def __init__(self,data):
self._data = data
(self.sport,self.dport, \
self.seq,self.ack,self.b1,self.b2, \
self.window,self.checksum,
self.urgent)=struct.unpack(">HHIIBBHHH",data[0:20])
self.offset = (self.b1 & 0xf0)>>4
self.res = (self.b1&0xf)|(self.b2&0xc0)<<4
self.__flags = {1:"C", 2:"E", 4:"U", 8:"A", 16:"P", 32:"R", 64:"S", 128:"F"}
self.__flags_keys = self.__flags.keys().sort()
self.flags = (self.b2 & 0x3f)
self.opt =[]
strlog ="""
tcp packet
sport = %s
dport = %s
seq = %s
ack = %s
b1 = 0x%02x
b2 = 0x%02x
offset = %d
""" % (self.sport,self.dport,self.seq,self.ack,self.b1,self.b2,self.offset)
if self.offset <= 5:
self.payload = data[20:]
else:
optionsize = self.offset*4-20
self.options = data[20:20+optionsize]
self.payload = data[20+optionsize:]
self._parseoptions()
str函数,通过正则表达式,匹配对应的字符串
def __str__(self):
st=""" tcp_sport="%u" tcp_dport="%u" """+\
""" tcp_seq="%u" tcp_ack="%u" tcp_offset="%u" """+\
""" tcp_flags="%u" """+\
""" tcp_window="%u" tcp_csum="%u" tcp_urgptr="%u" """
st = st % \
(self.sport,self.dport,
self.seq,self.ack,self.b1,self.b2,
self.window,self.checksum,self.urgent)
if len(self.opt)>0:
st_opt = """ tcp_optnum="%u" """ % len(self.opt)
i=0
for (c,l,v) in self.opt:
st_opt = st_opt + \
""" tcp_optcode="%u" tcp_optlen="%u" tcp_optpayload="%s" """
st_opt = st_opt % \
(c,l,hexlify(v))
i = i+1
else:
st_opt=""
st = st+st_opt+""" tcp_payload="%s" """ % hexlify(self.payload)
return st
解析选项函数,传入self参数,其中的options是核心,c判断是否处理更多选项,处理则将参数放入opt对象中,若无参数传入则报错
def _parseoptions(self):
options = self.options[:]
while len(options)>0:
c, = struct.unpack(">B",options[0])
if c==0:
self.opt.append((0,0,""))
return
elif c==1:
self.opt.append((1,0,""))
options=options[1:]
else:
try:
l, = struct.unpack(">B",options[1:2])
l = l - 2
if(l > (len(options)-2)):
logger.warning("Can't decode TCP option %02x: Payload:%s " % (c, hexlify(self.options)))
self.offset = 5
return
if (l>0):
v = options[2:2+l]
self.opt.append((c,l,v))
options=options[2+l:]
elif l==0:
v=""
self.opt.append((c,l,v))
options=options[2+l:]
else:
raise Exception, "Error processing TCP Options"
except Exception, e_msg:
options=options[2:]
logger.warning("Can't decode TCP option %02x: Payload:%s (exception: %s)" % (c, hexlify(self.options), str(e_msg)))
转存函数,打印相关信息,通过option长度判断通过option还是payload调用dumpexdata函数
def dump(self):
print "TCP Header"
print "SPORT:%u DPORT:%u" % (self.sport,self.dport)
print "SEQ:%08x ACK:%08x" % (self.seq,self.ack)
print "HDRLEN:%04x WINDOW:%04x FLAGS:%s" % (self.offset,self.window,self._getflags())
print "CHEKSUM:%04x URG:%04x" % (self.checksum,self.urgent)
if len(self.options)>0:
print "Options"
dumphexdata(self.options)
print "Payload"
dumphexdata(self.payload)
获取flags函数,获取self对象中flag的key
def _getflags(self):
st =""
for k in self.__flags_keys():
if self.flags & k:
st += self.__flags[k]
else:
st += "."
return st
根据socket.IPPROTO的信息返回连接类型
def getprotobynumber(n):
if n == socket.IPPROTO_ICMP:
return "ICMP"
elif n == socket.IPPROTO_UDP:
return "UDP"
elif n == socket.IPPROTO_TCP:
return "TCP"
else:
return "UNKNOWN:%u" % n
RawPacket、UnknownIPPacket类,存储以太网捕获的原始数据包
class RawPacket: class UnknownIPPacket:
def __init__(self,data):
self._data = data
转存函数,打印相关信息,调用dumpexdata函数
def dump(self):
dumphexdata(self._data)
str函数,通过正则表达式,匹配对应的字符串
def __str__(self):
return "raw_payload=\""+hexlify(self._data)+"\""
ICMPPacket类,存储一个ICMP报文
初始化函数,检查数据的长度,因为可能是一个假数据包
def __init__(self,data):
self._data = data
(self.type,self.code,self.checksum)= \
struct.unpack(">BBH",data[0:4])
if self.type == 0 or self.type == 8:
(self.icmp_id,self.icmp_seq) = struct.unpack(">HH",data[4:8])
self.packetpayload = data[8:]
else:
self.icmp_id = self.icmp_seq = 0
self.packetpayload = data[4:]
转存函数,打印相关信息
def dump(self):
print "ICMP Header"
print "TYPE: %02x CODE:%02x CHECKSUM:%04x" % (self.type,self.code,self.checksum)
str函数,通过正则表达式,匹配对应的字符串
def __str__(self):
st=""" icmp_type="%u" icmp_code="%u" """+\
""" icmp_csum="%u" icmp_id="%u" icmp_seq="%u" """ + \
""" icmp_payload="%s" """
st = st % (self.type,self.code,self.checksum,self.icmp_id,self.icmp_seq,hexlify(self.packetpayload))
return st
IPPacket类,存储一个IP数据包
初始化函数,先用包中的数据初始化类,然后解码IP报文
def __init__(self,data):
self.opt = []
if len(data)>=20:
self.packet = data
(self.version, self.tos, self.length, \
self.id,self.offset, \
self.ttl,self.protocol, \
self.checksum,self.sip, \
self.dip) = struct.unpack(">BBHHHBBHII",data[0:20])
self.hdrlen = (self.version & 0xf)
self.version = (self.version & 0xf0)>>4
if self.hdrlen == 5:
self.options=""
self.payload=data[20:20+self.length]
else:
lenopt = self.hdrlen*4-20
print "Opciones: %u " % lenopt
self.options=data[20:20+lenopt]
self._parseoptions()
self.payload=data[20+lenopt:]
if len(self.payload)>0:
if self.protocol == socket.IPPROTO_UDP:
self.packetpayload = UDPPacket(self.payload)
self.dport = self.packetpayload.dport
self.sport = self.packetpayload.dport
elif self.protocol == socket.IPPROTO_TCP:
self.packetpayload = TCPPacket(self.payload)
self.dport = self.packetpayload.dport
self.sport = self.packetpayload.sport
elif self.protocol == socket.IPPROTO_ICMP:
self.packetpayload = ICMPPacket(self.payload)
self.dport = self.sport = 0
else:
self.packetpayload = UnknownIPPacket(self.payload)
self.dport = self.sport = 0
else:
self.packetpayload = ""
else:
raise Exception,"Error: Incomplete ip packet"
str函数,通过正则表达式,匹配对应的字符串
def __str__(self):
st = """ip_ver="%u" ip_hdrlen="%u" ip_tos="%u" """ + \
"""ip_len="%u" ip_id="%u" """ + \
"""ip_offset="%u" """ + \
"""ip_ttl="%u" ip_proto="%u" """ + \
"""ip_csum="%u" """ + \
"""ip_src="%s" ip_dst="%s" """
st = st % \
(self.version,self.hdrlen,self.tos,
self.length,self.id,
self.offset,
self.ttl,self.protocol,
self.checksum,socket.inet_ntoa(struct.pack(">L",self.sip)),
socket.inet_ntoa(struct.pack(">L",self.dip)))
if len(self.opt)>0:
st_opt=""" ip_optnum="%u" """ % len(self.opt)
for (c,l,v) in self.opt:
print c,l,v
st_opt = st_opt + \
"""ip_optcode="%u" ip_optlen="%u" ip_optpayload="%s" """ % \
(c,l,hexlify(v))
else:
st_opt=""
if self.packetpayload<>"":
st = st+st_opt+str(self.packetpayload)
else:
st = st+st_opt
return st
转存函数,先转存ip包,然后打印选项
def dump(self):
print "IP Header"
print "Version:%02x hdrlen:%02x tos:%02x packetlen:%04x" % (self.version,self.hdrlen,self.tos,self.length)
print "ID:%04x Flags:%02x FragOffset:%04x" % (self.id,self.flags,self.offset)
print "TTL:%02x Protocol:%s Checksum:%04x" % (self.ttl,getprotobynumber(self.protocol),self.checksum)
print "SRC:%s DST:%s" % (socket.inet_ntoa(struct.pack("L",socket.htonl(self.sip))), \
socket.inet_ntoa(struct.pack("L",socket.htonl(self.dip))))
if len(self.options)>0:
print "Dump IP options:"
dumphexdata(self.options)
if self.packetpayload!=None:
self.packetpayload.dump()
IPV6Packet类,存储一个IPv6数据包
IPv6数据包格式
Oct byte 1 byte2 byte 3 byte 4
|________________|________________|________________|________________|
0 |version| class(8) | Flow label(20b) |
4 |payload len | next header | hop limit |
8 |_______________________src addr (128bits)__________________________|
12|___________________________________________________________________|
16|___________________________________________________________________|
20|___________________________________________________________________|
24|_______________________dest addr (128 bits)________________________|
28|___________________________________________________________________|
32|___________________________________________________________________|
36|___________________________________________________________________|
初始化函数,首先用包中的数据初始化类,然后指定下一个报头的类型
def __init__(self,data):
self.opt = []
if len(data)>=40:
vlc,pack_length,next_header_type,hop_lmt = struct.unpack("!IHBB",data[0:8])
self.src_ip = socket.inet_ntop(socket.AF_INET6,data[8:24])
self.dst_ip = socket.inet_ntop(socket.AF_INET6,data[24:40])
version = vlc >> 28
traffic_class = (vlc & 0x0ff00000) >> 20
label = (vlc & 0x000fffff)
header_len = 40
data_size = len(data)
self.packet = data
self.version = version
self.traffic_class = traffic_class
self.flow_label = label
self.payload_len = pack_length
self.hop_limit = hop_lmt
self.protocol = next_header_type
self.payload = data[40:40+self.payload_len]
if pack_length> 0:
if self.protocol == socket.IPPROTO_ICMP:
self.packetpayload = ICMPPacket(self.payload)
self.dport = 0
if self.protocol == socket.IPPROTO_TCP:
self.packetpayload = TCPPacket(self.payload)
self.dport = self.packetpayload.dport
self.sport = self.packetpayload.sport
if self.protocol == socket.IPPROTO_UDP:
self.packetpayload = UDPPacket(self.payload)
self.dport = self.packetpayload.dport
self.sport = self.packetpayload.dport
else:
logger.info("Unknown packet type:%s" % self.protocol)
self.packetpayload = ""
else:
raise Exception,"Error: Incomplete ip packet"
def __str__(self):
st = """ip_ver="%u" ip_traffic="%u" ip_flowlabel="%u" """ + \
"""ip_payload_len="%u" ip_proto="%u" """ + \
"""ip_hoplimit="%u" ip_src="%s" ip_dst="%s" """
st = st % \
(self.version, \
self.traffic_class, \
self.flow_label, \
self.payload_len, \
self.protocol, \
self.hop_limit, \
self.src_ip, \
self.dst_ip
)
if self.packetpayload <> "":
st = st + str(self.packetpayload)
return st
存储ip包函数
def dump(self):
print "IP Header"
print "Version:0x%x traffic_class:0x%02x flow_label:0x%03x" % (self.version, self.traffic_class, self.flow_label)
print "payload_len:0x%04x next_header:0x%02x hop_limit:0x%02x" % (self.packet_len, self.protocol, self.hop_limit)
print "SRC:%s DST:%s" % (self.src_ip, self.dst_ip)
if len(self.options) > 0:
print "Dump IP options:"
dumphexdata(self.options)
if self.packetpayload != None:
self.packetpayload.dump()