坚持使用 Scapy 文档中的 VarLenQField 示例

Stuck with VarLenQField example from Scapy's documentation

我正在查看 Adding new protocols 的 Scapy 示例,但我被卡住了。由于 string->bytes 转换,他们页面上显示的旧代码在 Python 3 中抛出错误,但这是小事。我已经编写了自己的 vlenq2str()str2vlenq() 实现。我分别称它们为 vlenq2m()m2vlenq()vlenq2m 将整数值转换为字节(用于数据包的原始数据),m2vlenq 将这些字节从数据包的原始数据转换回整数(作为 scapy 内部表示)。

我的实际问题是,在调用数据包的 show2 方法后的某个时候,scapy 抛出一个 TypeError,我不知道为什么。

这是我的最小示例(来自 scapy 文档的代码的现代化版本):

#!/usr/bin/env python


from scapy.fields import Field, StrLenField
from scapy.packet import Packet, ls
from scapy.compat import raw


def vlenq2m(val: int) -> bytes:
    s = list()
    s.append(val & 0x7F)
    val = val >> 7
    while val:
        s.append(0x80 | (val & 0x7F))
        val = val >> 7
    s.reverse()
    return bytes(s)


def m2vlenq(m: bytes=b"") -> tuple[bytes, int]:
    i = l = 0
    for n in m:
        l = l << 7
        l = l + (n & 0x7F)
        i = i + 1
        if not n & 0x80:
            break
    return m[i + 1:], l


class VarLenQField(Field):
    """variable length quantities"""
    __slots__ = ["fld"]
    def __init__(self, name, default, fld):
        Field.__init__(self, name, default)
        self.fld = fld
    def i2m(self, pkt, x):
        if x is None:
            f = pkt.get_field(self.fld)
            x = f.i2len(pkt, pkt.getfieldval(self.fld))
            x = vlenq2m(x)
        return raw(x)
    def m2i(self, pkt, x):
        if s is None:
            return None, 0
        return m2vlenq(x)[1]
    def addfield(self, pkt, s, val):
        return s + self.i2m(pkt, val)
    def getfield(self, pkt, s):
        return m2vlenq(s)


class Foo(Packet):
    name = "Foo"
    fields_desc = [
            VarLenQField("len", None, "data"),
            StrLenField("data", "", "len"),
            ]


if __name__ == "__main__":
    f = Foo(data="test data")
    breakpoint()
    f.show2()

这是错误堆栈:

$ python -i sominimal.py
Traceback (most recent call last):
  File "D:\Python\mlp-monitor\src\mlptc\sominimal.py", line 64, in <module>
    f.show2()
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 1289, in show2
    return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py", line 266, in __call__
    i.__init__(*args, **kargs)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 158, in __init__
    self.dissect(_pkt)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 875, in dissect
    s = self.do_dissect(s)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\packet.py", line 839, in do_dissect
    s, fval = f.getfield(self, s)
  File "C:\ProgramData\Anaconda3\envs\mmon\lib\site-packages\scapy\fields.py", line 1380, in getfield
    len_pkt = self.length_from(pkt)
TypeError: 'NoneType' object is not callable

这里是我对调试器的仔细检查,直到出现错误为止。

$ python -i sominimal.py
> d:\python\mlp-monitor\src\mlptc\sominimal.py(65)<module>()
-> f.show2()
(Pdb) s
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\packet.py(1277)show2()
-> def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\packet.py(1289)show2()
-> return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl)
(Pdb) s
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(50)raw()
-> def raw(x):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(53)raw()
-> return bytes(x)
(Pdb) n
--Return--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\compat.py(53)raw()->b'\ttest data'
-> return bytes(x)
(Pdb) n
--Call--
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(256)__call__()
-> def __call__(cls, *args, **kargs):
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(257)__call__()
-> if "dispatch_hook" in cls.__dict__:
(Pdb) n
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(265)__call__()
-> i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
(Pdb) s
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(266)__call__()
-> i.__init__(*args, **kargs)
(Pdb) type(i)
<class '__main__.Foo'>
(Pdb) type(i.__init__)
<class 'method'>
(Pdb) n
TypeError: 'NoneType' object is not callable
> c:\programdata\anaconda3\envs\mmon\lib\site-packages\scapy\base_classes.py(266)__call__()
-> i.__init__(*args, **kargs)

你非常接近。您只错过了一个小而奇怪的怪癖:在 StrLenField

中使用函数而不是字符串
class Foo(Packet):
    name = "Foo"
    fields_desc = [
        VarLenQField("len", None, "data"),
        StrLenField("data", "", length_from=lambda pkt: pkt.len)
    ]

还哇,页面上有一个使用字符串的错误示例。我会修好的