|
|
//go:build windows && !arm64
|
|
|
|
|
|
package tcm
|
|
|
|
|
|
import (
|
|
|
"b612.me/bcap"
|
|
|
"b612.me/bcap/libpcap"
|
|
|
"b612.me/starlog"
|
|
|
"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
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
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 (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
|
|
|
}
|
|
|
for {
|
|
|
select {
|
|
|
case <-stopCtx.Done():
|
|
|
return nil
|
|
|
case p := <-n.packetCache:
|
|
|
w.WritePacket(p.Metadata().CaptureInfo, p.Data())
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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 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())
|
|
|
}
|