You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
10 KiB
Go
391 lines
10 KiB
Go
4 days ago
|
//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
|
||
|
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())
|
||
|
}
|