diff --git a/core/options.go b/core/options.go index 887b2901..d9c2566c 100644 --- a/core/options.go +++ b/core/options.go @@ -7,6 +7,7 @@ type Options struct { Caplet *string Debug *bool Silent *bool + NoColors *bool NoHistory *bool EnvFile *string Commands *string @@ -20,6 +21,7 @@ func ParseOptions() (Options, error) { Caplet: flag.String("caplet", "", "Read commands from this file and execute them in the interactive session."), Debug: flag.Bool("debug", false, "Print debug messages."), Silent: flag.Bool("silent", false, "Suppress all logs which are not errors."), + NoColors: flag.Bool("no-colors", false, "Disable output color effect,s."), NoHistory: flag.Bool("no-history", false, "Disable interactive session history file."), EnvFile: flag.String("env-file", "", "Load environment variables from this file if found, set to empty to disable environment persistance."), Commands: flag.String("eval", "", "Run one or more commands separated by ; in the interactive session, used to set variables via command line."), diff --git a/core/swag.go b/core/swag.go index 1e9d2ae3..95d71e06 100644 --- a/core/swag.go +++ b/core/swag.go @@ -26,14 +26,17 @@ var ( RESET = "\033[0m" - NoColors = false + HasColors = true ) -func init() { - NoColors = os.Getenv("TERM") == "dumb" || +func isDumbTerminal() bool { + return os.Getenv("TERM") == "dumb" || os.Getenv("TERM") == "" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) - if NoColors { +} + +func InitSwag(disableColors bool) { + if disableColors || isDumbTerminal() { BOLD = "" DIM = "" RED = "" @@ -48,6 +51,7 @@ func init() { BG_YELLOW = "" BG_LBLUE = "" RESET = "" + HasColors = false } } diff --git a/main.go b/main.go index cde04647..116d48d2 100644 --- a/main.go +++ b/main.go @@ -27,8 +27,12 @@ func main() { os.Exit(1) } - if core.NoColors == true { - fmt.Printf("\n\nWARNING: This terminal does not support colors, view will be very limited.\n\n") + if core.HasColors == false { + if *sess.Options.NoColors == true { + fmt.Printf("\n\nWARNING: Terminal colors have been disabled, view will be very limited.\n\n") + } else { + fmt.Printf("\n\nWARNING: This terminal does not support colors, view will be very limited.\n\n") + } } appName := fmt.Sprintf("%s v%s", core.Name, core.Version) @@ -59,12 +63,6 @@ func main() { log.Fatal("%s", err) } - for _, modName := range autoEnableList { - if err = sess.Run(modName + " on"); err != nil { - log.Fatal("Error while starting module %s: %", modName, err) - } - } - /* * Commands sent with -eval are used to set specific * caplet parameters (i.e. arp.spoof.targets) via command @@ -77,6 +75,13 @@ func main() { } } + // Start modules that are enabled by default. + for _, modName := range autoEnableList { + if err = sess.Run(modName + " on"); err != nil { + log.Fatal("Error while starting module %s: %", modName, err) + } + } + // Then run the caplet if specified. if *sess.Options.Caplet != "" { if err = sess.RunCaplet(*sess.Options.Caplet); err != nil { diff --git a/modules/events_stream.go b/modules/events_stream.go index fa1e9d8b..bc0d6394 100644 --- a/modules/events_stream.go +++ b/modules/events_stream.go @@ -2,6 +2,7 @@ package modules import ( "fmt" + "os" "strconv" "time" @@ -12,6 +13,7 @@ import ( type EventsStream struct { session.SessionModule + output *os.File ignoreList *IgnoreList waitFor string waitChan chan *session.Event @@ -22,6 +24,7 @@ type EventsStream struct { func NewEventsStream(s *session.Session) *EventsStream { stream := &EventsStream{ SessionModule: session.NewSessionModule("events.stream", s), + output: os.Stdout, quit: make(chan bool), waitChan: make(chan *session.Event), waitFor: "", @@ -104,6 +107,11 @@ func NewEventsStream(s *session.Session) *EventsStream { return nil })) + stream.AddParam(session.NewStringParameter("events.stream.output", + "", + "", + "If not empty, events will be written to this file instead of the standard output.")) + return stream } @@ -119,11 +127,25 @@ func (s EventsStream) Author() string { return "Simone Margaritelli " } -func (s *EventsStream) Configure() error { - return nil +func (s *EventsStream) Configure() (err error) { + var output string + + if err, output = s.StringParam("events.stream.output"); err == nil { + if output == "" { + s.output = os.Stdout + } else if output, err = core.ExpandPath(output); err == nil { + s.output, err = os.OpenFile(output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + } + } + + return err } func (s *EventsStream) Start() error { + if err := s.Configure(); err != nil { + return err + } + return s.SetRunning(true, func() { s.eventListener = s.Session.Events.Listen() for { @@ -196,5 +218,8 @@ func (s *EventsStream) startWaitingFor(tag string, timeout int) error { func (s *EventsStream) Stop() error { return s.SetRunning(false, func() { s.quit <- true + if s.output != os.Stdout { + s.output.Close() + } }) } diff --git a/modules/events_view.go b/modules/events_view.go index 21d99451..4ac29e80 100644 --- a/modules/events_view.go +++ b/modules/events_view.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "strings" "github.com/bettercap/bettercap/core" @@ -13,15 +14,15 @@ import ( const eventTimeFormat = "15:04:05" -func (s EventsStream) viewLogEvent(e session.Event) { - fmt.Printf("[%s] [%s] [%s] %s\n", +func (s *EventsStream) viewLogEvent(e session.Event) { + fmt.Fprintf(s.output, "[%s] [%s] [%s] %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e.Label(), e.Data.(session.LogMessage).Message) } -func (s EventsStream) viewWiFiEvent(e session.Event) { +func (s *EventsStream) viewWiFiEvent(e session.Event) { if strings.HasPrefix(e.Tag, "wifi.ap.") { ap := e.Data.(*network.AccessPoint) @@ -35,7 +36,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) { } if e.Tag == "wifi.ap.new" { - fmt.Printf("[%s] [%s] WiFi access point %s%s detected as %s%s.\n", + fmt.Fprintf(s.output, "[%s] [%s] WiFi access point %s%s detected as %s%s.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), core.Bold(ap.ESSID()), @@ -43,13 +44,13 @@ func (s EventsStream) viewWiFiEvent(e session.Event) { core.Green(ap.BSSID()), core.Dim(vend)) } else if e.Tag == "wifi.ap.lost" { - fmt.Printf("[%s] [%s] WiFi access point %s (%s) lost.\n", + fmt.Fprintf(s.output, "[%s] [%s] WiFi access point %s (%s) lost.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), core.Red(ap.ESSID()), ap.BSSID()) } else { - fmt.Printf("[%s] [%s] %s\n", + fmt.Fprintf(s.output, "[%s] [%s] %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), ap.String()) @@ -67,7 +68,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) { rssi = fmt.Sprintf(" (%d dBm)", probe.RSSI) } - fmt.Printf("[%s] [%s] Station %s%s is probing for SSID %s%s\n", + fmt.Fprintf(s.output, "[%s] [%s] Station %s%s is probing for SSID %s%s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), probe.FromAddr.String(), @@ -77,7 +78,7 @@ func (s EventsStream) viewWiFiEvent(e session.Event) { } } -func (s EventsStream) viewEndpointEvent(e session.Event) { +func (s *EventsStream) viewEndpointEvent(e session.Event) { t := e.Data.(*network.Endpoint) vend := "" name := "" @@ -93,7 +94,7 @@ func (s EventsStream) viewEndpointEvent(e session.Event) { } if e.Tag == "endpoint.new" { - fmt.Printf("[%s] [%s] Endpoint %s%s detected as %s%s.\n", + fmt.Fprintf(s.output, "[%s] [%s] Endpoint %s%s detected as %s%s.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), core.Bold(t.IpAddress), @@ -101,27 +102,27 @@ func (s EventsStream) viewEndpointEvent(e session.Event) { core.Green(t.HwAddress), core.Dim(vend)) } else if e.Tag == "endpoint.lost" { - fmt.Printf("[%s] [%s] Endpoint %s%s lost.\n", + fmt.Fprintf(s.output, "[%s] [%s] Endpoint %s%s lost.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), core.Red(t.IpAddress), core.Dim(vend)) } else { - fmt.Printf("[%s] [%s] %s\n", + fmt.Fprintf(s.output, "[%s] [%s] %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), t.String()) } } -func (s EventsStream) viewModuleEvent(e session.Event) { - fmt.Printf("[%s] [%s] %s\n", +func (s *EventsStream) viewModuleEvent(e session.Event) { + fmt.Fprintf(s.output, "[%s] [%s] %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e.Data) } -func (s EventsStream) viewSnifferEvent(e session.Event) { +func (s *EventsStream) viewSnifferEvent(e session.Event) { se := e.Data.(SnifferEvent) misc := "" @@ -150,16 +151,16 @@ func (s EventsStream) viewSnifferEvent(e session.Event) { misc = fmt.Sprintf("%s", se.Data) } - fmt.Printf("[%s] [%s] %s %s\n", + fmt.Fprintf(s.output, "[%s] [%s] %s %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), se.Message, misc) } -func (s EventsStream) viewSynScanEvent(e session.Event) { +func (s *EventsStream) viewSynScanEvent(e session.Event) { se := e.Data.(SynScanEvent) - fmt.Printf("[%s] [%s] Found open port %d for %s\n", + fmt.Fprintf(s.output, "[%s] [%s] Found open port %d for %s\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), se.Port, @@ -182,10 +183,10 @@ func (s *EventsStream) View(e session.Event, refresh bool) { } else if strings.HasPrefix(e.Tag, "syn.scan.") { s.viewSynScanEvent(e) } else { - fmt.Printf("[%s] [%s] %v\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e) + fmt.Fprintf(s.output, "[%s] [%s] %v\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), e) } - if refresh { + if refresh && s.output == os.Stdout { s.Session.Refresh() } } diff --git a/modules/events_view_ble.go b/modules/events_view_ble.go index 5a1c79d5..41886327 100644 --- a/modules/events_view_ble.go +++ b/modules/events_view_ble.go @@ -11,7 +11,7 @@ import ( "github.com/bettercap/bettercap/session" ) -func (s EventsStream) viewBLEEvent(e session.Event) { +func (s *EventsStream) viewBLEEvent(e session.Event) { if e.Tag == "ble.device.new" { dev := e.Data.(*network.BLEDevice) name := dev.Device.Name() @@ -23,7 +23,7 @@ func (s EventsStream) viewBLEEvent(e session.Event) { vend = fmt.Sprintf(" (%s)", core.Yellow(vend)) } - fmt.Printf("[%s] [%s] New BLE device%s detected as %s%s %s.\n", + fmt.Fprintf(s.output, "[%s] [%s] New BLE device%s detected as %s%s %s.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), name, @@ -41,14 +41,14 @@ func (s EventsStream) viewBLEEvent(e session.Event) { vend = fmt.Sprintf(" (%s)", core.Yellow(vend)) } - fmt.Printf("[%s] [%s] BLE device%s %s%s lost.\n", + fmt.Fprintf(s.output, "[%s] [%s] BLE device%s %s%s lost.\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag), name, dev.Device.ID(), vend) } /* else { - fmt.Printf("[%s] [%s]\n", + fmt.Fprintf(s.output,"[%s] [%s]\n", e.Time.Format(eventTimeFormat), core.Green(e.Tag)) } */ diff --git a/modules/events_view_ble_unsupported.go b/modules/events_view_ble_unsupported.go index 9bddc30e..11dc7e6e 100644 --- a/modules/events_view_ble_unsupported.go +++ b/modules/events_view_ble_unsupported.go @@ -6,6 +6,6 @@ import ( "github.com/bettercap/bettercap/session" ) -func (s EventsStream) viewBLEEvent(e session.Event) { +func (s *EventsStream) viewBLEEvent(e session.Event) { } diff --git a/session/prompt.go b/session/prompt.go index e93828ff..b05ff45f 100644 --- a/session/prompt.go +++ b/session/prompt.go @@ -15,23 +15,6 @@ const ( DefaultPrompt = "{by}{fw}{cidr} {fb}> {env.iface.ipv4} {reset} {bold}ยป {reset}" ) -var PromptEffects = map[string]string{ - "{bold}": core.BOLD, - "{dim}": core.DIM, - "{r}": core.RED, - "{g}": core.GREEN, - "{b}": core.BLUE, - "{y}": core.YELLOW, - "{fb}": core.FG_BLACK, - "{fw}": core.FG_WHITE, - "{bdg}": core.BG_DGRAY, - "{br}": core.BG_RED, - "{bg}": core.BG_GREEN, - "{by}": core.BG_YELLOW, - "{blb}": core.BG_LBLUE, // Ziggy this is for you <3 - "{reset}": core.RESET, -} - var PromptCallbacks = map[string]func(s *Session) string{ "{cidr}": func(s *Session) string { return s.Interface.CIDR() @@ -71,7 +54,26 @@ func (p Prompt) Render(s *Session) string { prompt = DefaultPrompt } - for tok, effect := range PromptEffects { + // these are here because if colors are disabled, + // we need the updated core.* variables + var effects = map[string]string{ + "{bold}": core.BOLD, + "{dim}": core.DIM, + "{r}": core.RED, + "{g}": core.GREEN, + "{b}": core.BLUE, + "{y}": core.YELLOW, + "{fb}": core.FG_BLACK, + "{fw}": core.FG_WHITE, + "{bdg}": core.BG_DGRAY, + "{br}": core.BG_RED, + "{bg}": core.BG_GREEN, + "{by}": core.BG_YELLOW, + "{blb}": core.BG_LBLUE, // Ziggy this is for you <3 + "{reset}": core.RESET, + } + + for tok, effect := range effects { prompt = strings.Replace(prompt, tok, effect, -1) } diff --git a/session/session.go b/session/session.go index 57381ad6..01d3e6c4 100644 --- a/session/session.go +++ b/session/session.go @@ -138,6 +138,8 @@ func New() (*Session, error) { return nil, err } + core.InitSwag(*s.Options.NoColors) + if *s.Options.CpuProfile != "" { if f, err := os.Create(*s.Options.CpuProfile); err != nil { return nil, err