Python使用raw socket构造TCP SYN包发送时报WinError 10022错误
问题现象
使用Raw Socket构造TCP SYN数据包时,执行发送操作触发如下错误:
报错位置:
server.sendto(packet, (dst_ip, 0))
错误信息:OSError: [WinError 10022] An invalid argument was supplied(提供了无效参数)
错误原因
- Windows平台对
AF_INET类型、SOCK_RAW类型、IPPROTO_TCP协议的原始套接字有强制约束:必须手动构造完整的IP头+TCP头,且提前通过setsockopt开启IP_HDRINCL选项告知系统用户自行填充IP头,否则系统直接判定传入参数无效。原代码未开启该选项,且仅构造了TCP头、缺失IP头。 - TCP头数据偏移字段(
doff)取值错误:该字段单位为4字节,标准20字节TCP头对应的取值为5(5*4=20字节),原代码赋值为80,完全不符合协议规范,导致头结构解析异常。 - 发送时目标端口参数错误:原代码
sendto传入的目标端口为0,属于无效端口值,目标端口应为构造TCP头时指定的80端口。 - 校验和计算逻辑错误:原代码构造TCP校验和伪头部时使用的结构体格式串与字段定义不匹配,计算出的校验和为无效值,即使绕过参数校验,数据包也会被协议栈直接丢弃。
- 运行权限不足:Windows下原始套接字操作需要管理员权限,普通权限启动Python进程也会触发参数类报错。
修正后代码
import socket import struct def checksum_calculator(data): checksum = 0 data_len = len(data) index = 0 # 按2字节为单位累加计算 while data_len > 1: checksum += (data[index] << 8) + data[index + 1] checksum = (checksum & 0xffff) + (checksum >> 16) index += 2 data_len -= 2 # 处理奇数长度剩余字节 if data_len: checksum += data[index] << 8 checksum = (checksum & 0xffff) + (checksum >> 16) return (~checksum) & 0xffff # 创建原始TCP套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP) # 关键配置:开启IP头自定义选项,Windows平台必须设置 sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # 基础地址配置 src_ip = socket.gethostbyname(socket.gethostname()) dst_ip = socket.gethostbyname('www.google.com') # IP头字段配置 ip_version = 4 ip_ihl = 5 # IP头长度,单位4字节,标准20字节头对应值5 ip_tos = 0 ip_total_length = 40 # 20字节IP头 + 20字节TCP头 ip_id = 54321 ip_frag_offset = 0 ip_ttl = 64 ip_protocol = socket.IPPROTO_TCP ip_checksum = 0 # 先填0,计算完成后回填 # TCP头字段配置 src_port = 4790 dst_port = 80 tcp_seq = 0 tcp_ack_seq = 0 tcp_doff = 5 # TCP头长度,单位4字节,标准20字节头对应值5 # TCP标志位 tcp_fin = 0 tcp_syn = 1 tcp_rst = 0 tcp_psh = 0 tcp_ack = 0 tcp_urg = 0 tcp_window = socket.htons(5840) tcp_checksum = 0 tcp_urg_ptr = 0 # 组装临时IP头计算校验和 ip_ihl_ver = (ip_version << 4) + ip_ihl tmp_ip_header = struct.pack( '!BBHHHBBH4s4s', ip_ihl_ver, ip_tos, ip_total_length, ip_id, ip_frag_offset, ip_ttl, ip_protocol, ip_checksum, socket.inet_aton(src_ip), socket.inet_aton(dst_ip) ) ip_checksum = checksum_calculator(tmp_ip_header) # 回填正确校验和,生成最终IP头 ip_header = struct.pack( '!BBHHHBBH4s4s', ip_ihl_ver, ip_tos, ip_total_length, ip_id, ip_frag_offset, ip_ttl, ip_protocol, ip_checksum, socket.inet_aton(src_ip), socket.inet_aton(dst_ip) ) # 构造TCP伪头部用于计算TCP校验和 tcp_placeholder = 0 tcp_total_length = tcp_doff * 4 tcp_pseudo_header = struct.pack( '!4s4sBBH', socket.inet_aton(src_ip), socket.inet_aton(dst_ip), tcp_placeholder, ip_protocol, tcp_total_length ) tcp_flags = tcp_fin + (tcp_syn << 1) + (tcp_rst << 2) + (tcp_psh << 3) + (tcp_ack << 4) + (tcp_urg << 5) # 组装临时TCP头计算校验和 tmp_tcp_header = struct.pack( '!HHLLBBHHH', src_port, dst_port, tcp_seq, tcp_ack_seq, (tcp_doff << 4), # 数据偏移存放在高4位,低4位为保留位填0 tcp_flags, tcp_window, tcp_checksum, tcp_urg_ptr ) tcp_checksum = checksum_calculator(tcp_pseudo_header + tmp_tcp_header) # 回填正确校验和,生成最终TCP头 tcp_header = struct.pack( '!HHLLBBHHH', src_port, dst_port, tcp_seq, tcp_ack_seq, (tcp_doff << 4), tcp_flags, tcp_window, tcp_checksum, tcp_urg_ptr ) # 构造完整IP数据包:IP头 + TCP头 full_packet = ip_header + tcp_header # 发送数据包,目标端口填写实际的80端口 sock.sendto(full_packet, (dst_ip, dst_port))
运行注意事项
- 必须以管理员权限启动命令行/Python进程,否则原始套接字调用会被系统拦截。
- 本地防火墙、安全软件可能拦截自定义原始数据包,测试时可临时添加对应放行规则。
- 若处于NAT网络环境(如家用路由器共享上网、公司内网),自定义构造的TCP SYN包可能被网关、运营商设备丢弃,无法收到目标响应属于正常网络限制。
内容的提问来源于stack exchange,提问作者Vinura Yashohara
相关产品推荐
相关产品推荐

