session.go 9.61 KB
Newer Older
Sophie Brun's avatar
Sophie Brun committed
1 2 3 4 5 6 7 8 9 10 11
package session

import (
	"errors"
	"fmt"
	"net"
	"os"
	"regexp"
	"runtime"
	"runtime/pprof"
	"sort"
Sophie Brun's avatar
Sophie Brun committed
12
	"strings"
Sophie Brun's avatar
Sophie Brun committed
13 14 15 16
	"time"

	"github.com/bettercap/readline"

Sophie Brun's avatar
Sophie Brun committed
17
	"github.com/bettercap/bettercap/caplets"
Sophie Brun's avatar
Sophie Brun committed
18 19 20 21 22
	"github.com/bettercap/bettercap/core"
	"github.com/bettercap/bettercap/firewall"
	"github.com/bettercap/bettercap/network"
	"github.com/bettercap/bettercap/packets"

Sophie Brun's avatar
Sophie Brun committed
23
	"github.com/evilsocket/islazy/data"
Sophie Brun's avatar
Sophie Brun committed
24 25
	"github.com/evilsocket/islazy/fs"
	"github.com/evilsocket/islazy/log"
Sophie Brun's avatar
Sophie Brun committed
26
	"github.com/evilsocket/islazy/ops"
Sophie Brun's avatar
Sophie Brun committed
27 28
	"github.com/evilsocket/islazy/str"
	"github.com/evilsocket/islazy/tui"
Sophie Brun's avatar
Sophie Brun committed
29 30
)

Sophie Brun's avatar
Sophie Brun committed
31 32 33
const (
	HistoryFile = "~/bettercap.history"
)
Sophie Brun's avatar
Sophie Brun committed
34 35 36 37

var (
	I = (*Session)(nil)

38
	ErrNotSupported = errors.New("this component is not supported on this OS")
Sophie Brun's avatar
Sophie Brun committed
39 40

	reCmdSpaceCleaner = regexp.MustCompile(`^([^\s]+)\s+(.+)$`)
Sophie Brun's avatar
Sophie Brun committed
41
	reEnvVarCapture   = regexp.MustCompile(`{env\.([^}]+)}`)
Sophie Brun's avatar
Sophie Brun committed
42 43
)

44 45 46 47 48 49 50 51
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)
}

Sophie Brun's avatar
Sophie Brun committed
52 53
type UnknownCommandCallback func(cmd string) bool

Sophie Brun's avatar
Sophie Brun committed
54 55 56 57 58 59 60 61 62 63
type GPS struct {
	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
}

Sophie Brun's avatar
Sophie Brun committed
64 65 66 67
const AliasesFile = "~/bettercap.aliases"

var aliasesFileName, _ = fs.Expand(AliasesFile)

Sophie Brun's avatar
Sophie Brun committed
68
type Session struct {
Sophie Brun's avatar
Sophie Brun committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	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
Sophie Brun's avatar
Sophie Brun committed
91 92 93
}

func New() (*Session, error) {
Sophie Brun's avatar
Sophie Brun committed
94 95 96 97 98 99 100 101 102
	opts, err := core.ParseOptions()
	if err != nil {
		return nil, err
	}

	if *opts.NoColors || !tui.Effects() {
		tui.Disable()
		log.NoEffects = true
	}
Sophie Brun's avatar
Sophie Brun committed
103 104

	s := &Session{
Sophie Brun's avatar
Sophie Brun committed
105 106 107 108 109
		Prompt:  NewPrompt(),
		Options: opts,
		Env:     nil,
		Active:  false,
		Queue:   nil,
Sophie Brun's avatar
Sophie Brun committed
110

Sophie Brun's avatar
Sophie Brun committed
111 112 113 114 115
		CoreHandlers:     make([]CommandHandler, 0),
		Modules:          make([]Module, 0),
		Events:           nil,
		EventsIgnoreList: NewEventsIgnoreList(),
		UnkCmdCallback:   nil,
Sophie Brun's avatar
Sophie Brun committed
116 117 118 119 120 121 122 123 124 125
	}

	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
		}
	}

Sophie Brun's avatar
Sophie Brun committed
126 127 128 129
	if s.Env, err = NewEnvironment(*s.Options.EnvFile); err != nil {
		return nil, err
	}

Sophie Brun's avatar
Sophie Brun committed
130 131 132 133
	if s.Aliases, err = data.NewUnsortedKV(aliasesFileName, data.FlushOnEdit); err != nil {
		return nil, err
	}

Sophie Brun's avatar
Sophie Brun committed
134 135 136 137 138 139 140 141 142 143 144
	s.Events = NewEventPool(*s.Options.Debug, *s.Options.Silent)

	s.registerCoreHandlers()

	if I == nil {
		I = s
	}

	return s, nil
}

Sophie Brun's avatar
Sophie Brun committed
145 146 147 148 149 150 151 152 153 154 155 156
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()
}

