597 lines
15 KiB
Go
597 lines
15 KiB
Go
//go:build windows && !arm64
|
||
|
||
package tcm
|
||
|
||
import (
|
||
"b612.me/bcap"
|
||
"b612.me/bcap/libpcap"
|
||
"b612.me/starlog"
|
||
"b612.me/starmap"
|
||
"context"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"github.com/google/gopacket"
|
||
"github.com/google/gopacket/layers"
|
||
"github.com/google/gopacket/pcap"
|
||
"github.com/google/gopacket/pcapgo"
|
||
"net"
|
||
"os"
|
||
"os/signal"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"syscall"
|
||
"time"
|
||
)
|
||
|
||
type Libpcap struct {
|
||
count uint64
|
||
// 按连接数最后一个历史报文
|
||
cap *bcap.Packets
|
||
// 监控目标ip地址列表
|
||
target []string
|
||
// 将军,下达命令吧!
|
||
// 监控命令列表
|
||
targetCmd []string
|
||
targetAsHex bool
|
||
//保存为pcap格式的数据文件
|
||
saveFile string
|
||
// 写缓存
|
||
packetCache chan gopacket.Packet
|
||
//交互模式
|
||
interactive bool
|
||
//展示所有报文信息,包括未在追踪列表的
|
||
showAll bool
|
||
//展示payload报文(utf-8直接输出)
|
||
showPayload bool
|
||
//payload展示为hex
|
||
showAsHex bool
|
||
//payload允许展示的最大字符
|
||
maxShowPayloadSize int
|
||
//不展示任何信息
|
||
noShowMode bool
|
||
// 触发封禁词后,使用RST重置链路
|
||
useRST bool
|
||
// RST模式,target=目标端单向RST,reverse=对端反向RST,both=双向RST
|
||
rstMode string
|
||
printColor []*starlog.Color
|
||
logCache chan loged
|
||
ctx context.Context
|
||
fn context.CancelFunc
|
||
bpf string
|
||
eth string
|
||
host string
|
||
handle *libpcap.NetCatch
|
||
blockMap sync.Map
|
||
CapFileCacheNum int
|
||
Flags []string
|
||
}
|
||
|
||
type loged struct {
|
||
str string
|
||
stateLevel int
|
||
logLevel string
|
||
}
|
||
|
||
func (t *Libpcap) Run() error {
|
||
var err error
|
||
stopSignal := make(chan os.Signal)
|
||
starlog.Noticef("Starting libpcap capture\n")
|
||
if t.bpf == "" {
|
||
starlog.Errorln("请输入一个抓包模式")
|
||
os.Exit(3)
|
||
}
|
||
if t.host == "" && t.eth == "" {
|
||
starlog.Errorln("请输入eth网卡名或host名")
|
||
ifs, err := libpcap.FindAllDevs()
|
||
if err == nil {
|
||
fmt.Println("网卡名如下:\n----------\n")
|
||
for k, v := range ifs {
|
||
var ips []string
|
||
for _, vv := range v.Addresses {
|
||
ips = append(ips, vv.IP.String())
|
||
}
|
||
fmt.Printf("%d.\t%v\t%s\t%v\n", k+1, v.Name, strings.Join(ips, " , "), v.Description)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
return fmt.Errorf("请输入eth网卡名或host名")
|
||
}
|
||
if t.host == "" {
|
||
t.handle, err = libpcap.NewCatchEth(t.eth, t.bpf)
|
||
} else {
|
||
t.handle, err = libpcap.NewCatch(t.host, t.bpf)
|
||
}
|
||
if err != nil {
|
||
starlog.Errorln("failed to listen:", err)
|
||
return err
|
||
}
|
||
t.handle.SetRecall(t.handlePacket)
|
||
if t.targetAsHex {
|
||
for k, v := range t.targetCmd {
|
||
tmp, _ := hex.DecodeString(v)
|
||
t.targetCmd[k] = string(tmp)
|
||
}
|
||
}
|
||
go func() {
|
||
if err = t.handle.Run(); err != nil {
|
||
starlog.Errorln(err)
|
||
stopSignal <- syscall.SIGKILL
|
||
}
|
||
}()
|
||
if t.saveFile != "" {
|
||
f, err := os.Create(t.saveFile)
|
||
if err != nil {
|
||
starlog.Errorln("创建写入文件失败", err)
|
||
os.Exit(4)
|
||
}
|
||
defer f.Close()
|
||
go t.pcapWriter(t.ctx, f)
|
||
}
|
||
signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||
go t.logPrint(t.ctx)
|
||
select {
|
||
//todo need nf.Stop()
|
||
case <-stopSignal:
|
||
starlog.Warningf("Received stop signal\n")
|
||
|
||
case <-t.ctx.Done():
|
||
starlog.Infoln("TCPMonitor Task Finished")
|
||
|
||
}
|
||
return nil
|
||
|
||
}
|
||
|
||
func NewLibpcap() *Libpcap {
|
||
var nf = new(Libpcap)
|
||
nf.packetCache = make(chan gopacket.Packet, 2048)
|
||
nf.logCache = make(chan loged, 2048)
|
||
nf.printColor = []*starlog.Color{
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgHiCyan), //1=tcp_connect_1,
|
||
starlog.NewColor(starlog.FgHiCyan), //2=tcp_connect_2,
|
||
starlog.NewColor(starlog.FgHiCyan), //3=tcp_connect_3,
|
||
starlog.NewColor(starlog.FgCyan), //4=tcp_bye_bye
|
||
starlog.NewColor(starlog.FgCyan), //5=tcp_bye_bye
|
||
starlog.NewColor(starlog.FgCyan), //6=tcp_bye_bye
|
||
starlog.NewColor(starlog.FgCyan), //7=tcp_bye_bye
|
||
starlog.NewColor(starlog.FgCyan), //8=tcp_bye_bye
|
||
starlog.NewColor(starlog.FgGreen), //9=tcp_ok
|
||
starlog.NewColor(starlog.BgRed, starlog.FgYellow), //10=tcp_retrans
|
||
starlog.NewColor(starlog.FgHiMagenta), //ece
|
||
starlog.NewColor(starlog.FgHiMagenta), //cwr
|
||
starlog.NewColor(starlog.FgRed), //rst
|
||
starlog.NewColor(starlog.FgHiGreen), //keepalive
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
starlog.NewColor(starlog.FgWhite), //20=udp
|
||
starlog.NewColor(starlog.FgWhite), //0=unknown
|
||
}
|
||
nf.cap = bcap.NewPackets()
|
||
nf.ctx, nf.fn = context.WithCancel(context.Background())
|
||
return nf
|
||
}
|
||
|
||
func (n *Libpcap) logPrint(ctx context.Context) {
|
||
for {
|
||
select {
|
||
case <-ctx.Done():
|
||
return
|
||
case l := <-n.logCache:
|
||
if n.noShowMode {
|
||
fmt.Printf("已捕获报文数量:%v个\r", n.count)
|
||
continue
|
||
}
|
||
switch l.logLevel {
|
||
case "info":
|
||
starlog.Info(l.str)
|
||
case "notice":
|
||
starlog.Notice(l.str)
|
||
case "warning":
|
||
starlog.Warning(l.str)
|
||
case "error":
|
||
starlog.Error(l.str)
|
||
case "critical":
|
||
starlog.Critical(l.str)
|
||
case "debug":
|
||
starlog.Debug(l.str)
|
||
case "payload":
|
||
fmt.Println(l.str)
|
||
default:
|
||
n.printColor[l.stateLevel].Print(l.str)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func (n *Libpcap) ipInRange(ip string) bool {
|
||
if len(n.target) == 0 {
|
||
return false
|
||
}
|
||
for _, v := range n.target {
|
||
if v == ip {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (n *Libpcap) strInRange(str string) bool {
|
||
if len(n.targetCmd) == 0 {
|
||
return false
|
||
}
|
||
for _, v := range n.targetCmd {
|
||
if v == "" {
|
||
continue
|
||
}
|
||
if strings.Contains(str, v) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (n *Libpcap) handlePacket(p gopacket.Packet) {
|
||
n.count++
|
||
if n.saveFile != "" {
|
||
n.packetCache <- p
|
||
}
|
||
var layer gopacket.Layer
|
||
for _, layerType := range []gopacket.LayerType{
|
||
layers.LayerTypeTCP, layers.LayerTypeUDP, layers.LayerTypeICMPv4, layers.LayerTypeICMPv6,
|
||
layers.LayerTypeIPv4, layers.LayerTypeIPv6, layers.LayerTypeARP,
|
||
} {
|
||
layer = p.Layer(layerType)
|
||
if layer == nil {
|
||
continue
|
||
}
|
||
break
|
||
}
|
||
if layer == nil {
|
||
n.logCache <- loged{
|
||
str: "无法定义layer类型\n",
|
||
stateLevel: 0,
|
||
logLevel: "error",
|
||
}
|
||
return
|
||
}
|
||
info, err := n.cap.ParsePacket(p)
|
||
if err != nil {
|
||
starlog.Errorln(err)
|
||
return
|
||
}
|
||
var dec string
|
||
switch info.StateDescript() {
|
||
case 0:
|
||
dec = "未知状态"
|
||
case 1:
|
||
dec = "SYN tcp建立一次握手"
|
||
case 2:
|
||
dec = "SYN,ACK tcp建立二次握手"
|
||
case 3:
|
||
dec = "ACK tcp建立三次握手"
|
||
case 4:
|
||
dec = "FIN tcp断开一次挥手"
|
||
case 5:
|
||
dec = "ACK tcp断开二次挥手"
|
||
case 6:
|
||
dec = "FIN,ACK tcp断开二次三次挥手"
|
||
case 7:
|
||
dec = "FIN tcp断开三次挥手"
|
||
case 8:
|
||
dec = "ACK tcp断开四次挥手"
|
||
case 9:
|
||
dec = "tcp报文"
|
||
case 10:
|
||
dec = "TCP重传"
|
||
case 11:
|
||
dec = "TCP ece"
|
||
case 12:
|
||
dec = "TCP cwr"
|
||
case 13:
|
||
dec = "TCP RST重置"
|
||
case 14:
|
||
dec = "TCP Keepalive"
|
||
}
|
||
if !n.showAll && !n.ipInRange(info.SrcIP) && !n.ipInRange(info.DstIP) {
|
||
return
|
||
}
|
||
if len(n.Flags) > 0 && !n.writerMatch(p, true) {
|
||
return
|
||
}
|
||
n.logCache <- loged{
|
||
str: fmt.Sprintf("%s %v:%v -> %v:%v %s seq=%v ack=%v win=%v len=%v\n", time.Now().Format("2006-01-02 15:04:05.000000"), info.SrcIP, info.SrcPort,
|
||
info.DstIP, info.DstPort, dec, info.TcpSeq(), info.TcpAck(), info.TcpWindow(), info.TcpPayloads()),
|
||
stateLevel: int(info.StateDescript()),
|
||
logLevel: "",
|
||
}
|
||
|
||
if n.showPayload {
|
||
str := string(layer.LayerPayload())
|
||
if n.maxShowPayloadSize > 0 {
|
||
if len(str) > n.maxShowPayloadSize {
|
||
str = str[:n.maxShowPayloadSize]
|
||
}
|
||
}
|
||
if n.showAsHex {
|
||
str = hex.EncodeToString([]byte(str))
|
||
}
|
||
n.logCache <- loged{
|
||
str: str,
|
||
stateLevel: int(info.StateDescript()),
|
||
logLevel: "payload",
|
||
}
|
||
}
|
||
if n.ipInRange(info.SrcIP) || n.ipInRange(info.DstIP) {
|
||
_, ok := n.blockMap.Load(info.Key)
|
||
if n.useRST && (ok || n.strInRange(string(layer.LayerPayload()))) {
|
||
n.blockMap.Store(info.Key, true)
|
||
starlog.Warningf("触发封禁关键词 RST重置\n")
|
||
RealSendRST(n.handle.Handle, info, n.rstMode, 3)
|
||
}
|
||
}
|
||
}
|
||
|
||
func RealSendRST(p *pcap.Handle, info bcap.PacketInfo, target string, number int) {
|
||
for i := 0; i < number; i++ {
|
||
if target == "both" || target == "target" {
|
||
SendRST(p, info.SrcMac, info.DstMac, info.SrcIP, info.SrcPort, info.DstIP, info.DstPort, info.TcpSeq()+(uint32(i)*uint32(info.TcpWindow())))
|
||
//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 (n *Libpcap) pcapWriter(stopCtx context.Context, fp *os.File) error {
|
||
w := pcapgo.NewWriter(fp)
|
||
err := w.WriteFileHeader(65535, layers.LinkTypeEthernet)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
buf := starmap.NewStarChanStack(uint64(n.CapFileCacheNum))
|
||
avail := 0
|
||
for {
|
||
select {
|
||
case <-stopCtx.Done():
|
||
return nil
|
||
case p := <-n.packetCache:
|
||
if n.CapFileCacheNum == 0 {
|
||
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
||
continue
|
||
}
|
||
if avail > 0 {
|
||
avail--
|
||
if n.writerMatch(p, false) {
|
||
avail = n.CapFileCacheNum
|
||
}
|
||
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
||
continue
|
||
}
|
||
if buf.Free() == 0 {
|
||
buf.Pop()
|
||
}
|
||
if !n.writerMatch(p, false) {
|
||
buf.Push(p)
|
||
continue
|
||
}
|
||
for buf.Len() > 0 {
|
||
hp, err := buf.Pop()
|
||
if err == nil {
|
||
w.WritePacket(hp.(gopacket.Packet).Metadata().CaptureInfo, hp.(gopacket.Packet).Data())
|
||
}
|
||
}
|
||
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
||
avail = n.CapFileCacheNum
|
||
}
|
||
}
|
||
}
|
||
|
||
func (n *Libpcap) writerMatch(pkt gopacket.Packet, onlyFlags bool) bool {
|
||
tcpLayer := pkt.Layer(layers.LayerTypeTCP)
|
||
if !onlyFlags {
|
||
var src, dst string
|
||
if nw := pkt.NetworkLayer(); nw != nil {
|
||
srcp, dstp := nw.NetworkFlow().Endpoints()
|
||
src = srcp.String()
|
||
dst = dstp.String()
|
||
}
|
||
if !(n.ipInRange(src) || n.ipInRange(dst)) {
|
||
return false
|
||
}
|
||
if tcpLayer == nil {
|
||
if len(n.targetCmd) != 0 && !n.strInRange(string(pkt.TransportLayer().LayerPayload())) {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
if len(n.targetCmd) != 0 && !n.strInRange(string(tcpLayer.LayerPayload())) {
|
||
return false
|
||
}
|
||
}
|
||
if len(n.Flags) == 0 {
|
||
return true
|
||
}
|
||
if tcpLayer == nil {
|
||
return false
|
||
}
|
||
tl := tcpLayer.(*layers.TCP)
|
||
for _, seq := range n.Flags {
|
||
notMatch := false
|
||
bkfor:
|
||
for _, v := range strings.Split(strings.ToUpper(seq), ",") {
|
||
switch strings.TrimSpace(v) {
|
||
case "SYN":
|
||
if !tl.SYN {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "ACK":
|
||
if !tl.ACK {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "FIN":
|
||
if !tl.FIN {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "RST":
|
||
if !tl.RST {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "CWR":
|
||
if !tl.CWR {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "ECE":
|
||
if !tl.ECE {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "NS":
|
||
if !tl.NS {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "PSH":
|
||
if !tl.PSH {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
case "URG":
|
||
if !tl.URG {
|
||
notMatch = true
|
||
break bkfor
|
||
}
|
||
}
|
||
}
|
||
if !notMatch {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
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())
|
||
}
|