pacpng文件格式说明

文件在这里可以找到:PcapngReaderPython/pcapng_demo.py · duocore/TurtleRock - Gitee.com#coding=utf-8## 说明文字大部分是这个网址翻译的:# https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.txt# 注意:# 1、测试的pcapng文件里有些类型的包没有出现,下面的脚本执行过程中有一些未覆盖到。# 2、一些option因为时间关系没有解析。后续
摘要由CSDN通过智能技术生成

文件在这里可以找到:

PcapngReaderPython/pcapng_demo.py · duocore/TurtleRock - Gitee.com

#coding=utf-8
#
# 说明文字大部分是这个网址翻译的:
# https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.txt
# 注意:
# 1、测试的pcapng文件里有些类型的包没有出现,下面的脚本执行过程中有一些未覆盖到。
# 2、一些option因为时间关系没有解析。后续有时间再补
# 3、运行版本python3.4+

from enum import Enum
from optparse import OptionParser

def round_to_4byte(input_integer_value):
    """
    32位向上对齐。例如输入值=3,则返回值=4。又比如0=>0, 5=>8
    :param input_integer_value: 需要向上对齐的数字
    :return:
    """
    return (input_integer_value + 3) & (~3)

class BlockTypeCode(Enum):
    BTS_SectionHeaderBlock = 0x0A0D0D0A
    BTS_InterfaceDescriptionBlock = 0x00000001
    BTS_PacketBlock_Obsolete = 0x00000002
    BTS_SimplePacketBlock = 0x00000003
    BTS_NameResolutionBlock = 0x00000004
    BTS_InterfaceStatisticsBlock = 0x00000005
    BTS_EnhancedPacketBlock = 0x00000006
    BTS_DecryptionSecretsBlock = 0x0000000A
    BTS_CustomBlockWhichCanBeCopied = 0x00000BAD
    BTS_CustomBlockWhichShouldNotBeCopied = 0x40000BAD

class PcapngBlockHeader:
    """
    3.1.  General Block Structure
    捕获文件以块的形式组织,这些块相互堆叠以形成文件。 所有块共享一个通用格式,如图1所示。.
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                          Block Type                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                      Block Total Length                       |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                          Block Body                           /
    /          /* 可变长度, 但是必须32位(4字节)对齐*/             /
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                      Block Total Length                       |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    Figure 1: Basic block structure.
    这些字段具有以下含义:
    Block Type (32 bits): 标识块的唯一值。最高有效位(MSB)等于1的值保留供本地使用。 They allow to save private data
        to the file and to extend the file format. The list of currently defined types can be found in Appendix B
    Block Total Length: 此块的总大小,以字节为单位。例如一个没有正文的块的长度是12个字节。
    Block Body: 块的内容。
    Block Total Length: 此块的总大小,以字节为单位。该字段和复制前值放在尾部以方便文件的反向访问。
    这种在所有块之间共享的结构使得处理文件和跳过不需要或未知的块变得容易。 一些块可以在内部包含
    其他块(嵌套块)。 一些块是必需的,即如果它们不存在则转储文件无效,其他块是可选的。
    The General Block Structure allows defining other blocks if needed. A parser that does non understand them
    can simply ignore their content.
    """
    block_type = -1
    block_total_length = -1
    _is_big_endian = True

    def __init__(self, is_big_endian):
        self._is_big_endian = is_big_endian

    def read_from_file_object(self, file_object):
        #open("file.pcapng", "rb") with file_object
        return_value = False
        try:
            string_from_bytes_endian = "big"
            if not self._is_big_endian:
                string_from_bytes_endian = "little"
            data = file_object.read(4)
            # 一般而言 big: 网络字节序, little:主机字节序
            self.block_type = int.from_bytes(data, string_from_bytes_endian) # 'big' or 'little'
            data = file_object.read(4)
            self.block_total_length = int.from_bytes(data, string_from_bytes_endian)
            print("block type:%x, block total length:%x"%(self.block_type, self.block_total_length))
            return_value = True
        except Exception as e:
            print(e)
        return return_value

