mirror of
https://github.com/bettercap/bettercap
synced 2025-08-20 13:33:21 -07:00
refact: refactored sniffer to allow several parsers
This commit is contained in:
parent
74867aaae4
commit
55b9b1f189
5 changed files with 235 additions and 180 deletions
9
caplets/local-sniffer.cap
Normal file
9
caplets/local-sniffer.cap
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
events.stream off
|
||||||
|
events.clear
|
||||||
|
set events.stream.filter net.sniff
|
||||||
|
events.stream on
|
||||||
|
|
||||||
|
set net.sniffer.verbose true
|
||||||
|
set net.sniffer.local true
|
||||||
|
|
||||||
|
net.sniffer on
|
|
@ -3,111 +3,14 @@ package modules
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/core"
|
|
||||||
"github.com/evilsocket/bettercap-ng/log"
|
|
||||||
"github.com/evilsocket/bettercap-ng/session"
|
"github.com/evilsocket/bettercap-ng/session"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/google/gopacket/pcap"
|
|
||||||
"github.com/google/gopacket/pcapgo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SnifferContext struct {
|
|
||||||
Handle *pcap.Handle
|
|
||||||
DumpLocal bool
|
|
||||||
Verbose bool
|
|
||||||
Filter string
|
|
||||||
Expression string
|
|
||||||
Compiled *regexp.Regexp
|
|
||||||
Output string
|
|
||||||
OutputFile *os.File
|
|
||||||
OutputWriter *pcapgo.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSnifferContext() *SnifferContext {
|
|
||||||
return &SnifferContext{
|
|
||||||
Handle: nil,
|
|
||||||
DumpLocal: false,
|
|
||||||
Verbose: true,
|
|
||||||
Filter: "",
|
|
||||||
Expression: "",
|
|
||||||
Compiled: nil,
|
|
||||||
Output: "",
|
|
||||||
OutputFile: nil,
|
|
||||||
OutputWriter: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
no = core.Red("no")
|
|
||||||
yes = core.Green("yes")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *SnifferContext) Log(sess *session.Session) {
|
|
||||||
if c.DumpLocal {
|
|
||||||
log.Info("Skip local packets : %s", no)
|
|
||||||
} else {
|
|
||||||
log.Info("Skip local packets : %s", yes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Verbose {
|
|
||||||
log.Info("Verbose : %s", yes)
|
|
||||||
} else {
|
|
||||||
log.Info("Verbose : %s", no)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Filter != "" {
|
|
||||||
log.Info("BPF Filter : '%s'", core.Yellow(c.Filter))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Expression != "" {
|
|
||||||
log.Info("Regular expression : '%s'", core.Yellow(c.Expression))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Output != "" {
|
|
||||||
log.Info("File output : '%s'", core.Yellow(c.Output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SnifferContext) Close() {
|
|
||||||
if c.Handle != nil {
|
|
||||||
c.Handle.Close()
|
|
||||||
c.Handle = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.OutputFile != nil {
|
|
||||||
c.OutputFile.Close()
|
|
||||||
c.OutputFile = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SnifferStats struct {
|
|
||||||
NumLocal uint64
|
|
||||||
NumMatched uint64
|
|
||||||
NumDumped uint64
|
|
||||||
NumWrote uint64
|
|
||||||
Started time.Time
|
|
||||||
FirstPacket time.Time
|
|
||||||
LastPacket time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSnifferStats() *SnifferStats {
|
|
||||||
return &SnifferStats{
|
|
||||||
NumLocal: 0,
|
|
||||||
NumMatched: 0,
|
|
||||||
NumDumped: 0,
|
|
||||||
NumWrote: 0,
|
|
||||||
Started: time.Now(),
|
|
||||||
FirstPacket: time.Time{},
|
|
||||||
LastPacket: time.Time{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Sniffer struct {
|
type Sniffer struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
Stats *SnifferStats
|
Stats *SnifferStats
|
||||||
|
@ -146,7 +49,12 @@ func NewSniffer(s *session.Session) *Sniffer {
|
||||||
sniff.AddHandler(session.NewModuleHandler("net.sniffer stats", "",
|
sniff.AddHandler(session.NewModuleHandler("net.sniffer stats", "",
|
||||||
"Print sniffer session configuration and statistics.",
|
"Print sniffer session configuration and statistics.",
|
||||||
func(args []string) error {
|
func(args []string) error {
|
||||||
return sniff.PrintStats()
|
sniff.Ctx.Log(sniff.Session)
|
||||||
|
|
||||||
|
if sniff.Stats == nil {
|
||||||
|
return fmt.Errorf("No stats yet.")
|
||||||
|
}
|
||||||
|
return sniff.Stats.Print()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
sniff.AddHandler(session.NewModuleHandler("net.sniffer on", "",
|
sniff.AddHandler(session.NewModuleHandler("net.sniffer on", "",
|
||||||
|
@ -208,91 +116,24 @@ func (s Sniffer) isLocalPacket(packet gopacket.Packet) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sniffer) GetContext() (error, *SnifferContext) {
|
func (s *Sniffer) onPacketMatched(pkt gopacket.Packet) {
|
||||||
var err error
|
if s.Ctx.Verbose == false {
|
||||||
|
return
|
||||||
ctx := NewSnifferContext()
|
|
||||||
|
|
||||||
if ctx.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err, v := s.Param("net.sniffer.verbose").Get(s.Session); err != nil {
|
dumped := false
|
||||||
return err, ctx
|
for _, parser := range PacketParsers {
|
||||||
} else {
|
if parser(pkt) == true {
|
||||||
ctx.Verbose = v.(bool)
|
dumped = true
|
||||||
}
|
break
|
||||||
|
|
||||||
if err, v := s.Param("net.sniffer.local").Get(s.Session); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
} else {
|
|
||||||
ctx.DumpLocal = v.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err, v := s.Param("net.sniffer.filter").Get(s.Session); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
} else {
|
|
||||||
if ctx.Filter = v.(string); ctx.Filter != "" {
|
|
||||||
err = ctx.Handle.SetBPFFilter(ctx.Filter)
|
|
||||||
if err != nil {
|
|
||||||
return err, ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err, v := s.Param("net.sniffer.regexp").Get(s.Session); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
} else {
|
|
||||||
if ctx.Expression = v.(string); ctx.Expression != "" {
|
|
||||||
if ctx.Compiled, err = regexp.Compile(ctx.Expression); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err, v := s.Param("net.sniffer.output").Get(s.Session); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
} else {
|
|
||||||
if ctx.Output = v.(string); ctx.Output != "" {
|
|
||||||
if ctx.OutputFile, err = os.Create(ctx.Output); err != nil {
|
|
||||||
return err, ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.OutputWriter = pcapgo.NewWriter(ctx.OutputFile)
|
|
||||||
ctx.OutputWriter.WriteFileHeader(65536, layers.LinkTypeEthernet)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ctx
|
if dumped == false {
|
||||||
|
noParser(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sniffer) PrintStats() error {
|
s.Stats.NumDumped++
|
||||||
if s.Stats == nil {
|
|
||||||
return fmt.Errorf("No stats yet.")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Ctx.Log(s.Session)
|
|
||||||
|
|
||||||
first := "never"
|
|
||||||
last := "never"
|
|
||||||
|
|
||||||
if s.Stats.FirstPacket.IsZero() == false {
|
|
||||||
first = s.Stats.FirstPacket.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Stats.LastPacket.IsZero() == false {
|
|
||||||
last = s.Stats.LastPacket.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Sniffer Started : %s", s.Stats.Started)
|
|
||||||
log.Info("First Packet Seen : %s", first)
|
|
||||||
log.Info("Last Packet Seen : %s", last)
|
|
||||||
log.Info("Local Packets : %d", s.Stats.NumLocal)
|
|
||||||
log.Info("Matched Packets : %d", s.Stats.NumMatched)
|
|
||||||
log.Info("Dumped Packets : %d", s.Stats.NumDumped)
|
|
||||||
log.Info("Wrote Packets : %d", s.Stats.NumWrote)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sniffer) Start() error {
|
func (s *Sniffer) Start() error {
|
||||||
|
@ -336,10 +177,7 @@ func (s *Sniffer) Start() error {
|
||||||
if s.Ctx.Compiled == nil || s.Ctx.Compiled.Match(data) == true {
|
if s.Ctx.Compiled == nil || s.Ctx.Compiled.Match(data) == true {
|
||||||
s.Stats.NumMatched++
|
s.Stats.NumMatched++
|
||||||
|
|
||||||
if s.Ctx.Verbose {
|
s.onPacketMatched(packet)
|
||||||
fmt.Println(packet.Dump())
|
|
||||||
s.Stats.NumDumped++
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Ctx.OutputWriter != nil {
|
if s.Ctx.OutputWriter != nil {
|
||||||
s.Ctx.OutputWriter.WritePacket(packet.Metadata().CaptureInfo, data)
|
s.Ctx.OutputWriter.WritePacket(packet.Metadata().CaptureInfo, data)
|
||||||
|
|
141
modules/net_sniff_context.go
Normal file
141
modules/net_sniff_context.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
"github.com/evilsocket/bettercap-ng/log"
|
||||||
|
"github.com/evilsocket/bettercap-ng/session"
|
||||||
|
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcap"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SnifferContext struct {
|
||||||
|
Handle *pcap.Handle
|
||||||
|
DumpLocal bool
|
||||||
|
Verbose bool
|
||||||
|
Filter string
|
||||||
|
Expression string
|
||||||
|
Compiled *regexp.Regexp
|
||||||
|
Output string
|
||||||
|
OutputFile *os.File
|
||||||
|
OutputWriter *pcapgo.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sniffer) GetContext() (error, *SnifferContext) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := NewSnifferContext()
|
||||||
|
|
||||||
|
if ctx.Handle, err = pcap.OpenLive(s.Session.Interface.Name(), 65536, true, pcap.BlockForever); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, v := s.Param("net.sniffer.verbose").Get(s.Session); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
} else {
|
||||||
|
ctx.Verbose = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, v := s.Param("net.sniffer.local").Get(s.Session); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
} else {
|
||||||
|
ctx.DumpLocal = v.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, v := s.Param("net.sniffer.filter").Get(s.Session); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
} else {
|
||||||
|
if ctx.Filter = v.(string); ctx.Filter != "" {
|
||||||
|
err = ctx.Handle.SetBPFFilter(ctx.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return err, ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, v := s.Param("net.sniffer.regexp").Get(s.Session); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
} else {
|
||||||
|
if ctx.Expression = v.(string); ctx.Expression != "" {
|
||||||
|
if ctx.Compiled, err = regexp.Compile(ctx.Expression); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, v := s.Param("net.sniffer.output").Get(s.Session); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
} else {
|
||||||
|
if ctx.Output = v.(string); ctx.Output != "" {
|
||||||
|
if ctx.OutputFile, err = os.Create(ctx.Output); err != nil {
|
||||||
|
return err, ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.OutputWriter = pcapgo.NewWriter(ctx.OutputFile)
|
||||||
|
ctx.OutputWriter.WriteFileHeader(65536, layers.LinkTypeEthernet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSnifferContext() *SnifferContext {
|
||||||
|
return &SnifferContext{
|
||||||
|
Handle: nil,
|
||||||
|
DumpLocal: false,
|
||||||
|
Verbose: true,
|
||||||
|
Filter: "",
|
||||||
|
Expression: "",
|
||||||
|
Compiled: nil,
|
||||||
|
Output: "",
|
||||||
|
OutputFile: nil,
|
||||||
|
OutputWriter: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
no = core.Red("no")
|
||||||
|
yes = core.Green("yes")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *SnifferContext) Log(sess *session.Session) {
|
||||||
|
if c.DumpLocal {
|
||||||
|
log.Info("Skip local packets : %s", no)
|
||||||
|
} else {
|
||||||
|
log.Info("Skip local packets : %s", yes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Verbose {
|
||||||
|
log.Info("Verbose : %s", yes)
|
||||||
|
} else {
|
||||||
|
log.Info("Verbose : %s", no)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Filter != "" {
|
||||||
|
log.Info("BPF Filter : '%s'", core.Yellow(c.Filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Expression != "" {
|
||||||
|
log.Info("Regular expression : '%s'", core.Yellow(c.Expression))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Output != "" {
|
||||||
|
log.Info("File output : '%s'", core.Yellow(c.Output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SnifferContext) Close() {
|
||||||
|
if c.Handle != nil {
|
||||||
|
c.Handle.Close()
|
||||||
|
c.Handle = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.OutputFile != nil {
|
||||||
|
c.OutputFile.Close()
|
||||||
|
c.OutputFile = nil
|
||||||
|
}
|
||||||
|
}
|
16
modules/net_sniff_parsers.go
Normal file
16
modules/net_sniff_parsers.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
// "github.com/evilsocket/bettercap-ng/session"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SnifferPacketParser func(pkt gopacket.Packet) bool
|
||||||
|
|
||||||
|
var PacketParsers = []SnifferPacketParser{}
|
||||||
|
|
||||||
|
func noParser(pkt gopacket.Packet) bool {
|
||||||
|
fmt.Println(pkt.Dump())
|
||||||
|
return true
|
||||||
|
}
|
51
modules/net_sniff_stats.go
Normal file
51
modules/net_sniff_stats.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/evilsocket/bettercap-ng/log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SnifferStats struct {
|
||||||
|
NumLocal uint64
|
||||||
|
NumMatched uint64
|
||||||
|
NumDumped uint64
|
||||||
|
NumWrote uint64
|
||||||
|
Started time.Time
|
||||||
|
FirstPacket time.Time
|
||||||
|
LastPacket time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSnifferStats() *SnifferStats {
|
||||||
|
return &SnifferStats{
|
||||||
|
NumLocal: 0,
|
||||||
|
NumMatched: 0,
|
||||||
|
NumDumped: 0,
|
||||||
|
NumWrote: 0,
|
||||||
|
Started: time.Now(),
|
||||||
|
FirstPacket: time.Time{},
|
||||||
|
LastPacket: time.Time{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SnifferStats) Print() error {
|
||||||
|
first := "never"
|
||||||
|
last := "never"
|
||||||
|
|
||||||
|
if s.FirstPacket.IsZero() == false {
|
||||||
|
first = s.FirstPacket.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.LastPacket.IsZero() == false {
|
||||||
|
last = s.LastPacket.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Sniffer Started : %s", s.Started)
|
||||||
|
log.Info("First Packet Seen : %s", first)
|
||||||
|
log.Info("Last Packet Seen : %s", last)
|
||||||
|
log.Info("Local Packets : %d", s.NumLocal)
|
||||||
|
log.Info("Matched Packets : %d", s.NumMatched)
|
||||||
|
log.Info("Dumped Packets : %d", s.NumDumped)
|
||||||
|
log.Info("Wrote Packets : %d", s.NumWrote)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue