namedstruct二進(jìn)制結(jié)構(gòu)體解析庫(kù)
namedstruct 是一個(gè)解析二進(jìn)制結(jié)構(gòu)體的專(zhuān)用庫(kù),它不僅可以解析簡(jiǎn)單的 C 結(jié)構(gòu)體,還可以支持變長(zhǎng)結(jié)構(gòu)體、可擴(kuò)展的結(jié)構(gòu)體之類(lèi)復(fù)雜的情況。它被用來(lái)在 VLCP 中解析 OpenFlow(SDN 專(zhuān)用的二進(jìn)制協(xié)議)。
使用這個(gè)庫(kù),你會(huì)發(fā)現(xiàn),即便是像 OpenFlow 這樣復(fù)雜的協(xié)議,實(shí)際上只需要將對(duì)應(yīng)的 C 程序的頭文件(比如openflow.h)按照相應(yīng)的規(guī)則進(jìn)行修改,添加一些簡(jiǎn)單的參數(shù)說(shuō)明結(jié)構(gòu)體之間的關(guān)系,就可以用一行代碼將它完整解析出來(lái),或者用一行代碼生成出相應(yīng)的結(jié)構(gòu)體。
可以使用pip安裝:
pip install nstruct
示例
###### BASIC STRUCT #####
from namedstruct import *
mytype = nstruct((uint16, 'myshort'), # unsigned short int
(uint8, 'mybyte'), # unsigned char
(uint8,), # a padding byte of unsigned char
(char[5], 'mystr'), # a 16-byte bytes string
(uint8,),
(uint16[5], 'myarray'), #
name = 'mytype',
padding = 1)
# Create an object
obj0 = mytype()
# Access a field
s = obj0.myshort
obj0.myshort = 12
# Create an object with the specified fields initialized
obj1 = mytype(myshort = 2, mystr = b'123', myarray = [1,2,3,4,5])
# Unpack an object from stream, return the object and bytes size used
obj2,size = mytype.parse(b'\x00\x02\x01\x00123\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05')
# Unpack an object from packed bytes
obj3 = mytype.create(b'\x00\x02\x01\x00123\x00\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05')
# Estimate the struct size
size = len(obj0)
# Estimate the struct size excludes automatic padding bytes
size2 = obj0._realsize()
# Pack the object
b = obj0._tobytes()
b2 = mytype.tobytes(obj0)
# Use the type in other structs
mytype2 = nstruct((mytype, 'mydata'),
(mytype[4], 'mystructarray'),
name = 'mytype2',
padding = 1)
obj4 = mytype2()
obj4.mydata.myshort = 12
obj4.mystructarray[0].mybyte = 7
b3 = obj4._tobytes()
###### VARIABLE LENGTH TYPES #####
my_unsize_struct = nstruct((uint16, 'length'),
(raw, 'data'),
padding = 1,
name = 'my_unsize_struct')
"""
>>> my_unsize_struct.create(b'\x00\x07abcde').data
b'abcde'
>>> my_unsize_struct.parse(b'\x00\x07abcde')[0].data
b''
"""
my_size_struct = nstruct((uint16, 'length'),
(raw, 'data'),
padding = 1,
name = 'my_size_struct',
prepack = packrealsize('length'),
size = lambda x: x.length)
"""
packrealsize('length') is equivalent to:
def _packsize(x):
x.length = x._realsize()
"""
"""
>>> my_unsize_struct(data = b'abcde')._tobytes()
b'\x00\x07abcde'
>>> my_unsize_struct.parse(b'\x00\x07abcde')[0].data
b'abcde'
"""
##### EXTENDING #####
my_base = nstruct((uint16, 'length'),
(uint8, 'type'),
(uint8, 'basedata'),
name = 'my_base',
size = lambda x: x.length,
prepack = packrealsize('length'),
padding = 4)
my_child1 = nstruct((uint16, 'data1'),
(uint8, 'data2'),
name = 'my_child1',
base = my_base,
criteria = lambda x: x.type == 1,
init = packvalue(1, 'type'))
my_child2 = nstruct((uint32, 'data3'),
name = 'my_child2',
base = my_base,
criteria = lambda x: x.type == 2,
init = packvalue(2, 'type'))
"""
Fields and most base class options are inherited, e.g. size, prepack, padding
>>> my_child1(basedata = 1, data1 = 2, data2 = 3)._tobytes()
b'\x00\x07\x01\x01\x00\x02\x03\x00'
>>> my_child2(basedata = 1, data3 = 4)._tobytes()
b'\x00\x08\x02\x01\x00\x00\x00\x04'
"""
# Fields in child classes are automatically parsed when the type is determined
obj1, _ = my_base.parse(b'\x00\x07\x01\x01\x00\x02\x03\x00')
"""
>>> obj1.basedata
1
>>> obj1.data1
2
>>> obj1.data2
3
>>> obj1._gettype()
my_child1
"""
# Base type can be used in fields or arrays of other structs
my_base_array = nstruct((uint16, 'total_len'),
(my_base[0], 'array'),
name = 'my_base_array',
padding = 1,
size = lambda x: x.total_len,
prepack = packrealsize('total_len'))
obj2 = my_base_array()
obj2.array.append(my_child1(data1 = 1, data2 = 2, basedata = 3))
obj2.array.append(my_child2(data3 = 4, basedata = 5))
"""
>>> obj2._tobytes()
b'\x00\x12\x00\x07\x01\x03\x00\x01\x02\x00\x00\x08\x02\x05\x00\x00\x00\x04'
"""
obj3, _ = my_base_array.parse(b'\x00\x12\x00\x07\x01\x03\x00\x01\x02\x00\x00\x08\x02\x05\x00\x00\x00\x04')
"""
>>> obj3.array[0].data2
2
"""
評(píng)論
圖片
表情
