tcp网络报文信息携带_TCP的SYN报文可以携带payload吗?

本文探讨了一种在TCP SYN报文中添加payload的非标准做法,通过示例代码展示了如何实现在SYN包中携带额外数据。尽管这违背了TCP协议标准,但实验表明在某些情况下可以成功进行通信。这种方法可以简化服务端配置,用于传递额外信息,如作为敲门报文,影响TCP握手过程。不过,可靠性及标准合规性问题仍然存在。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于敲门服务,是不是厌倦了复杂繁琐的服务端配置?

Send TCP SYN packet with payload?

众所周知,TCP的SYN报文是不能携带payload的,因为:

  • 序列号还没有协商号,无法确定数据的序列号区间。
  • 接收窗口还没有确定,不知道payload能不能被接收。

等等,等等…

烦透了!在过程中,我非常讨厌去讨论标准,讨厌有人在耳旁叨叨类似“RFC规定…但没有强制…”,“Intel手册XXX…但是…”,正如“C语言未初始化的变量到底是多少”这个问题一样无趣。

实际动手试一下不就好了吗?

请看客户端为SYN报文增加裸数据的代码:

663e0c2e14ad5ab58836e97b61592ff1.png
#include #include #include #include int port = 22;module_param(port, int, 0644);char *templ = NULL;module_param(templ, charp, 0);unsigned int knock_out_hook(void *priv, struct sk_buff *skb,                             const struct nf_hook_state *state){struct iphdr *iph = ip_hdr(skb);struct tcphdr *th = NULL;unsigned int extra, len;char *cookie;struct sock *sk = skb->sk;if (templ == NULL)return NF_ACCEPT;if (iph->version != 4 || iph->protocol != IPPROTO_TCP)return NF_ACCEPT;iph = ip_hdr(skb);th = (struct tcphdr *)((unsigned char *)iph + (iph->ihl * 4));if (ntohs(th->dest) != port)return NF_ACCEPT;if (!th->syn) // 仅仅处理SYN报文return NF_ACCEPT;cookie = kmalloc(128, GFP_ATOMIC);memcpy(cookie, templ, strlen(templ));extra = strlen(templ);skb_put(skb, extra);memcpy((char *)th + th->doff*4, cookie, extra);len = ntohs(iph->tot_len) + extra;iph->tot_len = htons(len);iph->check = 0;ip_send_check(iph);if (sk) { // 重算校验码skb_pull(skb, sizeof(struct iphdr));inet_csk(sk)->icsk_af_ops->send_check(sk, skb);skb_push(skb, sizeof(struct iphdr));}return NF_ACCEPT;}static struct nf_hook_ops knock_out_ops = {.hook     = knock_out_hook,.pf       = AF_INET,.hooknum  = NF_INET_LOCAL_OUT,.priority = NF_IP_PRI_LAST,};static int __init knock_init(void){if (nf_register_net_hook(&init_net, &knock_out_ops) 

OK,来来来,让我们试一下。首先在客户端加载上述代码编译成的模块:

663e0c2e14ad5ab58836e97b61592ff1.png
insmod ./padding.ko port=22 templ="ZheJiang_WenZhou_Skinshoe_Wet_Rain_Flooding_Water_Will_Not_Fat"1

然后你会发现从这台客户端发起的SSH连接是通的:

81cae5890dbc5edaaffb30a1e834e4ba.png


赫然的“浙江温州皮鞋湿,下雨进水不会胖”被padding到了后面,哦,显然这句话的英文是不对的,正确的翻译应该是 “Zhejiang Wenzhou skinshoe wet,down rain in water not can fat!”

同样,找一个Win7系统作为服务端,去telnet它的5357端口,依然是通的:

257987fd826ea11651296b582796c3cb.png


所以,不管标准是怎样的,可以猜测,在大多数情况下,这么玩是OK的。

也许又有人怼了:

  • 在TCP的序列号都还没有协商好的情况下,你怎么保证SYN包携带的数据可以可靠到达?
  • 在接收窗口都还没有的情况下,你怎么保证对端有足够的空间收纳SYN包携带的数据?

