如何用Golang发送指定TCP数据包?Wireshark包转字节数组问题
解决方法
你捕获的是包含**以太网帧头、IP头、TCP头(带SYN标志)**的完整链路层数据包,不能通过net.Dial创建的TCP连接发送——因为这种连接是系统已经完成三次握手的传输层连接,会自动处理下层协议封装。要发送原始数据包,需要使用raw socket,并先把捕获的十六进制数据转换成字节数组。
步骤1:提取并转换十六进制数据
先把你提供的十六进制内容清理掉注释、地址前缀和空格,得到纯十六进制字符串,再用Go的encoding/hex包转成字节数组,最终实现发送:
package main import ( "encoding/hex" "fmt" "net" "syscall" ) func main() { // 清理后的纯十六进制字符串(移除注释、地址标识和空格) hexStr := "5c647adfa951c87f5404ca0908004500003448494000800604a4c0a80068c0632c63f70b083aa055b59c000000008002faf071100000020405b40103030801010402" // 将十六进制字符串转换为字节数组 packetBytes, err := hex.DecodeString(hexStr) if err != nil { fmt.Println("十六进制转字节数组失败:", err) return } // 创建raw socket,用于发送链路层原始数据包 fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, syscall.HTONS(syscall.ETH_P_ALL)) if err != nil { fmt.Println("创建raw socket失败:", err) return } defer syscall.Close(fd) // 指定发送数据包的网卡(替换为你机器的实际网卡名,比如eth0、en0) iface, err := net.InterfaceByName("en0") if err != nil { fmt.Println("获取网卡信息失败:", err) return } // 构造链路层地址结构体,绑定网卡和协议 addr := syscall.SockaddrLinklayer{ Ifindex: iface.Index, Protocol: syscall.HTONS(syscall.ETH_P_ALL), } // 发送原始数据包 err = syscall.Sendto(fd, packetBytes, 0, &addr) if err != nil { fmt.Println("发送数据包失败:", err) return } fmt.Println("数据包发送成功") }
关键注意事项
- 运行代码需要root/管理员权限,因为raw socket属于系统特权资源。
- 必须替换代码中的网卡名称(如
en0)为你当前机器的实际网卡名,可通过ifconfig或ip addr命令查看。 - 捕获的数据包中的源MAC、源IP是你当时抓包时的机器地址,如果换了设备发送,需要修改十六进制字符串中对应字段:
- 以太网帧头前6字节是目标MAC,接下来6字节是源MAC
- IP头的第13-16字节是源IP,17-20字节是目标IP
对你原有代码的说明
你用net.Dial创建的是已建立的TCP连接,系统已经自动完成了TCP三次握手(包括发送SYN包),这种连接只能用于发送应用层数据,无法手动发送TCP握手阶段的原始SYN包。如果要模拟TCP连接建立过程,必须使用raw socket直接操作底层数据包。
内容的提问来源于stack exchange,提问作者Ming
相关产品推荐
相关产品推荐

