Scapy在已建立TCP连接发数据包,非Scapy服务端无法接收的问题
为什么Scapy在已建立TCP连接发送的数据包无法被标准TCP服务端recv()获取?
我来帮你拆解这个问题——核心原因出在TCP序列号(seq)和确认号(ack)的错误设置,另外还有Scapy发送原始包与系统TCP栈的冲突问题。咱们一步步梳理:
首先明确你的场景:用Scapy手动完成TCP三次握手后,服务端通过标准socket库的accept()确认了连接,但后续Scapy发送的数据始终无法被服务端的recv()读取,tcpdump能抓到包但服务端就是收不到。
关键问题点分析
1. 代码中seq/ack的致命错误
在三次握手完成后,你发送数据时直接硬编码了seq=123和ack=1:
tcp = ip / TCP(sport=sport, dport=dport, flags="PA", seq=123, ack=1) / "scapy packet 123"
但从tcpdump的连接序列来看:
- 你发送SYN包时
seq=1000,服务端回复SYN-ACK的seq=3017593595,ack=1001(表示期望你下一个包的seq是1001) - 你发送的ACK包用了正确的
seq=1001和ack=3017593596(SYNACK.seq+1),三次握手完成 - 但15秒后发送数据时,你把seq设成123(溢出后变成
4294966419),ack设成1——这两个值完全不符合当前连接的状态!
服务端的TCP栈会维护每个连接的seq/ack状态,它期望收到的seq是1001,但你发的包seq完全不对,所以直接把这个包丢弃了,根本不会传递给用户态的recv()调用。这就是服务端一直卡住的核心原因。
2. 系统TCP栈的潜在干扰
Scapy是直接发送原始数据包,绕过了系统的TCP栈。而你的系统栈并不知道你用Scapy建立了这个连接,当它收到服务端的回复时,可能会发送RST包断开连接(虽然你的tcpdump里没看到,但这是常见的隐藏问题)。
解决方案
1. 修正seq和ack的取值
发送数据时必须使用当前连接的正确seq和ack:
- 客户端下一个要发送的seq = 三次握手ACK包的seq(也就是
SYNACK.ack,这里是1001) - ack = 服务端最后发送的seq + 1(也就是
SYNACK.seq + 1,这里是3017593596)
修改Scapy发送数据的代码部分:
# 保存三次握手后的正确seq和ack值 client_next_seq = SYNACK.ack server_next_seq = SYNACK.seq + 1 time.sleep(15) ip = IP(src=src, dst=dst) # 正确设置seq和ack,flags="PA"表示带数据的ACK包 tcp = ip / TCP(sport=sport, dport=dport, flags="PA", seq=client_next_seq, ack=server_next_seq) / "scapy packet 123" tcp.show2() send(tcp) # 如果后续还要发更多数据,记得更新client_next_seq: # client_next_seq += len("scapy packet 123")
2. 阻止系统TCP栈干扰连接
因为Scapy发送的包绕过了系统栈,系统栈可能会对这个连接发送RST包。你需要在Scapy所在机器上用iptables阻止系统栈处理该连接的流量:
# 精准针对你的连接设置规则,替换成实际的IP和端口 iptables -A OUTPUT -p tcp -s <你的源IP> -d <目标IP> --sport <你的客户端端口> --dport <服务端端口> -j DROP
3. 发送带错误校验和的数据包
如果你的目标是发送校验和错误的包,需要关闭Scapy自动计算校验和的功能:
# 全局关闭自动校验和计算 conf.checkIPaddr = False conf.checkTCPsum = False # 或者手动设置错误的校验和 tcp = ip / TCP(sport=sport, dport=dport, flags="PA", seq=client_next_seq, ack=server_next_seq, chksum=0x1234) / "scapy packet 123"
注意:部分系统或网络设备会直接丢弃校验和错误的包,所以服务端是否能收到取决于系统配置。
内容的提问来源于stack exchange,提问作者fhamad
相关产品推荐
相关产品推荐