class PcapngOptions:
    """
    3.5.  Options
    所有block的body部分都可以嵌入可选字段。可选字段可用于插入一些在读取数据时可能有用的信息,但对于数据包处理来
    说并不是真正需要的。因此每个工具都可以读取可选字段(如果有)的内容,或者跳过其中的一些甚至全部。
    一次跳过所有可选字段很简单,因为大多数块由具有固定格式的第一部分和可选的第二个部分组成。 因此,块长度字段(
    Block Length,出现在通用块结构中,参见第 2.1 节)可用于跳过所有内容,直到下一个块。
    选项是一系列类型 - 长度 - 值字段,每个字段包含一个值:
    Option Type (2 bytes): 它包含指定当前 TLV 记录类型的代码。最高有效位等于 1 的选项类型保留供本地使用;因此,
    不能保证所用的code在所有捕获文件(由其他应用程序生成)中是唯一的。In case of vendor-specific extensions
    that have to be identified uniquely, vendors must request an Option Code whose MSB is equal to zero.
    Option Length (2 bytes): 它包含以下“选项值”字段的实际长度,不含填充字节。
    Option Value (variable length): 它包含给定选项的值,与32位边界对齐(4字节对齐)。该字段的实际长度(即排除
    填充字节后)由选项长度字段指定。
    选项可能会重复多次(例如一个接口有多个关联的IP地址)TODO: mention for each option, if it can/shouldn't
    appear more than one time. The option list is terminated by a Option which uses the special
    'End of Option' code (opt_endofopt).
    The format of the optional fields is shown in Figure 7.
     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |      Option Code              |         Option Length         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                       Option Value                            /
    /         /* variable length, 4字节对齐(32 bits)*/            /
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                                                               /
    /                 . . . other options . . .                     /
    /                                                               /
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |   Option Code == opt_endofopt  |  Option Length == 0          |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     Figure 7: Options format.
    以下code可以出现在任何可选字段中:
    Name          Code    Length    Description                              Example(s)
    opt_endofopt	0       0         It delimits the end of the optional fields. This block cannot be repeated within a given list of options.
    opt_comment   1      variable	 包含与当前块关联的注释的 UTF-8 字符串。	"This packet is the beginning of all of our problems" / "Packets 17-23 showing a bogus TCP retransmission, as reported in bugzilla entry 1486!" / "Captured at the southern plant" / "I've checked again, now it's working ok" / ...
    """
    _option_buffer = None
    _option_length = 0

    def __init__(self, option_buffer, option_length):
        self._option_buffer = option_buffer
        self._option_length = option_length

    def parse_option_for_section_header_block(self, section_endian):
        """
        解析并打印所有用于section header block的options。
        除了第2.5节中定义的选项外,还有以下选项在此块中有效:
        名字          代码  长度  说明                                         例子
        shb_hardware  2     可变  UTF-8字符串,描述创建此section的硬件。     "x86 Personal Computer" / "Sun Sparc Workstation" / ...
        shb_os        3     可变  UTF-8字符串,创建此section的操作系统名称。 "Windows XP SP2" / "openSUSE 10.2" / ...
        shb_userappl  4     可变  UTF-8字符串,创建此section的应用程序名称。 "dumpcap V0.99.7" / ...
        :section_endian: "big"或者"little"表示大端还是小端机器
        :return:
        """
        remain_option_length = self._option_length
        cursor = 0
        opt_endofopt = 0
        pnp = {2:"opt_shb_hardware", 3:"opt_shb_os", 4:"opt_shb_userappl"}
        while remain_option_length != 0:
            option_code = int.from_bytes(self._option_buffer[cursor : cursor+2], section_endian)
            if option_code == opt_endofopt:
                print("match end of option list op code.")
                break
            option_length = int.from_bytes(self._option_buffer[cursor+2 : cursor+4], section_endian)
            option_value = self._option_buffer[cursor+4 : cursor+4+option_length]
            if option_code in pnp:
                print("option:%s, option_length:%d(%d)" % (pnp[option_code], option_length, round_to_4byte(option_length)),
                      "option_value:", option_value)
            else:
                print("option:%d, option_length:%d(%d)"%(option_code, option_length, round_to_4byte(option_length)),
                      "option_value:", option_value)
            cursor += (4 + round_to_4byte(option_length))

    def parse_option_for_interface_description_block(self, section_endian):
        pnp = {2: "if_name", 3: "if_description", 4: "if_IPv4addr",
               5: "if_IPv6addr", 6: "if_MACaddr", 7: "if_EUIaddr",
               8: "if_speed", 9: "if_tsresol", 10: "if_tzone",
               11: "if_filter", 12: "if_os", 13: "if_fcslen",
               14: "if_tsoffset"}
        remain_option_length = self._option_length
        cursor = 0
        opt_endofopt = 0
        while remain_option_length != 0:
            option_code = int.from_bytes(self._option_buffer[cursor: cursor + 2], section_endian)
            if option_code == opt_endofopt:
                print("match end of option list op code.")
                break
            option_length = int.from_bytes(self._option_buffer[cursor + 2: cursor + 4], section_endian)
            option_value = self._option_buffer[cursor + 4: cursor + 4 + option_length]
            if option_code in pnp:
                print("option:%s, option_length:%d(%d)" % (
                pnp[option_code], option_length, round_to_4byte(option_length)),
                      "option_value:", option_value)
            else:
                print("option:%d, option_length:%d(%d)" % (option_code, option_length, round_to_4byte(option_length)),
                      "option_value:", option_value)
            cursor += (4 + round_to_4byte(option_length))