Sophie Brun's avatar
Sophie Brun committed
157 158 159 160 161 162
func (s *Session) Module(name string) (err error, mod Module) {
	for _, m := range s.Modules {
		if m.Name() == name {
			return nil, m
		}
	}
Sophie Brun's avatar
Sophie Brun committed
163
	return fmt.Errorf("module %s not found", name), mod
Sophie Brun's avatar
Sophie Brun committed
164 165
}

Sophie Brun's avatar
Sophie Brun committed
166
func (s *Session) Close() {
Sophie Brun's avatar
Sophie Brun committed
167 168 169 170
	if *s.Options.PrintVersion {
		return
	}

Sophie Brun's avatar
Sophie Brun committed
171
	if *s.Options.Debug {
Sophie Brun's avatar
Sophie Brun committed
172
		fmt.Printf("\nStopping modules and cleaning session state ...\n")
Sophie Brun's avatar
Sophie Brun committed
173 174 175 176 177 178 179 180 181 182 183 184
		s.Events.Add("session.closing", nil)
	}

	for _, m := range s.Modules {
		if m.Running() {
			m.Stop()
		}
	}

	s.Firewall.Restore()

	if *s.Options.EnvFile != "" {
Sophie Brun's avatar
Sophie Brun committed
185
		envFile, _ := fs.Expand(*s.Options.EnvFile)
Sophie Brun's avatar
Sophie Brun committed
186
		if err := s.Env.Save(envFile); err != nil {
Sophie Brun's avatar
Sophie Brun committed
187
			fmt.Printf("error while storing the environment to %s: %s", envFile, err)
Sophie Brun's avatar
Sophie Brun committed
188 189 190 191 192 193 194 195 196 197
		}
	}

	if *s.Options.CpuProfile != "" {
		pprof.StopCPUProfile()
	}

	if *s.Options.MemProfile != "" {
		f, err := os.Create(*s.Options.MemProfile)
		if err != nil {
Sophie Brun's avatar
Sophie Brun committed
198
			fmt.Printf("could not create memory profile: %s\n", err)
Sophie Brun's avatar
Sophie Brun committed
199 200 201 202 203
			return
		}
		defer f.Close()
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
Sophie Brun's avatar
Sophie Brun committed
204
			fmt.Printf("could not write memory profile: %s\n", err)
Sophie Brun's avatar
Sophie Brun committed
205 206 207 208 209 210 211 212 213 214 215 216
		}
	}
}

func (s *Session) Register(mod Module) error {
	s.Modules = append(s.Modules, mod)
	return nil
}

func (s *Session) Start() error {
	var err error

Sophie Brun's avatar
Sophie Brun committed
217 218 219 220
	network.Debug = func(format string, args ...interface{}) {
		s.Events.Log(log.DEBUG, format, args...)
	}

Sophie Brun's avatar
Sophie Brun committed
221 222 223 224 225 226 227 228 229 230 231 232 233
	// 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
	}

Sophie Brun's avatar
Sophie Brun committed
234 235 236 237
	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)
Sophie Brun's avatar
Sophie Brun committed
238
		}
Sophie Brun's avatar
Sophie Brun committed
239 240 241 242 243 244
	} else {
		s.Gateway, err = network.FindGateway(s.Interface)
	}

	if err != nil {
		level := ops.Ternary(s.Interface.IsMonitor(), log.DEBUG, log.WARNING).(log.Verbosity)
Sophie Brun's avatar
Sophie Brun committed
245
		s.Events.Log(level, "%s", err.Error())
Sophie Brun's avatar
Sophie Brun committed
246 247 248 249 250 251 252 253
	}

	if s.Gateway == nil || s.Gateway.IpAddress == s.Interface.IpAddress {
		s.Gateway = s.Interface
	}

	s.Firewall = firewall.Make(s.Interface)

Sophie Brun's avatar
Sophie Brun committed
254
	s.HID = network.NewHID(s.Aliases, func(dev *network.HIDDevice) {
Sophie Brun's avatar
Sophie Brun committed
255 256 257 258 259
		s.Events.Add("hid.device.new", dev)
	}, func(dev *network.HIDDevice) {
		s.Events.Add("hid.device.lost", dev)
	})

Sophie Brun's avatar
Sophie Brun committed
260
	s.BLE = network.NewBLE(s.Aliases, func(dev *network.BLEDevice) {
Sophie Brun's avatar
Sophie Brun committed
261 262 263 264 265
		s.Events.Add("ble.device.new", dev)
	}, func(dev *network.BLEDevice) {
		s.Events.Add("ble.device.lost", dev)
	})

Sophie Brun's avatar
Sophie Brun committed
266
	s.WiFi = network.NewWiFi(s.Interface, s.Aliases, func(ap *network.AccessPoint) {
Sophie Brun's avatar
Sophie Brun committed
267 268 269 270 271
		s.Events.Add("wifi.ap.new", ap)
	}, func(ap *network.AccessPoint) {
		s.Events.Add("wifi.ap.lost", ap)
	})