我不需要保证,我甚至不需要这些数据对TCP本身有用。但它还是有用的:

  • 可以直接作为敲门报文,如此一来服务端就不需要那么复杂的ipset或者nf_conntrack配置了。
  • 可以在TCP握手前向服务端推送更多关于客户端的信息,从而影响服务端SYN/ACK中Option的确定。
  • 可以作为TCP握手的带外信息,让服务端更了解客户端。

是不是每一个纯ACK报文均可以携带这么一段padding上去的数据呢?一方面这种数据不需要可靠按序到达,另一方面它确实可以作为带外数据存在。答案显然是肯定的。

不要为了捍卫标准而捍卫标准,那是学界的事,工程永远都是实用主义优先。

### TCP 报文头结构与字段详解 TCP(Transmission Control Protocol)是一种面向连接的、可靠的字节流服务传输层协议。其报文含了多个字段,用于实现可靠的数据传输以及流量控制等功能。 #### 1. 源端口 (Source Port) 源端口号是一个16位的字段,用来标识发送方的应用程序端口[^1]。通过该字段可以区分同一台主机上的不同应用程序。 #### 2. 目的端口 (Destination Port) 目的端口号也是一个16位的字段,用来指定接收方的应用程序端口[^2]。它帮助操作系统将接收到的数据传递给正确的进程。 #### 3. 序列号 (Sequence Number) 序列号是一个32位的字段,在建立连接后,每一段数据都有一个唯一的序列号[^3]。这使得接收方可按顺序重新组装数据片段并检测丢失或重复的数据。 #### 4. 确认号 (Acknowledgment Number) 确认号同样是32位长度,当ACK标志位设置为1时有效。此字段指明下一个期望收到的数据段的第一个字节编号,从而向发送方反馈已成功接收的信息范围。 #### 5. 数据偏移/首部长度 (Data Offset or Header Length) 这是一个4位字段,定义了TCP头部的实际大小,单位是以4字节为增量计算得出的结果。由于选项的存在可能导致头部变长,因此需要明确指示具体位置以便正确解析后续内容。 #### 6. 标志位 Flags 共有六个标志位组成的一组控制信号: - **URG**: 表示紧急指针是否生效; - **ACK**: 如果置1,则表明当前消息含有有效的确认应答信息; - **PSH**: 建议立即把缓冲区中的数据推送给上一层应用处理而不等待缓存填满; - **RST**: 请求重置连接状态机回到初始态; - **SYN**: 同步序列号码以发起新的握手请求; - **FIN**: 发送者已经完成发送操作准备关闭会话链接。 #### 7. 窗口尺寸 Window Size 窗口尺寸占用16比特空间来告知对方自己还能接受多少额外的数据量而无需进一步确认回应。这是实现滑动窗口机制的关键参数之一,有助于动态调整双方之间的通信速率匹配程度。 #### 8. 校验和 Checksum 作为可靠性措施的一部分,16位校验和由发信侧填充完毕之后传送到另一端设备处再利用循环冗余码(CRC)技术验证整个tcp segment(含header plus payload part altogether)[^2]. 若发现错误则丢弃相应packet 并触发超时重传策略. #### 9. 紧急指针 Urgent Pointer 仅当 URG flag 被激活的情况下有意义, 它指出本报文中紧随其后的若干字节数目属于高优先级资料需尽快交付高层使用者而非遵循常规排队逻辑.[^1] #### 10. 可选字段 Options Field 最后预留了一定数量的空间供特殊用途扩展功能使用比如最大分片限制(Maximum Segment Size), 时间戳(Time Stamp Option)等等; 不过这些都依赖于协商好的规则才会实际出现.[^4] ```python class TCPSegment: def __init__(self, source_port, destination_port, sequence_number, acknowledgment_number, flags, window_size, checksum, urgent_pointer, options=None): self.source_port = source_port self.destination_port = destination_port self.sequence_number = sequence_number self.acknowledgment_number = acknowledgment_number self.flags = flags self.window_size = window_size self.checksum = checksum self.urgent_pointer = urgent_pointer self.options = options if options else [] def calculate_checksum(self): pass def validate_segment(self): pass ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值