class PcapngSecionHeaderBlock:
    """
    Section Header Block是必要数据。它用于标识捕获转储文件的一个section的开头。 Section Header Block不包含数据,
    而是标识逻辑相关的块(接口、数据包)列表。
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +---------------------------------------------------------------+
    0 |                   Block Type = 0x0A0D0D0A                     |
      +---------------------------------------------------------------+
    4 |                      Block Total Length                       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |                      Byte-Order Magic                         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    12|          Major Version        |         Minor Version         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    16|                                                               |
      |                          Section Length                       |
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    24/                                                               /
      /                      Options (variable)                       /
      /                                                               /
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                      Block Total Length                       |
      +---------------------------------------------------------------+
      Section Header Block 的块类型是与4个字符的字符串“\r\n\n\r”对应的整数(0x0A0D0D0A)。使用此值有两个原因:
      1,此数字用于检测文件是否已通过FTP或 HTTP 从一台机器传输到另一台机器并进行了不适当的ASCII转换。在这种情况
      下,此字段的值将不同于标准值 ("\r\n\n\r"),并且读取方可以检测到可能已损坏的文件。
      2,该值是回文的,因此无论节的字节序如何,读取方都能够识别节标题块。字节顺序是通过读取Byte-Order Magic来识
      别的,它位于块类型之后的8个字节。
      Block Total Length: total size of this block,以字节为单位。
      Byte-Order Magic: 幻数,其值为十六进制数 0x1A2B3C4D。这个数字可以用来区分这个section是小端机还是大端机上生成的。
      Major Version:当前格式主版本号。当前值为1。如果格式的更改导致工具无法读取旧格式存档,则该值应该更改。
      Minor Version:当前格式次版本号。当前值为0。如果格式的更改导致工具无法读取旧格式存档,则该值应该更改。
      Section Length:此section的长度,以字节为单位,不包含本Section Header Block的长度,此字段可用于跳过该section
      ,以便在大文件中更快地导航。Section Length为-1(0xFFFFFFFFFFFFFFFF)表示未指定节的大小,跳过该节的唯一方法是
      解析其包含的块(block)。请注意如果此字段有效(即不是 -1),则其值始终与32位对齐,因为所有块(block)都与32位
      边界对齐。此外在访问此字段时应特别小心:由于文件中所有块的对齐方式都是32位,因此不能保证此字段与64位边界对齐
      。这可能在64位工作站上发生问题。
      Options: 可选的选项列表(根据第2.5节中定义的规则格式化)
      添加新的块类型或选项不一定需要更改Major或Minor版本号,因为不知道块类型或选项的代码可以跳过它;仅当跳过块或选项
      会导致无法工作时,才需要更改Minor版本号。
      除了第2.5节中定义的选项外,还有以下选项在此块中有效:
      名字          代码  长度  说明                                         例子
      shb_hardware  2     可变  UTF-8字符串,描述创建此section的硬件。     "x86 Personal Computer" / "Sun Sparc Workstation" / ...
      shb_os        3     可变  UTF-8字符串,创建此section的操作系统名称。 "Windows XP SP2" / "openSUSE 10.2" / ...
      shb_userappl  4     可变  UTF-8字符串,创建此section的应用程序名称。 "dumpcap V0.99.7" / ...
    """
    magic = -1
    version_major = -1
    version_minor = -1
    section_length = -1 # 如值为-1表示未知
    _endian = ""
    incorrect_format = False
    block_type = 0x0A0D0D0A
    block_total_length = -1
    block_total_length_with_padding = -1

    def read_from_file_object(self, file_object):
        return_value = False
        try:
            # 这里先不计算,因为还不知道字节序,下面要根据magic的值确定是否要转换。
            # file_offset + 8 (函数调用前还有个0x0A0D0D0A头)
            block_total_length_raw = file_object.read(4)

            # file_offset + 12
            data = file_object.read(4)
            self.magic = int.from_bytes(data, "big")
            if 0x1A2B3C4D != self.magic:
                self._endian = "little"
                self.magic = int.from_bytes(data, "little")
                if 0x1A2B3C4D != self.magic:
                    self.incorrect_format = True
                    return False
            else:
                self._endian = "big"
            self.block_total_length = int.from_bytes(block_total_length_raw, self._endian)
            self.block_total_length_with_padding = round_to_4byte(self.block_total_length)
            print("block type:%#x, block total length:%#x(with padding: %#x)"%(self.block_type,
                                                                               self.block_total_length,
                                                                               self.block_total_length_with_padding))
            # file_offset + 14
            data = file_object.read(2)
            self.version_major = int.from_bytes(data, self._endian)
            # file_offset + 16
            data = file_object.read(2)
            self.version_minor = int.from_bytes(data, self._endian)
            print("File version: %d.%d"%(self.version_major, self.version_minor))
            # file_offset + 24
            data = file_object.read(8)
            self.section_length = int.from_bytes(data, self._endian)
            print("section length : %#x" % self.section_length)

            data_raw_options = file_object.read(self.block_total_length_with_padding - 24 - 4)
            po = PcapngOptions(data_raw_options, self.block_total_length_with_padding - 24 - 4)
            po.parse_option_for_section_header_block(self._endian)

            data = file_object.read(4)
            block_total_length_backward = int.from_bytes(data, self._endian)
            print("Block total length backward:%#x"%block_total_length_backward)
            if block_total_length_backward != self.block_total_length:
                print("错误,前后块长度不一致。")
                return False

            return_value = True
        except Exception as e:
            print(e)
        return return_value

    def get_endian(self):
        return self._endian


class PcapngInterfaceDescriptionBlock:
    """4.2.  Interface Description Block (必需)
    网络接口描述块是必要的。 需要此块来细化进行捕获的网络接口的特征。为了将捕获的数据正确关联到相应的接口,必须在使用
    它的任何其他块之前定义接口描述块;因此,该块通常紧跟在Section Header Block之后。
    接口描述块仅在它所属的section内有效。The structure of a Interface Description Block is shown in Figure 9.
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +---------------------------------------------------------------+
     0 |                    Block Type = 0x00000001                    |
       +---------------------------------------------------------------+
     4 |                      Block Total Length                       |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&#
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值