如何在Scapy中实现ISO 8823表示层可变长度字段的解析(适配MMS协议)
如何在Scapy中实现ISO 8823表示层可变长度字段的解析(适配MMS协议)
我完全懂你现在的困扰——这种带动态编码规则的长度字段,在Scapy里直接用内置字段确实搞不定。不过别担心,我们可以通过自定义Scapy字段类型来完美适配ISO 8823的长度规则,一次性解决Length A、B、C这三个字段的解析问题。
核心思路:自定义可变长度字段
ISO 8823的长度编码规则其实是一种常见的TLV变长编码(最高位标识是否为扩展长度,低7位标识后续字节数)。我们可以继承Scapy的Field类,实现一个专门处理这种规则的字段ISO8823LengthField,这样三个长度字段都能复用这个逻辑,不用重复写代码。
自定义字段代码实现
先把这个核心的自定义字段写出来,每一步我都加了注释:
from scapy.all import Packet, Field, ByteField, StrLenField class ISO8823LengthField(Field): """ 适配ISO 8823表示层的可变长度字段解析规则: 1. 若第一个字节 <= 0x7F,直接作为长度值 2. 若第一个字节 >= 0x80,取低7位为后续字节数,读取对应字节后按大端拼接为实际长度 """ def __init__(self, name, default): super().__init__(name, default, fmt="!I") # 用大端格式处理多字节长度 def from_bytes(self, pkt: bytes, pos: int = 0): # 读取第一个字节判断类型 first_byte = pkt[pos] pos += 1 if first_byte <= 0x7F: # 短长度:直接返回当前字节值 return first_byte, pos else: # 长长度:低7位是后续需要读取的字节数 byte_count = first_byte & 0x7F # 读取对应字节数,按大端拼接成整数 length_bytes = pkt[pos:pos+byte_count] pos += byte_count # 把字节串转成大端整数 length = int.from_bytes(length_bytes, byteorder='big') return length, pos def to_bytes(self, pkt, val): # 你说只需要解析不需要构建,这里可以简化实现(如果后续需要构建再完善) if val <= 0x7F: return bytes([val]) else: # 计算需要的字节数 byte_count = (val.bit_length() + 7) // 8 # 生成前缀字节:0x80 | 字节数 prefix = 0x80 | byte_count # 转成大端字节串 length_bytes = val.to_bytes(byte_count, byteorder='big') return bytes([prefix]) + length_bytes
修改你的X226类,替换为自定义字段
现在把你原来的X226类里的占位符替换成这个自定义字段,注意字段的顺序要严格对应协议结构:
class X226(Packet): name = "X.226p (ISO 8823 Presentation Layer for MMS)" fields_desc = [ ByteField("fully_encoded_data", 0x61), # 替换为自定义的Length A字段 ISO8823LengthField("length_a", 0), ByteField("pdv_list", 0x30), # Length B字段 ISO8823LengthField("length_b", 0), ByteField("presentation_context_tag", 0x02), ByteField("length", 0x01), ByteField("mms_annex_version1", 0x03), ByteField("single_asn1_type_tag", 0xa0), # Length C字段 ISO8823LengthField("length_c", 0), # 根据Length C的值读取MMS PDU内容 StrLenField("mms_pdu", b"", length_from=lambda pkt: pkt.length_c) ] # 测试一下你的例子 if __name__ == "__main__": # 测试短长度例子:0x61 0x21 0x30... test_pkt1 = bytes.fromhex("61213000") pkt1 = X226(test_pkt1) print("测试短Length A:", hex(pkt1.length_a)) # 应该输出0x21 # 测试长长度例子1:0x61 0x81 0xbc 0x30... test_pkt2 = bytes.fromhex("6181bc3000") pkt2 = X226(test_pkt2) print("测试长Length A(1字节扩展):", hex(pkt2.length_a)) # 应该输出0xbc # 测试长长度例子2:0x61 0x82 0x01 0x3b 0x30... test_pkt3 = bytes.fromhex("6182013b3000") pkt3 = X226(test_pkt3) print("测试长Length A(2字节扩展):", hex(pkt3.length_a)) # 应该输出0x13b
关键细节说明
- 多字节长度的端序:从你给的例子(0x82 0x01 0x3b → 0x013b)可以看出,协议用的是大端字节序,所以我们用
int.from_bytes(..., byteorder='big')来解析。 - 字段顺序的重要性:Scapy是按
fields_desc的顺序依次解析的,所以必须严格对应协议的字节流顺序,不能乱序。 - MMS PDU的读取:你原来的
StrLenField用length_from=lambda pkt: pkt.length_c是对的,解析完Length C后,就可以用它来读取后续的MMS PDU内容。
关于MMS协议解析的其他建议
如果你不想从头写所有MMS字段的解析,其实Scapy社区有一些第三方扩展已经实现了MMS协议的基础解析,但如果你需要精准控制每个字段的解析逻辑,上面的自定义字段方案是最灵活的。
备注:内容来源于stack exchange,提问作者klin
相关产品推荐
相关产品推荐

