在python类中无痛苦地描述数据包的结构:不需要编写for循环或嵌套if条件。
bisturi的Python项目详细描述
bisturi是一个以不那么痛苦的方式解析二进制数据的库:不 需要编写“for”循环,而不是嵌套“if”条件这是一种 “你所看到的就是你所说的”解析器,它允许你 以声明方式打包和解包字节。
让我们通过例子来看看bisturi
这是典型的类型长度值包,我们可以如何描述 在比斯图里。
>>>frombisturi.packetimportPacket>>>frombisturi.fieldimportInt,Data>>>classTypeLengthValue(Packet):...type=Int(1)...length=Int(2)...value=Data(length)
>;从源代码很容易看出类型是1字节的整数 同时长度是2的整数。值是 长度字节的可变大小。
这就是你所需要的。bisturi的主要目标是 你要写简单的类,易于阅读和理解 一切都在分析幕后的细节。
在包定义之后,您可以在一次调用中解析任何字节字符串:
>>>raw=b'\t\x00\x04ABCD'>>>tlv=TypeLengthValue.unpack(raw)>>>tlv.type9>>>tlv.length4>>>tlv.value'ABCD'
你还可以解析(解包)一个字节字符串,你可以做相反的事情, 将数据包打包成字节字符串:
>>>tlv.pack()'\t\x00\x04ABCD'
int和data不是唯一可用的字段。下面是一个例子 如何描述位掩码
>>>frombisturi.fieldimportBits>>>classFrameControl(Packet):...length=Bits(6)...more_fragments=Bits(1)...fragment_offset=Bits(9)...data=Data(length)>>>raw=b'\x0c\x05abc'>>>fc=FrameControl.unpack(raw)>>>fc.length3>>>fc.more_fragments0>>>fc.fragment_offset5>>>fc.data'abc'
下面是如何描述一系列值(aka list)和 可选字段:
>>>classImage1D(Packet):...has_name=Bits(1)...count_numbers=Bits(7)......numbers=Int(1).repeated(count_numbers)...optional_name=Data(until_marker=b'\x00').when(has_name)>>>raw_without_name=b'\x03ABC'>>>image1d=Image1D.unpack(raw_without_name)>>>image1d.has_name0>>>image1d.count_numbers3>>>image1d.numbers[65,66,67]>>>image1d.optional_nameisNoneTrue>>>raw_with_name=b'\x83ABCsome null terminated name\x00garbage-garbage'>>>image1d=Image1D.unpack(raw_with_name)>>>image1d.has_name1>>>image1d.numbers[65,66,67]>>>image1d.optional_name'some null terminated name'
不仅可以使用字段的单个值定义大小或 其他字段的计数,但您可以描述任意表达式或 甚至对需要语句的更复杂的语句使用callable (在python中,它们不是表达式;请考虑“if”语句)。
我的意思是:
>>>classMatrix(Packet):...rows=Int(1)...columns=Int(1)......values=Int(1).repeated(rows*columns)# arithmetic operations>>>classAddress(Packet):...ip_address=Int(1).repeated(4)...domain_name=Data(until_marker=b'\x00').when((ip_address[:3]==[0,0,0])&...(ip_address[3]!=0))# subscript and comparisions>>>classToken(Packet):...size=Int(1)...data=Data(byte_count=lambdapkt,raw,offset,**k:pkt.sizeifpkt.size<8else8)...# ^-- an arbitrary callable is allowed too>>>raw_matrix=b'\x02\x03ABCDEF'>>>matrix_2x3=Matrix.unpack(raw_matrix)>>>cols=matrix_2x3.columns>>>matrix_2x3.values[0:cols]# first row[65,66,67]>>>matrix_2x3.values[cols:cols*2]# second row[68,69,70]>>>raw_resolved_address=b'\xc0\xa8\x00\x01'>>>resolved_address=Address.unpack(raw_resolved_address)>>>resolved_address.ip_address[192,168,0,1]>>>resolved_address.domain_nameisNoneTrue>>>raw_unresolved_address=b'\x00\x00\x00\x01example.com\x00'>>>unresolved_address=Address.unpack(raw_unresolved_address)>>>unresolved_address.ip_address[0,0,0,1]>>>unresolved_address.domain_name'example.com'>>>raw_small_token=b'\x01A'>>>small_token=Token.unpack(raw_small_token)>>>small_token.data'A'>>>raw_too_long_token=b'\xffABCD1234EFGH5678'>>>truncated_token=Token.unpack(raw_too_long_token)>>>truncated_token.data'ABCD1234'