Skip to content

Commit 7610d3c

Browse files
Merge pull request #3 from shadowy-pycoder/arpspoof
Arpspoof package
2 parents 0b361e8 + a603067 commit 7610d3c

File tree

10 files changed

+708
-70
lines changed

10 files changed

+708
-70
lines changed

arpspoof/arpspoof.go

Lines changed: 424 additions & 0 deletions
Large diffs are not rendered by default.

cmd/marpspoof/main.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"net/netip"
7+
"os"
8+
"os/signal"
9+
"regexp"
10+
"time"
11+
12+
"github.com/rs/zerolog"
13+
"github.com/shadowy-pycoder/colors"
14+
"github.com/shadowy-pycoder/mshark/arpspoof"
15+
"github.com/shadowy-pycoder/mshark/network"
16+
)
17+
18+
var (
19+
app = "marpspoof"
20+
ipPortPattern = regexp.MustCompile(
21+
`\b(?:\d{1,3}\.){3}\d{1,3}(?::(6553[0-5]|655[0-2]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]?\d{1,4}))?\b`,
22+
)
23+
macPattern = regexp.MustCompile(`(?i)([a-z0-9_]+_[0-9a-f]{2}(?::[0-9a-f]{2}){2}|(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2})`)
24+
)
25+
26+
func root(args []string) error {
27+
conf := &arpspoof.ARPSpoofConfig{}
28+
flags := flag.NewFlagSet(app, flag.ExitOnError)
29+
flags.StringVar(
30+
&conf.Targets,
31+
"t",
32+
"",
33+
"Targets for ARP spoofing. Example: \"targets 10.0.0.1,10.0.0.5-10,192.168.1.*,192.168.10.0/24\" (Default: entire subnet)",
34+
)
35+
gw := flags.String("g", "", "IPv4 address of custom gateway (Default: default gateway)")
36+
flags.StringVar(&conf.Interface, "i", "", "The name of the network interface. Example: eth0 (Default: default interface)")
37+
flags.BoolVar(&conf.FullDuplex, "f", false, "Run ARP spoofing in fullduplex mode")
38+
flags.BoolVar(&conf.Debug, "d", false, "Enable debug logging")
39+
nocolor := flags.Bool("nocolor", false, "Disable colored output")
40+
flags.BoolFunc("I", "Display list of interfaces and exit.", func(flagValue string) error {
41+
if err := network.DisplayInterfaces(); err != nil {
42+
fmt.Fprintf(os.Stderr, "%s: %v\n", app, err)
43+
os.Exit(2)
44+
}
45+
os.Exit(0)
46+
return nil
47+
})
48+
if err := flags.Parse(args); err != nil {
49+
return err
50+
}
51+
if *gw != "" {
52+
ip, err := netip.ParseAddr(*gw)
53+
if err != nil {
54+
return err
55+
}
56+
conf.Gateway = &ip
57+
}
58+
output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: *nocolor}
59+
output.FormatTimestamp = func(i any) string {
60+
ts, _ := time.Parse(time.RFC3339, i.(string))
61+
if *nocolor {
62+
return colors.WrapBrackets(ts.Format(time.TimeOnly))
63+
}
64+
return colors.Gray(colors.WrapBrackets(ts.Format(time.TimeOnly))).String()
65+
}
66+
output.FormatMessage = func(i any) string {
67+
if i == nil || i == "" {
68+
return ""
69+
}
70+
s := i.(string)
71+
if *nocolor {
72+
return s
73+
}
74+
result := ipPortPattern.ReplaceAllStringFunc(s, func(match string) string {
75+
return colors.Gray(match).String()
76+
})
77+
result = macPattern.ReplaceAllStringFunc(result, func(match string) string {
78+
return colors.Yellow(match).String()
79+
})
80+
return result
81+
}
82+
logger := zerolog.New(output).With().Timestamp().Logger()
83+
conf.Logger = &logger
84+
arpspoofer, err := arpspoof.NewARPSpoofer(conf)
85+
if err != nil {
86+
return err
87+
}
88+
go arpspoofer.Start()
89+
quit := make(chan os.Signal, 1)
90+
signal.Notify(quit, os.Interrupt)
91+
<-quit
92+
return arpspoofer.Stop()
93+
}
94+
95+
func main() {
96+
if err := root(os.Args[1:]); err != nil {
97+
fmt.Fprintf(os.Stderr, "%s: %v\n", app, err)
98+
os.Exit(2)
99+
}
100+
}

