Linux C UDP Socket编程:如何在数据包数据中间插入0x00字节?
问题描述
正在进行Linux C UDP编程实验,尝试查询游戏服务器的当前状态,流程如下:
- 向服务器发送固定消息,服务器回复包含挑战值的消息
- 再发送原消息并附加该挑战值
已写出基础代码并能运行,但协议要求在数据包的数据中间插入空字节(0x00),目前代码缺少该字节(已通过Wireshark确认此问题)。现有代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char **argv){ char *ip = "192.168.1.183"; int port = 27015; int sockfd; struct sockaddr_in addr; char buffer[1024]; char concbuf[1024]; char challenge[10]; socklen_t addr_size; sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&addr, '\0', sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); //Send this message to get a reply with a challenge value: bzero(buffer, 1024); strcpy(buffer, "ÿÿÿÿTSource Engine Query"); sendto(sockfd, buffer, 25, 0, (struct sockaddr*)&addr, sizeof(addr)); printf("[+]Data send: %s\n", buffer); //Recieve the challenge value: bzero(buffer, 1024); addr_size = sizeof(addr); recvfrom(sockfd, challenge, 9, 0, (struct sockaddr*)&addr, &addr_size); printf("[+]Data recv: %s\n", challenge); //Add the challenge value and message together: char* conchal; char* min4 = (challenge +5); //Remove the first 5 bytes, not wanted. printf("[+]Strip : %s\n",min4); conchal = malloc(strlen(min4)+25); strcpy(conchal,"ÿÿÿÿTSource Engine Query"); //Would like to add a 0x00 after this strcat(conchal,min4); //Concatenate the challange value and the message. sendto(sockfd, conchal, 29, 0, (struct sockaddr*)&addr, sizeof(addr)); printf("[+]Data send: %s\n", conchal); //Works, however, no 0x00 between the message and the challenge, which is needed. //This should return server information bzero(buffer, 1024); addr_size = sizeof(addr); recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr*)&addr, &addr_size); printf("[+]Data recv: %s\n", buffer); return 0; }
当前卡在如何在原消息和挑战值之间添加0x00字节这一步,请问最优实现方法是什么?(注:当前代码较为粗糙,仅处于实验阶段)
解决方案
核心问题是C标准库的字符串函数(strcpy/strcat)会把0x00当作字符串结束符,无法直接用来构造包含空字节的二进制数据。需要改用内存操作函数手动拼接数据,以下是两种可靠实现:
方法1:使用memcpy精准构造数据包
这是最推荐的方式,能明确控制每一个字节的内容:
// 替换原有的拼接代码部分 char* conchal; char* min4 = (challenge +5); // 跳过前5字节的无效数据 printf("[+]Strip : %s\n",min4); // 计算所需内存:原消息固定长度(24) + 1个0x00 + 挑战值长度 size_t base_msg_len = 24; // "ÿÿÿÿTSource Engine Query"的实际字节数是24 size_t chal_len = strlen(min4); conchal = malloc(base_msg_len + 1 + chal_len); if (!conchal) { perror("malloc failed"); exit(EXIT_FAILURE); } // 1. 复制原消息到缓冲区起始位置 memcpy(conchal, "ÿÿÿÿTSource Engine Query", base_msg_len); // 2. 在原消息末尾插入0x00字节 conchal[base_msg_len] = 0x00; // 3. 复制挑战值到0x00之后的位置 memcpy(conchal + base_msg_len + 1, min4, chal_len); // 发送时必须指定准确的总字节数,不能用strlen sendto(sockfd, conchal, base_msg_len + 1 + chal_len, 0, (struct sockaddr*)&addr, sizeof(addr)); printf("[+]Data send (with 0x00): 原消息+0x00+%s\n", min4);
方法2:分步内存赋值(更直观)
如果觉得memcpy不够直观,可以用下标直接操作内存:
// 替换原有的拼接代码部分 char* conchal; char* min4 = (challenge +5); size_t base_msg_len = 24; size_t chal_len = strlen(min4); conchal = malloc(base_msg_len + 1 + chal_len); if (!conchal) { perror("malloc failed"); exit(EXIT_FAILURE); } // 复制原消息 memcpy(conchal, "ÿÿÿÿTSource Engine Query", base_msg_len); // 手动插入空字节 conchal[base_msg_len] = '\0'; // 复制挑战值 memcpy(conchal + base_msg_len + 1, min4, chal_len); // 发送时使用准确的总长度 sendto(sockfd, conchal, base_msg_len + 1 + chal_len, 0, (struct sockaddr*)&addr, sizeof(addr));
关键注意事项
- 禁止用
strlen计算带空字节的数据包长度:strlen遇到0x00就会停止计数,必须手动记录固定长度或使用接收时的实际字节数。 - 发送时必须指定准确的字节数:不能用
strlen(conchal),否则会因为中间的0x00导致发送长度不足。 - 内存分配要留足空间:确保
malloc的大小能容纳所有数据,避免缓冲区溢出。
内容的提问来源于stack exchange,提问作者ObligedTester
相关产品推荐
相关产品推荐

