被 Pythons ctypes LittleEndianStructure 搞糊涂了

Confused by Pythons ctypes LittleEndianStructure

我正在尝试使用 ctypes 模块来解析使用 socket 模块捕获的数据包,但我对使用 LittleEndianStructure.

时看到的内容感到困惑

在 CentOS 7 64 位虚拟机上使用 Python 3.6.8,我正在捕获一个带有负载 0x8401

的 UDP 数据包

然后我想把payload解析成下面的结构,然后打印出字段值。

class MSG(BigEndianStructure):
    _fields_ = [
        ("A", c_ubyte, 4),
        ("B", c_ubyte, 3),
        ("C", c_ubyte, 1),
        ("D", c_ubyte, 4),
        ("E", c_ubyte, 4)
    ]

当我使用 BigEndianStructure 时,我得到了我期望的结果

MSG
  A: 8
  B: 2
  C: 0
  D: 0
  E: 1 

然后我尝试使用 LittleEndianStructure,我希望负载交换到 0x0184 并生成以下内容

MSG
  A: 0
  B: 0
  C: 1
  D: 8
  E: 4 

然而我却得到了以下信息,我不明白为什么。

MSG
  A: 4
  B: 0
  C: 1
  D: 1
  E: 0 

这是完整的代码。感谢您的帮助。

import socket
import sys
import struct
from ctypes import *

class IP(Structure):
    _fields_ = [
        ("ihl",          c_ubyte, 4),
        ("version",      c_ubyte, 4),
        ("tos",          c_ubyte),
        ("len",          c_ushort),
        ("id",           c_ushort),
        ("offset",       c_ushort),
        ("ttl",          c_ubyte),
        ("protocol_num", c_ubyte),
        ("sum",          c_ushort),
        ("src",          c_uint32),
        ("dst",          c_uint32)
    ]

    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer=None):
        pass

class UDP(BigEndianStructure):
    _fields_ = [
        ("sport", c_ushort),
        ("dport", c_ushort),
        ("length", c_ushort),
        ("checksum", c_ushort)
    ]

    def __new__(self, socket_buffer):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer):
        pass

class MSG(LittleEndianStructure):
    _fields_ = [
        ("A", c_ubyte, 4),
        ("B", c_ubyte, 3),
        ("C", c_ubyte, 1),
        ("D", c_ubyte, 4),
        ("E", c_ubyte, 4)
    ]

    def __new__(self, socket_buffer):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer):
        pass

def sniff():

    eth_length = 14
    ip_length = 20
    udp_length = 8
    msg_length = 2

    try:
        # Capture all packets
        protocol = socket.ntohs(0x0003)  # captures all packets
        sniffer = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, protocol)

    except KeyboardInterrupt:
        sys.exit()

    while True:
        # read a packet
        raw_buffer = sniffer.recvfrom(65535)[0]

        eth_header = raw_buffer[:eth_length]
        eth_payload = raw_buffer[eth_length:]

        eth = struct.unpack('!6s6sH', eth_header)
        eth_protocol = socket.ntohs(eth[2])

        # IP packet
        if eth_protocol == 8:
            # create an IP header from the first 20 bytes
            ip_header = IP(eth_payload[:ip_length])
            ip_data = eth_payload[ip_length:]

            # UDP packet
            if ip_header.protocol_num == 17:
                udp_header = UDP(ip_data[:udp_length])
                udp_data = ip_data[udp_length:]

                if udp_header.dport == 8001:
                    msg_header = MSG(udp_data[:msg_length])
                    print("MSG")
                    print("   A: %s" % msg_header.A)
                    print("   B: %s" % msg_header.B)
                    print("   C: %s" % msg_header.C)
                    print("   D: %s" % msg_header.D)
                    print("   E: %s" % msg_header.E)

if __name__ == '__main__':
    sniff()

实际的小端结果是从最低位开始取位。

字节:84 01

大端字 840116 (10000100000000012) 并划分从高位开始的位:

A    B   C D    E
1000 010 0 0000 0001 so ABCDE = 82001

little endian word 018416 (00000001100001002) 并从低位开始划分位:

E    D    C B   A
0000 0001 1 000 0100 so ABCDE = 40110