bettercap/session/session.go
2019-03-30 00:26:38 +01:00

435 lines
9.6 KiB
Go

package session
import (
"errors"
"fmt"
"net"
"os"
"regexp"
"runtime"
"runtime/pprof"
"sort"
"strings"
"time"
"github.com/bettercap/readline"
"github.com/bettercap/bettercap/caplets"
"github.com/bettercap/bettercap/core"
"github.com/bettercap/bettercap/firewall"
"github.com/bettercap/bettercap/network"
"github.com/bettercap/bettercap/packets"
"github.com/evilsocket/islazy/data"
"github.com/evilsocket/islazy/fs"
"github.com/evilsocket/islazy/log"
"github.com/evilsocket/islazy/ops"
"github.com/evilsocket/islazy/str"
"github.com/evilsocket/islazy/tui"
)
const (
HistoryFile = "~/bettercap.history"
)
var (
I = (*Session)(nil)
ErrNotSupported = errors.New("this component is not supported on this OS")
reCmdSpaceCleaner = regexp.MustCompile(`^([^\s]+)\s+(.+)$`)
reEnvVarCapture = regexp.MustCompile(`{env\.([^}]+)}`)
)
func ErrAlreadyStarted(name string) error {
return fmt.Errorf("module %s is already running", name)
}
func ErrAlreadyStopped(name string) error {
return fmt.Errorf("module %s is not running", name)
}
type UnknownCommandCallback func(cmd string) bool
type GPS struct {
Updated time.Time
Latitude float64 // Latitude.
Longitude float64 // Longitude.
FixQuality string // Quality of fix.
NumSatellites int64 // Number of satellites in use.
HDOP float64 // Horizontal dilution of precision.
Altitude float64 // Altitude.
Separation float64 // Geoidal separation
}
const AliasesFile = "~/bettercap.aliases"
var aliasesFileName, _ = fs.Expand(AliasesFile)
type Session struct {
Options core.Options
Interface *network.Endpoint
Gateway *network.Endpoint
Env *Environment
Lan *network.LAN
WiFi *network.WiFi
BLE *network.BLE
HID *network.HID
Queue *packets.Queue
StartedAt time.Time
Active bool
GPS GPS
Modules ModuleList
Aliases *data.UnsortedKV
Input *readline.Instance
Prompt Prompt
CoreHandlers []CommandHandler
Events *EventPool
EventsIgnoreList *EventsIgnoreList
UnkCmdCallback UnknownCommandCallback
Firewall firewall.FirewallManager
}
func New() (*Session, error) {
opts, err := core.ParseOptions()
if err != nil {
return nil, err
}
if *opts.NoColors || !tui.Effects() {
tui.Disable()
log.NoEffects = true
}
s := &Session{
Prompt: NewPrompt(),
Options: opts,
Env: nil,
Active: false,
Queue: nil,
CoreHandlers: make([]CommandHandler, 0),
Modules: make([]Module, 0),
Events: nil,
EventsIgnoreList: NewEventsIgnoreList(),
UnkCmdCallback: nil,
}
if *s.Options.CpuProfile != "" {
if f, err := os.Create(*s.Options.CpuProfile); err != nil {
return nil, err
} else if err := pprof.StartCPUProfile(f); err != nil {
return nil, err
}
}
if s.Env, err = NewEnvironment(*s.Options.EnvFile); err != nil {
return nil, err
}
if s.Aliases, err = data.NewUnsortedKV(aliasesFileName, data.FlushOnEdit); err != nil {
return nil, err
}
s.Events = NewEventPool(*s.Options.Debug, *s.Options.Silent)
s.registerCoreHandlers()
if I == nil {
I = s
}
return s, nil
}
func (s *Session) Lock() {
s.Env.Lock()
s.Lan.Lock()
s.WiFi.Lock()
}
func (s *Session) Unlock() {
s.Env.Unlock()
s.Lan.Unlock()
s.WiFi.Unlock()
}
func (s *Session) Module(name string) (err error, mod Module) {
for _, m := range s.Modules {
if m.Name() == name {
return nil, m
}
}
return fmt.Errorf("module %s not found", name), mod
}
func (s *Session) Close() {
if *s.Options.PrintVersion {
return
}
if *s.Options.Debug {
fmt.Printf("\nStopping modules and cleaning session state ...\n")
s.Events.Add("session.closing", nil)
}
for _, m := range s.Modules {
if m.Running() {
m.Stop()
}
}
s.Firewall.Restore()
if *s.Options.EnvFile != "" {
envFile, _ := fs.Expand(*s.Options.EnvFile)
if err := s.Env.Save(envFile); err != nil {
fmt.Printf("error while storing the environment to %s: %s", envFile, err)
}
}
if *s.Options.CpuProfile != "" {
pprof.StopCPUProfile()
}
if *s.Options.MemProfile != "" {
f, err := os.Create(*s.Options.MemProfile)
if err != nil {
fmt.Printf("could not create memory profile: %s\n", err)
return
}
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
fmt.Printf("could not write memory profile: %s\n", err)
}
}
}
func (s *Session) Register(mod Module) error {
s.Modules = append(s.Modules, mod)
return nil
}
func (s *Session) Start() error {
var err error
network.Debug = func(format string, args ...interface{}) {
s.Events.Log(log.DEBUG, format, args...)
}
// make sure modules are always sorted by name
sort.Slice(s.Modules, func(i, j int) bool {
return s.Modules[i].Name() < s.Modules[j].Name()
})
if s.Interface, err = network.FindInterface(*s.Options.InterfaceName); err != nil {
return err
}
if s.Queue, err = packets.NewQueue(s.Interface); err != nil {
return err
}
if *s.Options.Gateway != "" {
if s.Gateway, err = network.GatewayProvidedByUser(s.Interface, *s.Options.Gateway); err != nil {
s.Events.Log(log.WARNING, "%s", err.Error())
s.Gateway, err = network.FindGateway(s.Interface)
}
} else {
s.Gateway, err = network.FindGateway(s.Interface)
}
if err != nil {
level := ops.Ternary(s.Interface.IsMonitor(), log.DEBUG, log.WARNING).(log.Verbosity)
s.Events.Log(level, "%s", err.Error())
}
if s.Gateway == nil || s.Gateway.IpAddress == s.Interface.IpAddress {
s.Gateway = s.Interface
}
s.Firewall = firewall.Make(s.Interface)
s.HID = network.NewHID(s.Aliases, func(dev *network.HIDDevice) {
s.Events.Add("hid.device.new", dev)
}, func(dev *network.HIDDevice) {
s.Events.Add("hid.device.lost", dev)
})
s.BLE = network.NewBLE(s.Aliases, func(dev *network.BLEDevice) {
s.Events.Add("ble.device.new", dev)
}, func(dev *network.BLEDevice) {
s.Events.Add("ble.device.lost", dev)
})
s.WiFi = network.NewWiFi(s.Interface, s.Aliases, func(ap *network.AccessPoint) {
s.Events.Add("wifi.ap.new", ap)
}, func(ap *network.AccessPoint) {
s.Events.Add("wifi.ap.lost", ap)
})
s.Lan = network.NewLAN(s.Interface, s.Gateway, s.Aliases, func(e *network.Endpoint) {
s.Events.Add("endpoint.new", e)
}, func(e *network.Endpoint) {
s.Events.Add("endpoint.lost", e)
})
s.setupEnv()
if err := s.setupReadline(); err != nil {
return err
}
s.setupSignals()
s.StartedAt = time.Now()
s.Active = true
s.startNetMon()
if *s.Options.Debug {
s.Events.Add("session.started", nil)
}
return nil
}
func (s *Session) Skip(ip net.IP) bool {
if ip.IsLoopback() {
return true
} else if ip.Equal(s.Interface.IP) {
return true
} else if ip.Equal(s.Gateway.IP) {
return true
}
return false
}
func (s *Session) FindMAC(ip net.IP, probe bool) (net.HardwareAddr, error) {
var mac string
var hw net.HardwareAddr
var err error
// do we have this ip mac address?
mac, err = network.ArpLookup(s.Interface.Name(), ip.String(), false)
if err != nil && probe {
from := s.Interface.IP
from_hw := s.Interface.HW
if err, probe := packets.NewUDPProbe(from, from_hw, ip, 139); err != nil {
log.Error("Error while creating UDP probe packet for %s: %s", ip.String(), err)
} else {
s.Queue.Send(probe)
}
time.Sleep(500 * time.Millisecond)
mac, _ = network.ArpLookup(s.Interface.Name(), ip.String(), false)
}
if mac == "" {
return nil, fmt.Errorf("Could not find hardware address for %s.", ip.String())
}
mac = network.NormalizeMac(mac)
hw, err = net.ParseMAC(mac)
if err != nil {
return nil, fmt.Errorf("Error while parsing hardware address '%s' for %s: %s", mac, ip.String(), err)
}
return hw, nil
}
func (s *Session) IsOn(moduleName string) bool {
for _, m := range s.Modules {
if m.Name() == moduleName {
return m.Running()
}
}
return false
}
func (s *Session) Refresh() {
p, _ := s.parseEnvTokens(s.Prompt.Render(s))
s.Input.SetPrompt(p)
s.Input.Refresh()
}
func (s *Session) ReadLine() (string, error) {
s.Refresh()
return s.Input.Readline()
}
func (s *Session) RunCaplet(filename string) error {
err, caplet := caplets.Load(filename)
if err != nil {
return err
}
return caplet.Eval(nil, func(line string) error {
return s.Run(line + "\n")
})
}
func parseCapletCommand(line string) (is bool, caplet *caplets.Caplet, argv []string) {
file := str.Trim(line)
parts := strings.Split(file, " ")
argc := len(parts)
argv = make([]string, 0)
// check for any arguments
if argc > 1 {
file = str.Trim(parts[0])
argv = parts[1:]
}
if err, cap := caplets.Load(file); err == nil {
return true, cap, argv
}
return false, nil, nil
}
func (s *Session) Run(line string) error {
line = str.TrimRight(line)
// remove extra spaces after the first command
// so that 'arp.spoof on' is normalized
// to 'arp.spoof on' (fixes #178)
line = reCmdSpaceCleaner.ReplaceAllString(line, "$1 $2")
// replace all {env.something} with their values
line, err := s.parseEnvTokens(line)
if err != nil {
return err
}
// is it a core command?
for _, h := range s.CoreHandlers {
if parsed, args := h.Parse(line); parsed {
return h.Exec(args, s)
}
}
// is it a module command?
for _, m := range s.Modules {
for _, h := range m.Handlers() {
if parsed, args := h.Parse(line); parsed {
return h.Exec(args)
}
}
}
// is it a caplet command?
if parsed, caplet, argv := parseCapletCommand(line); parsed {
return caplet.Eval(argv, func(line string) error {
return s.Run(line + "\n")
})
}
// is it a proxy module custom command?
if s.UnkCmdCallback != nil && s.UnkCmdCallback(line) {
return nil
}
return fmt.Errorf("unknown or invalid syntax \"%s%s%s\", type %shelp%s for the help menu.", tui.BOLD, line, tui.RESET, tui.BOLD, tui.RESET)
}