【问题标题】:Understanding raw socket packet header了解原始套接字数据包头
【发布时间】:2018-02-17 03:27:43
【问题描述】:

我在 Python 中使用this code 编写了一个套接字数据包嗅探器。

import socket, struct

# Setup socket object
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
s.bind((socket.gethostbyname(socket.gethostname()), 0))
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

Id = 0
while (True):
    data = s.recvfrom(65565)
    packet = data[0]
    address = data[1]
    header = struct.unpack("!BBHHHBBHBBBBBBBB", packet[:20])

    if (header[6] == 6):
        protocol = "TCP"
    elif (header[6] == 17):
        protocol = "UDP"

    print("Number: ", Id, end="\t")
    print("Protocol: ", protocol, end="\t")
    print("Address: ", address, end="")
    print("Header: ", header)
    #print("Data: ", data)
    print()
    Id += 1
  1. 我知道我可以在数据变量 (data[0]) 的索引 0 处找到接收方的 IP 地址,但是我在数据包的哪个位置可以找到发送方的 IP 地址和端口号?
  2. 标题元组中的所有整数是什么意思?我知道 header[6] 是 TCP/UDP 协议,但是其余的呢?

    标题:(69, 0, 61, 1541, 0, 128, 17, 0, 192, 168, 56, 1, 255, 255, 255, 255)

【问题讨论】:

    标签: python python-3.x sockets


    【解决方案1】:

    IP 标头有一个well-defined structure 和一个RFC。你决定用字节和短裤解压它。一般来说,您希望将每个字段的大小与正确的数据类型相匹配。看看适当的标题大小,您可以看到各个细分:

        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
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |Version|  IHL  |Type of Service|          Total Length         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |         Identification        |Flags|      Fragment Offset    |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |  Time to Live |    Protocol   |         Header Checksum       |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                       Source Address                          |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Destination Address                        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Options                    |    Padding    |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    

    这可以通过编程方式完成:

    struct.unpack('!BBHHHBBHII')
    # alternatively:
    struct.unpack('!BBHHHBBH4s4s')
    

    我更喜欢后者,因为您可以很容易地将其转换为 IP 地址:

    以太网头示例:

    0000   3c 4a 92 1f 04 00 74 c6 3b 8d 82 69 08 00 45 00  <J....t.;..i..E.
    0010   00 3c 0a 24 40 00 40 06 23 76 0a 14 01 0d 01 01  .<.$@.@.#v......
    0020   01 01 e7 7a 04 d2 a2 5e 0c d2 00 00 00 00 a0 02  ...z...^........
    0030   72 10 0d 51 00 00 02 04 05 b4 04 02 08 0a 9c d4  r..Q............
    0040   c0 c0 00 00 00 00 01 03 03 07                    ..........
    

    20 字节 IPv4 标头存在于 packet[14:34]。用上述格式解压它会产生这样的结果:

    >>> header = struct.unpack('!BBHHHBBH4s4s', packet[14:34])
    >>> header
    (69, 0, 60, 2596, 16384, 64, 6, 9078, b'\n\x14\x01\r', b'\x01\x01\x01\x01')
    

    索引 8 和 9 分别是源 IP 和目标 IP。因为它们是字节,我们可以将它们转换为int然后str得到字符串格式的IP:

    # Note: Python 3 only
    >>> ip_src = '.'.join(map(str, header[8]))  # 10.20.1.13
    >>> ip_dst = '.'.join(map(str, header[9]))  # 1.1.1.1
    

    【讨论】:

    • 关于你的最后一段,到int的转换在哪里?
    • 我说错了。在 python 3 中,遍历字节字符串会自动将每个字节转换为int。 \n 的值是 0x0a 或十进制的 10。 map(str, ...) 会将其转换为 "10" 而不是 10。
    • 我以为他在使用 python3,因为我没有看到 print_function import
    • 是的,我使用的是 Python 3。目标地址与ipconfig 中我的计算机或路由器 IP 地址不匹配,当我 ping cmd.exe 中的源地址和目标地址时,我得到'请求超时。'
    • @Goodies Grate 回答,您说“您希望将每个字段的大小与正确的数据类型相匹配。”我在哪里检查并获得此协议的正确类型,其他协议如以太网('!6s6sH')。
    猜你喜欢
    • 1970-01-01
    • 2022-01-01
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多