You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

关键细节说明

  1. 多字节长度的端序:从你给的例子(0x82 0x01 0x3b → 0x013b)可以看出,协议用的是大端字节序,所以我们用int.from_bytes(..., byteorder='big')来解析。
  2. 字段顺序的重要性:Scapy是按fields_desc的顺序依次解析的,所以必须严格对应协议的字节流顺序,不能乱序。
  3. MMS PDU的读取:你原来的StrLenField用length_from=lambda pkt: pkt.length_c是对的,解析完Length C后,就可以用它来读取后续的MMS PDU内容。

关于MMS协议解析的其他建议

如果你不想从头写所有MMS字段的解析,其实Scapy社区有一些第三方扩展已经实现了MMS协议的基础解析,但如果你需要精准控制每个字段的解析逻辑,上面的自定义字段方案是最灵活的。

备注:内容来源于stack exchange,提问作者klin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 10:59:33