【问题标题】:Cannot properly memcpy a char array to struct无法正确地 memcpy 一个 char 数组来构造
【发布时间】:2023-03-16 05:43:01
【问题描述】:

所以我有一个叫做数据包的构造

struct Packet {
    unsigned int packet_type;
    wchar_t packet_length[128];
    wchar_t file_name[256];
    wchar_t template_name[256];
    wchar_t file_name_list[1024];
    wchar_t file_data[1024];

    void serialize(char * dat) {
        memcpy(dat, this, sizeof(Packet));
    }

    void deserialize(const char * dat) {
        memcpy(this, dat, sizeof(Packet));
    }
};

我正在尝试从这些数据中反序列化

{byte[2692]}
[0]    0       unsigned int packet_type; (4 bytes)
[1]    0
[2]    0
[3]    0
[4]    50 '2'  wchar_t packet_length[128]; (128 bytes)
[3]    0
[5]    54 '6'
[3]    0
[6]    57 '9'
[3]    0
[7]    50 '2'
[8]    0
[...]  0
[132]  112 'p'  wchar_t file_name[256]; (256 bytes)
[133]  0
[134]  104 'h'
[...]  0

但是反序列化中的 memcpy 没有给我文件名,但它确实给了我数据包长度。这是怎么回事?谢谢!

编辑: 所以现在我很清楚 wchar_t 占用的空间比我以前想象的要多;但是,我被告知根本不要使用 memcpy?

我已经编写了这个反序列化方法,它可以正确抓取数据。这还会导致安全漏洞吗?

void deserialize(const char * dat) {
        memcpy(&(packet_type), dat, 4);
        memcpy(&(packet_length[0]), dat + 4, 128);
        memcpy(&(file_name[0]), dat + 132, 256);
        memcpy(&(template_name[0]), dat + 388, 256);
        memcpy(&(file_name_list[0]), dat + 644, 1024);
        memcpy(&(file_data[0]), dat + 1668, 1024);
    }

【问题讨论】:

  • 在您的目标系统上仔细检查wchar_t 的大小。我打赌它不是 1 字节,所以你对大小的期望是错误的。 wchar_t file_name[256] 应该至少是 512 字节而不是 256。然后给出你问题的最后一部分,我敢打赌你正在查看数组中的错误偏移量。
  • 我想这在很大程度上是未定义的行为。即使不是,您也将charwchar_t 混合在一起。由于您的系统上wchar_t 的大小看起来是两个字节而不是四个,我猜这是在 Windows 机器上运行的。查看wmemcpywmemcpy_s
  • 请不要使用幻数来表示类型大小(例如,4 表示无符号整数的大小)。最安全的方法是使用 sizeof(varname)。这不仅考虑了 varname 类型的大小,还处理了 varname 类型被更改的实例。

标签: c++ char bytearray deserialization memcpy


【解决方案1】:

请不要使用这种方法来序列化结构。它完全不便携。

此外,编译器还可能会根据目标架构、字节序、优化和一堆其他事情来填充、对齐或重新排序成员。

更优雅的方式是使用 boost::Serialization,它以可移植的方式处理低级细节。

另一方面,如果您只想检查您的结构,那么 调试器 会派上用场...

【讨论】:

  • 这是正确答案。这正是导致安全漏洞和难以发现的错误的方法。
【解决方案2】:

char 数组的布局假定wchar_t 的大小为两个字节;不是 - 这是一个系统示例,其中wchar_t 的大小为4,因此Packet 的大小为10756,而不是2692 字节:(link to a demo)。

这就是为什么您在编辑中的memcpy 技巧会出现问题:它假定char[] 数组中的数据布局与wchar_t[] 数组的布局匹配,它可能匹配也可能不匹配。如果您知道您的数据数组具有以小端格式(LSB 优先)存储的两个字符元素,您可以编写自己的函数将数据从源转换到目标,并为部分序列化数据调用它,例如这个:

void bytes_to_wchar(wchar_t *dest, const unsigned char* src, size_t length) {
    for (size_t i = 0 ; i != lengt ; i++) {
        dest[i] = src[2*i] | (src[2*i+1] << 8);
    }
}

现在您可以使用此函数将数据复制到 wchar_t 数组中,而与目标系统上的 wchar_t 大小或目标系统的字节序无关:

void deserialize(const char * dat) {
    bytes_to_wchar(packet_type,       dat + 0,      4);
    bytes_to_wchar(packet_length[0],  dat + 4,    128);
    bytes_to_wchar(file_name[0],      dat + 132,  256);
    bytes_to_wchar(template_name[0],  dat + 388,  256);
    bytes_to_wchar(file_name_list[0], dat + 644,  1024);
    bytes_to_wchar(file_data[0],      dat + 1668, 1024);
}

当您在相同的硬件上使用相同的编译器执行此操作时,从内存中保存数据并将其写回的快捷方式可能会起作用。即使这样,它仍然对您使用的头文件和编译器设置中的小调整很敏感。

如果你需要复制到struct的字符数组有一个固定的布局,你需要写一个函数来处理这个布局,将两字节组转换为wchar_ts,四字节组转换为@ 987654338@s,等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多