You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Go单二进制中拦截修改随机端口连接的2106端口数据包?

可行方案:单Go二进制实现2106端口数据包拦截与修改

完全可以在单个Go二进制文件里实现需求,不需要依赖iptables。核心是用gopacket库同时完成数据包捕获、修改与发送,不过需要处理TCP协议的状态细节,且程序需要root权限运行(原始网络操作需要特权)。

核心实现思路

  • 捕获目标数据包:保留你现有代码中基于pcap的2106端口过滤逻辑
  • 解析与修改数据包:解析出IP、TCP层,修改payload内容
  • 重构合法数据包:重新计算IP/TCP头部的校验和,调整TCP序列号/确认号以匹配连接状态
  • 发送修改后的数据包:通过原始套接字将新包发送到网络
  • (可选)拦截原数据包:如果不希望原包到达目标,可以结合pcap的SetDirection或操作系统的netfilter,纯Go实现也可通过抢占发送覆盖(复杂场景建议用ebpf或netfilter保证可靠性)

修改后的示例代码

基于你现有的捕获代码,添加解析、修改、发送逻辑:

package main

import (
	"fmt"
	"log"
	"sync"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

// 维护TCP连接状态,记录序列号偏移(简化版,实际需双向跟踪)
var connState = struct {
	sync.Mutex
	entries map[string]int64 // key: src:dst:sport:dport, value: seq偏移量
}{
	entries: make(map[string]int64),
}

func captureAndModifyPackets(device string, wg *sync.WaitGroup) {
	defer wg.Done()

	// 打开捕获句柄,同时支持发送
	handle, err := pcap.OpenLive(device, 65536, true, pcap.BlockForever)
	if err != nil {
		log.Printf("打开接口%s失败: %v", device, err)
		return
	}
	defer handle.Close()

	// 设置过滤规则:2106端口的TCP包
	filter := "tcp and (dst port 2106 or src port 2106)"
	if err := handle.SetBPFFilter(filter); err != nil {
		log.Printf("设置过滤器失败: %v", err)
		return
	}

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		// 解析IP和TCP层
		ipLayer := packet.Layer(layers.LayerTypeIPv4)
		tcpLayer := packet.Layer(layers.LayerTypeTCP)
		if ipLayer == nil || tcpLayer == nil {
			continue
		}

		ip, _ := ipLayer.(*layers.IPv4)
		tcp, _ := tcpLayer.(*layers.TCP)

		// 构造连接唯一标识
		connKey := fmt.Sprintf("%s:%d->%s:%d", ip.SrcIP, tcp.SrcPort, ip.DstIP, tcp.DstPort)

		// 示例:修改payload内容(将原内容替换为"modified_content")
		newPayload := []byte("modified_content")
		// 计算payload长度变化,生成序列号偏移
		offset := int64(len(newPayload) - len(tcp.Payload))

		// 更新连接状态的序列号偏移
		connState.Lock()
		currentOffset, exists := connState.entries[connKey]
		if !exists {
			currentOffset = 0
		}
		connState.entries[connKey] = currentOffset + offset
		connState.Unlock()

		// 构造新的TCP层
		newTCP := *tcp
		newTCP.Payload = newPayload
		// 关联IP层以重新计算TCP校验和
		newTCP.SetNetworkLayerForChecksum(ip)

		// 调整序列号和确认号(基于偏移量)
		newTCP.Seq += uint32(currentOffset)
		if newTCP.Ack {
			reverseConnKey := fmt.Sprintf("%s:%d->%s:%d", ip.DstIP, tcp.DstPort, ip.SrcIP, tcp.SrcPort)
			connState.Lock()
			reverseOffset := connState.entries[reverseConnKey]
			connState.Unlock()
			newTCP.Ack += uint32(reverseOffset)
		}

		// 序列化新数据包
		buffer := gopacket.NewSerializeBuffer()
		opts := gopacket.SerializeOptions{
			FixLengths:       true,
			ComputeChecksums: true,
		}
		if err := gopacket.SerializeLayers(buffer, opts,
			packet.Layer(layers.LayerTypeEthernet),
			ip,
			&newTCP,
			gopacket.Payload(newPayload),
		); err != nil {
			log.Printf("序列化数据包失败: %v", err)
			continue
		}

		// 发送修改后的数据包
		if err := handle.WritePacketData(buffer.Bytes()); err != nil {
			log.Printf("发送数据包失败: %v", err)
		}

		fmt.Printf("已修改并发送数据包: 原Payload长度%d -> 新长度%d\n", len(tcp.Payload), len(newPayload))
	}
}

func main() {
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err)
	}

	var wg sync.WaitGroup

	for _, device := range devices {
		wg.Add(1)
		go captureAndModifyPackets(device.Name, &wg)
	}

	wg.Wait()
}

关键注意点

  • Root权限:必须用sudo运行程序,原始数据包捕获和发送需要CAP_NET_RAW系统权限
  • TCP状态跟踪:示例中的连接状态是简化版,生产环境需要完善状态管理(比如处理FIN/RST包、超时清理无效连接)
  • 校验和与序列号:修改payload后必须重新计算校验和,同时调整TCP序列号/确认号,否则目标端会判定数据包非法并丢弃
  • 性能与可靠性:相比iptables+用户态代理的方案,原始数据包操作对TCP协议细节要求更高,适合特定场景;通用代理需求建议用iptables+Go代理的组合,稳定性更强

内容的提问来源于stack exchange,提问作者Ming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:25:20