Sophie Brun's avatar
Sophie Brun committed
272
	s.Lan = network.NewLAN(s.Interface, s.Gateway, s.Aliases, func(e *network.Endpoint) {
Sophie Brun's avatar
Sophie Brun committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
		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 {
Sophie Brun's avatar
Sophie Brun committed
299
	if ip.IsLoopback() {
Sophie Brun's avatar
Sophie Brun committed
300
		return true
Sophie Brun's avatar
Sophie Brun committed
301
	} else if ip.Equal(s.Interface.IP) {
Sophie Brun's avatar
Sophie Brun committed
302
		return true
Sophie Brun's avatar
Sophie Brun committed
303
	} else if ip.Equal(s.Gateway.IP) {
Sophie Brun's avatar
Sophie Brun committed
304 305 306 307 308
		return true
	}
	return false
}

Sophie Brun's avatar
Sophie Brun committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
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
}

Sophie Brun's avatar
Sophie Brun committed
342 343 344 345 346 347 348 349 350 351
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() {
Sophie Brun's avatar
Sophie Brun committed
352 353
	p, _ := s.parseEnvTokens(s.Prompt.Render(s))
	s.Input.SetPrompt(p)
Sophie Brun's avatar
Sophie Brun committed
354 355 356 357 358 359 360 361 362
	s.Input.Refresh()
}

func (s *Session) ReadLine() (string, error) {
	s.Refresh()
	return s.Input.Readline()
}

func (s *Session) RunCaplet(filename string) error {
Sophie Brun's avatar
Sophie Brun committed
363
	err, caplet := caplets.Load(filename)
Sophie Brun's avatar
Sophie Brun committed
364 365 366 367
	if err != nil {
		return err
	}

Sophie Brun's avatar
Sophie Brun committed
368 369 370 371 372 373
	return caplet.Eval(nil, func(line string) error {
		return s.Run(line + "\n")
	})
}

func parseCapletCommand(line string) (is bool, caplet *caplets.Caplet, argv []string) {
Sophie Brun's avatar
Sophie Brun committed
374
	file := str.Trim(line)
Sophie Brun's avatar
Sophie Brun committed
375 376 377 378 379
	parts := strings.Split(file, " ")
	argc := len(parts)
	argv = make([]string, 0)
	// check for any arguments
	if argc > 1 {
Sophie Brun's avatar
Sophie Brun committed
380 381
		file = str.Trim(parts[0])
		argv = parts[1:]
Sophie Brun's avatar
Sophie Brun committed
382 383 384 385 386 387 388
	}

	if err, cap := caplets.Load(file); err == nil {
		return true, cap, argv
	}

	return false, nil, nil
Sophie Brun's avatar
Sophie Brun committed
389 390 391
}

func (s *Session) Run(line string) error {
Sophie Brun's avatar
Sophie Brun committed
392
	line = str.TrimRight(line)
Sophie Brun's avatar
Sophie Brun committed
393 394 395 396 397
	// 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")

Sophie Brun's avatar
Sophie Brun committed
398
	// replace all {env.something} with their values
Sophie Brun's avatar
Sophie Brun committed
399 400 401 402 403
	line, err := s.parseEnvTokens(line)
	if err != nil {
		return err
	}

Sophie Brun's avatar
Sophie Brun committed
404 405
	// is it a core command?
	for _, h := range s.CoreHandlers {
Sophie Brun's avatar
Sophie Brun committed
406
		if parsed, args := h.Parse(line); parsed {
Sophie Brun's avatar
Sophie Brun committed
407 408 409 410 411 412 413
			return h.Exec(args, s)
		}
	}

	// is it a module command?
	for _, m := range s.Modules {
		for _, h := range m.Handlers() {
Sophie Brun's avatar
Sophie Brun committed
414
			if parsed, args := h.Parse(line); parsed {
Sophie Brun's avatar
Sophie Brun committed
415 416 417 418 419 420
				return h.Exec(args)
			}
		}
	}

	// is it a caplet command?
Sophie Brun's avatar
Sophie Brun committed
421
	if parsed, caplet, argv := parseCapletCommand(line); parsed {
Sophie Brun's avatar
Sophie Brun committed
422 423 424
		return caplet.Eval(argv, func(line string) error {
			return s.Run(line + "\n")
		})
Sophie Brun's avatar
Sophie Brun committed
425 426 427
	}

	// is it a proxy module custom command?
Sophie Brun's avatar
Sophie Brun committed
428
	if s.UnkCmdCallback != nil && s.UnkCmdCallback(line) {
Sophie Brun's avatar
Sophie Brun committed
429 430 431
		return nil
	}

Sophie Brun's avatar
Sophie Brun committed
432
	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)
Sophie Brun's avatar
Sophie Brun committed
433
}