//go:build windows && (amd64 || 386) package tcpkill import ( "b612.me/bcap" "b612.me/bcap/libpcap" "b612.me/starlog" "context" "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" netm "github.com/shirou/gopsutil/v4/net" "net" "os" "os/signal" "strconv" "strings" "sync" "syscall" "time" ) type TCPKill struct { NFQNums int AutoIptables bool BPF string Eth string Host string Pids []int32 SrcIP string SrcPort int DstIP string DstPort int KillType string RstNumbers int WaitMode bool Status string matchConns sync.Map matchCount uint sync.Mutex cache []string cap *bcap.Packets ctx context.Context stopFn context.CancelFunc requests chan *handler pc *libpcap.NetCatch macCache map[string]net.HardwareAddr } type handler struct { } func (t *TCPKill) presetWindows() { t.macCache = make(map[string]net.HardwareAddr) if t.BPF == "" { if t.DstIP != "" { t.BPF = fmt.Sprintf("tcp and host %s", t.DstIP) } else if t.SrcIP != "" { t.BPF = fmt.Sprintf("tcp and host %s", t.SrcIP) } else if t.SrcPort != 0 { t.BPF = fmt.Sprintf("tcp and port %d", t.SrcPort) } else if t.DstPort != 0 { t.BPF = fmt.Sprintf("tcp and port %d", t.DstPort) } else { t.BPF = "tcp" } } if t.Eth == "" && t.Host == "" { allDevice, err := pcap.FindAllDevs() if err != nil { starlog.Errorf("get pcap devices failed:%v\n", err) return } if t.SrcIP == "" { defer func() { t.SrcIP = "" }() ip := t.DstIP if ip == "" { ip = "223.6.6.6" } if strings.Contains(ip, ":") { ip = "[" + ip + "]" } l, err := net.Dial("udp", ip+":53") if err == nil { t.SrcIP = l.LocalAddr().(*net.UDPAddr).IP.String() starlog.Infof("No eth or ip detected,use %s as source ip\n", t.SrcIP) l.Close() } else { starlog.Errorf("Failed to get source ip:%v\n", err) } } for _, v := range allDevice { if t.SrcIP == "127.0.0.1" && v.Name == "\\Device\\NPF_Loopback" { t.Eth = v.Name return } for _, addr := range v.Addresses { if addr.IP.String() == t.SrcIP { t.Eth = v.Name return } } } } } func (t *TCPKill) Run() error { var err error if err = t.PreRun(); err != nil { return err } t.presetWindows() stopSignal := make(chan os.Signal) starlog.Noticef("Starting capture\n") signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) if t.Eth != "" { t.pc, err = libpcap.NewCatchEth(t.Eth, t.BPF) if err != nil { starlog.Errorf("Failed to create pcap handle:%v\n", err) return err } } if t.Host != "" { t.pc, err = libpcap.NewCatch(t.Host, t.BPF) if err != nil { starlog.Errorf("Failed to create pcap handle:%v\n", err) return err } } if t.pc == nil { starlog.Errorf("No pcap handle\n") return fmt.Errorf("No pcap handle") } t.pc.SetRecall(t.handlePacket) go func() { if err := t.pc.Run(); err != nil { starlog.Errorln(err) stopSignal <- syscall.SIGKILL } }() go t.SynSent() select { //todo need nf.Stop() case <-stopSignal: starlog.Warningf("Received stop signal\n") case <-t.ctx.Done(): starlog.Infoln("TCPKILL Task Finished") } return nil } func (t *TCPKill) SynSent() { for { time.Sleep(time.Millisecond * 2500) realConns, err := netm.Connections("tcp") if err != nil { continue } t.matchConns.Range(func(key, value any) bool { t.matchConns.Delete(key) t.matchCount-- return true }) for _, conn := range realConns { if t.Match(conn) { t.matchConns.Store(key(conn), conn) t.matchCount++ } } if t.matchCount == 0 && !t.WaitMode { starlog.Warningln("No matched connection anymore") t.stopFn() return } t.matchConns.Range(func(k, v any) bool { conn := v.(netm.ConnectionStat) if t.macCache[conn.Laddr.IP] == nil || t.macCache[conn.Raddr.IP] == nil { target := conn.Raddr.IP + ":" + strconv.Itoa(int(conn.Raddr.Port)) if strings.Contains(conn.Raddr.IP, ":") { target = "[" + conn.Raddr.IP + "]:" + strconv.Itoa(int(conn.Raddr.Port)) } starlog.Warningf("No mac address for %v or %v,try send syn to %v\n", conn.Laddr.IP, conn.Raddr.IP, target) tcpConn, err := net.DialTimeout("tcp", target, time.Millisecond*800) if err == nil { tcpConn.(*net.TCPConn).SetLinger(0) tcpConn.Close() } time.Sleep(time.Millisecond * 600) } t.Lock() if err := SendSYN(t.pc.Handle, t.macCache[conn.Laddr.IP], t.macCache[conn.Raddr.IP], conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), 1127); err != nil { starlog.Errorf("Failed to send SYN:%v\n", err) } if err := SendSYN(t.pc.Handle, t.macCache[conn.Raddr.IP], t.macCache[conn.Laddr.IP], conn.Raddr.IP, strconv.Itoa(int(conn.Raddr.Port)), conn.Laddr.IP, strconv.Itoa(int(conn.Laddr.Port)), 1127); err != nil { starlog.Errorf("Failed to send SYN:%v\n", err) } t.Unlock() starlog.Infof("Send SYN to %v <==> %v\n", conn.Laddr.String(), conn.Raddr.String()) return true }) } } func (t *TCPKill) handlePacket(p gopacket.Packet) { t.Lock() info, err := t.cap.ParsePacket(p) t.Unlock() if err != nil { return } tcpLayer := p.Layer(layers.LayerTypeTCP) if tcpLayer == nil { return } tcp := tcpLayer.(*layers.TCP) if tcp.SYN && !tcp.ACK { return } t.Lock() //fmt.Println(info.SrcIP, hex.EncodeToString(info.SrcMac), info.DstIP, hex.EncodeToString(info.DstMac)) t.macCache[info.SrcIP] = info.SrcMac t.macCache[info.DstIP] = info.DstMac t.Unlock() if tcp.RST { starlog.Warningf("RST packet:%v <==> %v\n", info.SrcIP+":"+info.SrcPort, info.DstIP+":"+info.DstPort) return } //starlog.Debugf("packet:%v <==> %v\n", info.SrcIP+":"+info.SrcPort, info.DstIP+":"+info.DstPort) if !t.Match(netm.ConnectionStat{ Status: "PCAP", Laddr: netm.Addr{ IP: info.SrcIP, Port: uint32(tcp.SrcPort), }, Raddr: netm.Addr{ IP: info.DstIP, Port: uint32(tcp.DstPort), }, }) && !t.Match(netm.ConnectionStat{ Status: "PCAP", Raddr: netm.Addr{ IP: info.SrcIP, Port: uint32(tcp.SrcPort), }, Laddr: netm.Addr{ IP: info.DstIP, Port: uint32(tcp.DstPort), }, }) { return } starlog.Noticef("Sending RST... %v:%v <==> %v:%v SEQ=%d ACK=%d\n", info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq(), info.TcpAck()) RealSendRST(t.pc.Handle, info, t.KillType, t.RstNumbers) } func RealSendRST(p *pcap.Handle, info bcap.PacketInfo, target string, number int) { for i := 0; i < number; i++ { if target == "both" || target == "target" { seq := uint32(info.TcpPayloads()) + info.TcpSeq() + (uint32(i) * uint32(info.TcpWindow())) SendRST(p, info.SrcMac, info.DstMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, seq) //SendRST(p, info.DstMac, info.SrcMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow()))) } if target == "both" || target == "reverse" { SendRST(p, info.DstMac, info.SrcMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) //SendRST(p, info.SrcMac, info.DstMac, info.DstIP, info.DstPort, info.SrcIP, info.SrcPort, info.TcpAck()+(uint32(i)*uint32(info.TcpWindow()))) } } } func SendRST(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { if net.ParseIP(dstIP).To4() != nil { return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) } return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, false) } func SendSYN(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32) error { if net.ParseIP(dstIP).To4() != nil { return sendIPv4(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) } return sendIPv6(p, srcMac, dstMac, srcIP, srcPort, dstIP, dstPort, seq, true) } func sendIPv4(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { dstNetIP := net.ParseIP(dstIP) eth := layers.Ethernet{ SrcMAC: srcMac, DstMAC: dstMac, EthernetType: layers.EthernetTypeIPv4, } iPv4 := layers.IPv4{ SrcIP: net.ParseIP(srcIP), DstIP: dstNetIP, Version: 4, TTL: 64, Protocol: layers.IPProtocolTCP, } sPort, err := strconv.Atoi(srcPort) if err != nil { return err } dPort, err := strconv.Atoi(dstPort) if err != nil { return err } tcp := layers.TCP{ SrcPort: layers.TCPPort(sPort), DstPort: layers.TCPPort(dPort), Seq: seq, RST: !isSyn, SYN: isSyn, } if err = tcp.SetNetworkLayerForChecksum(&iPv4); err != nil { return err } buffer := gopacket.NewSerializeBuffer() options := gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, } if srcMac == nil && dstMac == nil { if err = gopacket.SerializeLayers(buffer, options, &iPv4, &tcp); err != nil { return err } } else { if err = gopacket.SerializeLayers(buffer, options, ð, &iPv4, &tcp); err != nil { return err } } return p.WritePacketData(buffer.Bytes()) } func sendIPv6(p *pcap.Handle, srcMac, dstMac []byte, srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) error { dstNetIP := net.ParseIP(dstIP) eth := layers.Ethernet{ SrcMAC: srcMac, DstMAC: dstMac, EthernetType: layers.EthernetTypeIPv6, } iPv6 := layers.IPv6{ SrcIP: net.ParseIP(srcIP), DstIP: dstNetIP, Version: 6, NextHeader: layers.IPProtocolTCP, HopLimit: 64, } sPort, err := strconv.Atoi(srcPort) if err != nil { return err } dPort, err := strconv.Atoi(dstPort) if err != nil { return err } tcp := layers.TCP{ SrcPort: layers.TCPPort(sPort), DstPort: layers.TCPPort(dPort), Seq: seq, RST: !isSyn, SYN: isSyn, } if err := tcp.SetNetworkLayerForChecksum(&iPv6); err != nil { return err } buffer := gopacket.NewSerializeBuffer() options := gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, } if srcMac == nil && dstMac == nil { if err = gopacket.SerializeLayers(buffer, options, &iPv6, &tcp); err != nil { return err } } else { if err = gopacket.SerializeLayers(buffer, options, ð, &iPv6, &tcp); err != nil { return err } } return p.WritePacketData(buffer.Bytes()) }