struct.pack 在重新分配其输入之一后再次调用时给出不同的结果

问题描述 投票:0回答:1

我正在编写一个 Python 脚本来按照 日本网站 中的示例发送 ICMP 数据包。 有一部分代码使用

struct.pack
将一系列变量打包到字节数组中。 它看起来像下面这样:

import struct

def make_icmp_echo_request():
    _type = 8
    code = 0
    check = 0
    _id = 1
    seq = 1

    packed = struct.pack("!BBHHH", _type, code, check, _id, seq)
    return packed

make_icmp_echo_request()

不管你调用多少次

make_icmp_echo_request
,输出都是一样的。 不过网站也实现了一个
checksum
功能,这个功能会在ICMP包打包后使用。 奇怪的是,网站的实现会在调用
struct.pack
之后再调用一次
checksum
。 像下面这样的东西:

import struct

def checksum(data):
    data_len = len(data) // 2 * 2
    csum = 0
    for i in range(0, data_len, 2):
        csum += (data[i + 1] << 8) + data[i]
    if len(data) % 2 != 0:
        csum += data[-1]
    while csum >> 16:
        csum = (csum >> 16) + (csum & 0xffff)
    csum = csum >> 8 | (csum << 8 & 0xff00)
    return ~csum&0xffff

def make_icmp_echo_request():
    _type = 8
    code = 0
    check = 0
    _id = 1
    seq = 1

    packed = struct.pack("!BBHHH", _type, code, check, _id, seq)
    check = checksum(packed)
    return struct.pack("!BBHHH", _type, code, check, _id, seq)

make_icmp_echo_request()

我发现第一次调用

struct.pack
的输出与第二次调用的输出不同。 只有在第一次通话后调用
checksum
时才会发生这种情况。

我用下面的代码确认了这一点(我还解压了打包的字节数组以使事情更清楚)。

import struct

def checksum(data):
    data_len = len(data) // 2 * 2
    csum = 0
    for i in range(0, data_len, 2):
        csum += (data[i + 1] << 8) + data[i]
    if len(data) % 2 != 0:
        csum += data[-1]
    while csum >> 16:
        csum = (csum >> 16) + (csum & 0xffff)
    csum = csum >> 8 | (csum << 8 & 0xff00)
    return ~csum&0xffff


def make_icmp_echo_request():
    _type = 8
    code = 0
    check = 0
    _id = 1
    seq = 1

    for i in range(10):
        print(f"[{i}]")
        packed = struct.pack("!BBHHH", _type, code, check, _id, seq)
        check = checksum(packed)
        print(packed, check)
        unpacked = struct.unpack("!BBHHH", packed)
        print(unpacked)

    return packed

make_icmp_echo_request()
*** Loop: 0 ***
b'\x08\x00\x00\x00\x00\x01\x00\x01' 63485
(8, 0, 0, 1, 1)
*** Loop: 1 ***
b'\x08\x00\xf7\xfd\x00\x01\x00\x01' 0
(8, 0, 63485, 1, 1)
*** Loop: 2 ***
b'\x08\x00\x00\x00\x00\x01\x00\x01' 63485
(8, 0, 0, 1, 1)
*** Loop: 3 ***
b'\x08\x00\xf7\xfd\x00\x01\x00\x01' 0
(8, 0, 63485, 1, 1)
*** Loop: 4 ***
b'\x08\x00\x00\x00\x00\x01\x00\x01' 63485
(8, 0, 0, 1, 1)
*** Loop: 5 ***
b'\x08\x00\xf7\xfd\x00\x01\x00\x01' 0
(8, 0, 63485, 1, 1)
*** Loop: 6 ***
b'\x08\x00\x00\x00\x00\x01\x00\x01' 63485
(8, 0, 0, 1, 1)
*** Loop: 7 ***
b'\x08\x00\xf7\xfd\x00\x01\x00\x01' 0
(8, 0, 63485, 1, 1)
*** Loop: 8 ***
b'\x08\x00\x00\x00\x00\x01\x00\x01' 63485
(8, 0, 0, 1, 1)
*** Loop: 9 ***
b'\x08\x00\xf7\xfd\x00\x01\x00\x01' 0
(8, 0, 63485, 1, 1)

谁能解释一下? 如果我使用第一次调用

struct.pack
的结果,ICMP 请求将失败也很奇怪。

python struct bytebuffer icmp
1个回答
0
投票
    check = 0
    ...
    packed = struct.pack("!BBHHH", _type, code, check, _id, seq)
    check = checksum(packed)
    return struct.pack("!BBHHH", _type, code, check, _id, seq)

pack()
的第一次调用为
check
传递 0。然后
check
被反弹到
checksum()
调用的结果。对
pack()
的第二次调用使用
check
的新值。所以当然结果是不同的,当且仅当
checksum()
不返回0.

© www.soinside.com 2019 - 2024. All rights reserved.