cmd/mshark/cli.go

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ package main
33
import (
44
"flag"
55
"fmt"
6-
"net"
76
"os"
87
"path/filepath"
98
"slices"
109
"strings"
11-
"text/tabwriter"
1210
"time"
1311

1412
ms "github.com/shadowy-pycoder/mshark"
1513
"github.com/shadowy-pycoder/mshark/mpcap"
1614
"github.com/shadowy-pycoder/mshark/mpcapng"
15+
"github.com/shadowy-pycoder/mshark/network"
1716
)
1817

1918
const app string = "mshark"
@@ -62,21 +61,6 @@ func (f *ExtFlag) UnmarshalText(b []byte) error {
6261
return nil
6362
}
6463

65-
func displayInterfaces() error {
66-
w := new(tabwriter.Writer)
67-
w.Init(os.Stdout, 0, 0, 2, ' ', tabwriter.TabIndent)
68-
ifaces, err := net.Interfaces()
69-
if err != nil {
70-
return fmt.Errorf("failed to get network interfaces: %v", err)
71-
}
72-
fmt.Fprintln(w, "Index\tName\tFlags")
73-
fmt.Fprintln(w, "0\tany\tUP")
74-
for _, iface := range ifaces {
75-
fmt.Fprintf(w, "%d\t%s\t%s\n", iface.Index, iface.Name, strings.ToUpper(iface.Flags.String()))
76-
}
77-
return w.Flush()
78-
}
79-
8064
func createFile(app, ext string) (*os.File, error) {
8165
path := fmt.Sprintf("./%s_%s.%s", app, time.Now().UTC().Format("20060102_150405"), ext)
8266
f, err := os.OpenFile(filepath.FromSlash(path), os.O_CREATE|os.O_WRONLY, 0o644)
@@ -105,7 +89,7 @@ func root(args []string) error {
10589
packetBuffer := flags.Int("b", 8192, "The maximum size of packet queue.")
10690
flags.StringVar(&conf.Expr, "e", "", `BPF filter expression. Example: "ip proto tcp".`)
10791
flags.BoolFunc("D", "Display list of interfaces and exit.", func(flagValue string) error {
108-
if err := displayInterfaces(); err != nil {
92+
if err := network.DisplayInterfaces(); err != nil {
10993
fmt.Fprintf(os.Stderr, "%s: %v\n", app, err)
11094
os.Exit(2)
11195
}
@@ -130,7 +114,7 @@ func root(args []string) error {
130114
}
131115

132116
// getting network interface from the provided name
133-
in, err := ms.InterfaceByName(*iface)
117+
in, err := network.InterfaceByName(*iface)
134118
if err != nil {
135119
return err
136120
}

go.mod

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ go 1.24.1
44

55
require (
66
github.com/magefile/mage v1.15.0
7+
github.com/malfunkt/iprange v0.9.0
78
github.com/mdlayher/packet v1.1.2
89
github.com/packetcap/go-pcap v0.0.0-20240528124601-8c87ecf5dbc5
10+
github.com/rs/zerolog v1.34.0
11+
github.com/shadowy-pycoder/colors v0.0.1
912
github.com/stretchr/testify v1.9.0
1013
golang.org/x/net v0.28.0
1114
golang.org/x/text v0.27.0
@@ -14,7 +17,10 @@ require (
1417
require (
1518
github.com/davecgh/go-spew v1.1.1 // indirect
1619
github.com/josharian/native v1.1.0 // indirect
20+
github.com/mattn/go-colorable v0.1.13 // indirect
21+
github.com/mattn/go-isatty v0.0.19 // indirect
1722
github.com/mdlayher/socket v0.4.1 // indirect
23+
github.com/pkg/errors v0.9.1 // indirect
1824
github.com/pmezard/go-difflib v1.0.0 // indirect
1925
golang.org/x/sync v0.16.0 // indirect
2026
golang.org/x/sys v0.24.0 // indirect

go.sum

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
12
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
23
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
35
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
46
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
57
github.com/gopacket/gopacket v1.2.0 h1:eXbzFad7f73P1n2EJHQlsKuvIMJjVXK5tXoSca78I3A=
@@ -8,20 +10,37 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
810
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
911
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
1012
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
13+
github.com/malfunkt/iprange v0.9.0 h1:VCs0PKLUPotNVQTpVNszsut4lP7OCGNBwX+lOYBrnVQ=
14+
github.com/malfunkt/iprange v0.9.0/go.mod h1:TRGqO/f95gh3LOndUGTL46+W0GXA91WTqyZ0Quwvt4U=
15+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
16+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
17+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
18+
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
19+
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
1120
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
1221
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
1322
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
1423
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
1524
github.com/packetcap/go-pcap v0.0.0-20240528124601-8c87ecf5dbc5 h1:p4VuaitqUAqSZSomd7Wb4BPV/Jj7Hno2/iqtfX7DZJI=
1625
github.com/packetcap/go-pcap v0.0.0-20240528124601-8c87ecf5dbc5/go.mod h1:zIAoVKeWP0mz4zXY50UYQt6NLg2uwKRswMDcGEqOms4=
26+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
27+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1728
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1829
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
30+
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
31+
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
32+
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
33+
github.com/shadowy-pycoder/colors v0.0.1 h1:weCj/YIOupqy4BSP8KuVzr20fC+cuAv/tArz7bhhkP4=
34+
github.com/shadowy-pycoder/colors v0.0.1/go.mod h1:lkrJS1PY2oVigNLTT6pkbF7B/v0YcU2LD5PZnss1Q4U=
1935
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
2036
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
2137
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
2238
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
2339
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
2440
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
41+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
43+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2544
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
2645
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
2746
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=

mshark.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -140,31 +140,6 @@ func (mw *Writer) WriteHeader(c *Config) error {
140140
return err
141141
}
142142

143-
// InterfaceByName returns the interface specified by name.
144-
func InterfaceByName(name string) (*net.Interface, error) {
145-
var (
146-
in *net.Interface
147-
err error
148-
)
149-
if name == "any" {
150-
in = &net.Interface{Index: 0, Name: "any"}
151-
} else {
152-
in, err = net.InterfaceByName(name)
153-
if err != nil {
154-
return nil, fmt.Errorf("unknown interface %s: %v", name, err)
155-
}
156-
ok := true &&
157-
// Look for an Ethernet interface.
158-
len(in.HardwareAddr) == 6 &&
159-
// Look for up, multicast, broadcast.
160-
in.Flags&(net.FlagUp|net.FlagMulticast|net.FlagBroadcast) != 0
161-
if !ok {
162-
return nil, fmt.Errorf("interface %s is not up", name)
163-
}
164-
}
165-
return in, nil
166-
}
167-
168143
// OpenLive opens a live capture based on the given configuration and writes
169144
// all captured packets to the given PacketWriters.
170145
func OpenLive(conf *Config, pw ...PacketWriter) error {

mshark_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package mshark
33
import (
44
"io"
55
"testing"
6+
7+
"github.com/shadowy-pycoder/mshark/network"
68
)
79

810
func BenchmarkOpenLive(b *testing.B) {
911
b.ResetTimer()
10-
in, err := InterfaceByName("any")
12+
in, err := network.InterfaceByName("any")
1113
if err != nil {
1214
b.Fatal(err)
1315
}

0 commit comments

Comments
 (0)