diff --git a/Dockerfile b/Dockerfile index 8b23568f..d05fc8d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,13 +3,8 @@ FROM golang:1.10-alpine AS build-env ENV GOPATH=/gocode ENV SRC_DIR=/gocode/src/github.com/bettercap/bettercap -# As Alpine Linux uses a different folder, we need this -# ugly hack in order to compile gopacket statically -# https://github.com/bettercap/bettercap/issues/106 -RUN apk add --update ca-certificates && \ -apk add --no-cache --update bash iptables wireless-tools build-base libpcap-dev git python py-six && \ -mkdir -p /usr/lib/x86_64-linux-gnu/ && \ -cp /usr/lib/libpcap.a /usr/lib/x86_64-linux-gnu/libpcap.a +RUN apk add --update ca-certificates +RUN apk add --no-cache --update bash iptables wireless-tools build-base libpcap-dev git python py-six WORKDIR $SRC_DIR ADD . $SRC_DIR diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 95326b50..ff3d01ea 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,11 +1,9 @@ -### Prerequisites +# Prerequisites -* [ ] I read the [README](https://github.com/bettercap/bettercap/blob/master/README.md). -* [ ] I am running the [latest stable version](https://github.com/bettercap/bettercap/releases). -* [ ] I already searched [other issues](https://github.com/bettercap/bettercap/issues?q=is%3Aopen+is%3Aissue+label%3Abug) to see if this problem was already reported. -* [ ] I understand I don't necessarily have to paste this `Prerequisites` section in the issue. +Please, before creating this issue make sure that you read the [README](https://github.com/bettercap/bettercap/blob/master/README.md), that you are running the [latest stable version](https://github.com/bettercap/bettercap/releases) and that you already searched [other issues](https://github.com/bettercap/bettercap/issues?q=is%3Aopen+is%3Aissue+label%3Abug) to see if your problem or request was already reported. -### Description +! PLEASE REMOVE THIS PART AND LEAVE ONLY THE FOLLOWING SECTIONS IN YOUR REPORT ! +--- *Description of the bug or feature request* @@ -17,8 +15,7 @@ Please provide: * OS version and architecture you are using. * Go version if building from sources. * Command line arguments you are using. -* Caplet code you are using (if any). -* Interactive session commands you are using (if any). +* Caplet code you are using or the interactive session commands. * **Full debug output** while reproducing the issue ( `bettercap -debug ...` ). ### Steps to Reproduce diff --git a/build.sh b/build.sh index 110a2f06..d90ad51f 100755 --- a/build.sh +++ b/build.sh @@ -13,6 +13,16 @@ host_dep() { ping -c 1 $HOST > /dev/null || { echo "@ Virtual machine host $HOST not visible !"; exit 1; } } +create_exe_archive() { + bin_dep 'zip' + + OUTPUT=$1 + + echo "@ Creating archive $OUTPUT ..." + zip -j "$OUTPUT" bettercap.exe ../README.md ../LICENSE.md > /dev/null + rm -rf bettercap bettercap.exe +} + create_archive() { bin_dep 'zip' @@ -172,19 +182,17 @@ mkdir $BUILD_FOLDER cd $BUILD_FOLDER -build_linux_amd64 # && create_archive bettercap_linux_amd64_$VERSION.zip -# build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip -# build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip -# build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip -# build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip -# build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip -# build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip -# -# # these are still not static :( -# build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip -# build_android_arm && create_archive bettercap_android_arm_$VERSION.zip -# build_windows_amd64 && create_archive bettercap_windows_amd64_$VERSION.zip -# sha256sum * > checksums.txt +build_linux_amd64 && create_archive bettercap_linux_amd64_$VERSION.zip +build_macos_amd64 && create_archive bettercap_macos_amd64_$VERSION.zip +build_android_arm && create_archive bettercap_android_arm_$VERSION.zip +build_windows_amd64 && create_exe_archive bettercap_windows_amd64_$VERSION.zip +build_linux_arm7_static && create_archive bettercap_linux_arm7_$VERSION.zip +build_linux_arm7hf_static && create_archive bettercap_linux_arm7hf_$VERSION.zip +build_linux_mips_static && create_archive bettercap_linux_mips_$VERSION.zip +build_linux_mipsle_static && create_archive bettercap_linux_mipsle_$VERSION.zip +build_linux_mips64_static && create_archive bettercap_linux_mips64_$VERSION.zip +build_linux_mips64le_static && create_archive bettercap_linux_mips64le_$VERSION.zip +sha256sum * > checksums.txt echo echo diff --git a/core/core.go b/core/core.go index 2a212c03..5706001a 100644 --- a/core/core.go +++ b/core/core.go @@ -41,6 +41,17 @@ func UniqueInts(a []int, sorted bool) []int { return uniq } +func CommaSplit(csv string) []string { + filtered := make([]string, 0) + for _, part := range strings.Split(csv, ",") { + part = Trim(part) + if part != "" { + filtered = append(filtered, part) + } + } + return filtered +} + func ExecSilent(executable string, args []string) (string, error) { path, err := exec.LookPath(executable) if err != nil { diff --git a/core/options.go b/core/options.go index 887b2901..261bf172 100644 --- a/core/options.go +++ b/core/options.go @@ -5,8 +5,10 @@ import "flag" type Options struct { InterfaceName *string Caplet *string + AutoStart *string Debug *bool Silent *bool + NoColors *bool NoHistory *bool EnvFile *string Commands *string @@ -17,9 +19,11 @@ type Options struct { func ParseOptions() (Options, error) { o := Options{ InterfaceName: flag.String("iface", "", "Network interface to bind to, if empty the default interface will be auto selected."), + AutoStart: flag.String("autostart", "events.stream, net.recon", "Comma separated list of modules to auto start."), 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 effects."), 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..9dc1e5a6 100644 --- a/main.go +++ b/main.go @@ -14,8 +14,6 @@ import ( var sess *session.Session var err error -// Some modules are enabled by default in order -// to make the interactive session useful. var autoEnableList = []string{ "events.stream", "net.recon", @@ -26,9 +24,14 @@ func main() { fmt.Println(err) os.Exit(1) } + defer sess.Close() - 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 +62,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 +74,14 @@ func main() { } } + // Some modules are enabled by default in order + // to make the interactive session useful. + for _, modName := range core.CommaSplit(*sess.Options.AutoStart) { + if err = sess.Run(modName + " on"); err != nil { + log.Fatal("Error while starting module %s: %s", modName, err) + } + } + // Then run the caplet if specified. if *sess.Options.Caplet != "" { if err = sess.RunCaplet(*sess.Options.Caplet); err != nil { @@ -100,9 +105,4 @@ func main() { } } } - - sess.Close() - - // Windows requires this otherwise the app never exits ... - os.Exit(0) } diff --git a/modules/api_rest.go b/modules/api_rest.go index 920fd044..f54ebdaf 100644 --- a/modules/api_rest.go +++ b/modules/api_rest.go @@ -11,6 +11,7 @@ import ( "github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/tls" + "github.com/gorilla/mux" "github.com/gorilla/websocket" ) @@ -146,10 +147,22 @@ func (api *RestAPI) Configure() error { api.server.Addr = fmt.Sprintf("%s:%d", ip, port) - router := http.NewServeMux() + router := mux.NewRouter() - router.HandleFunc("/api/session", api.sessionRoute) router.HandleFunc("/api/events", api.eventsRoute) + router.HandleFunc("/api/session", api.sessionRoute) + router.HandleFunc("/api/session/ble", api.sessionRoute) + router.HandleFunc("/api/session/ble/{mac}", api.sessionRoute) + router.HandleFunc("/api/session/env", api.sessionRoute) + router.HandleFunc("/api/session/gateway", api.sessionRoute) + router.HandleFunc("/api/session/interface", api.sessionRoute) + router.HandleFunc("/api/session/lan", api.sessionRoute) + router.HandleFunc("/api/session/lan/{mac}", api.sessionRoute) + router.HandleFunc("/api/session/options", api.sessionRoute) + router.HandleFunc("/api/session/packets", api.sessionRoute) + router.HandleFunc("/api/session/started-at", api.sessionRoute) + router.HandleFunc("/api/session/wifi", api.sessionRoute) + router.HandleFunc("/api/session/wifi/{mac}", api.sessionRoute) api.server.Handler = router diff --git a/modules/api_rest_controller.go b/modules/api_rest_controller.go index 50e7caac..71315e6c 100644 --- a/modules/api_rest_controller.go +++ b/modules/api_rest_controller.go @@ -6,21 +6,11 @@ import ( "net/http" "strconv" "strings" - "time" "github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/session" - "github.com/gorilla/websocket" -) - -const ( - // Time allowed to write an event to the client. - writeWait = 10 * time.Second - // Time allowed to read the next pong message from the client. - pongWait = 60 * time.Second - // Send pings to client with this period. Must be less than pongWait. - pingPeriod = (pongWait * 9) / 10 + "github.com/gorilla/mux" ) type CommandRequest struct { @@ -67,6 +57,71 @@ func (api *RestAPI) showSession(w http.ResponseWriter, r *http.Request) { toJSON(w, session.I) } +func (api *RestAPI) showBle(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + mac := strings.ToLower(params["mac"]) + + if mac == "" { + toJSON(w, session.I.BLE) + } else if dev, found := session.I.BLE.Get(mac); found == true { + toJSON(w, dev) + } else { + http.Error(w, "Not Found", 404) + } +} + +func (api *RestAPI) showEnv(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.Env) +} + +func (api *RestAPI) showGateway(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.Gateway) +} + +func (api *RestAPI) showInterface(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.Interface) +} + +func (api *RestAPI) showLan(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + mac := strings.ToLower(params["mac"]) + + if mac == "" { + toJSON(w, session.I.Lan) + } else if host, found := session.I.Lan.Get(mac); found == true { + toJSON(w, host) + } else { + http.Error(w, "Not Found", 404) + } +} + +func (api *RestAPI) showOptions(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.Options) +} + +func (api *RestAPI) showPackets(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.Queue) +} + +func (api *RestAPI) showStartedAt(w http.ResponseWriter, r *http.Request) { + toJSON(w, session.I.StartedAt) +} + +func (api *RestAPI) showWiFi(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + mac := strings.ToLower(params["mac"]) + + if mac == "" { + toJSON(w, session.I.WiFi) + } else if station, found := session.I.WiFi.Get(mac); found == true { + toJSON(w, station) + } else if client, found := session.I.WiFi.GetClient(mac); found == true { + toJSON(w, client) + } else { + http.Error(w, "Not Found", 404) + } +} + func (api *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) { var err error var cmd CommandRequest @@ -82,103 +137,12 @@ func (api *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) { } } -func (api *RestAPI) streamEvent(ws *websocket.Conn, event session.Event) error { - msg, err := json.Marshal(event) - if err != nil { - log.Error("Error while creating websocket message: %s", err) - return err - } - - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.TextMessage, msg); err != nil { - if !strings.Contains(err.Error(), "closed connection") { - log.Error("Error while writing websocket message: %s", err) - return err - } - } - - return nil -} - -func (api *RestAPI) sendPing(ws *websocket.Conn) error { - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { - log.Error("Error while writing websocket ping message: %s", err) - return err - } - return nil -} - -func (api *RestAPI) streamWriter(ws *websocket.Conn, w http.ResponseWriter, r *http.Request) { - defer ws.Close() - - // first we stream what we already have - events := session.I.Events.Sorted() - n := len(events) - if n > 0 { - log.Debug("Sending %d events.", n) - for _, event := range events { - if err := api.streamEvent(ws, event); err != nil { - return - } - } - } - - session.I.Events.Clear() - - log.Debug("Listening for events and streaming to ws endpoint ...") - - pingTicker := time.NewTicker(pingPeriod) - - for { - select { - case <-pingTicker.C: - if err := api.sendPing(ws); err != nil { - return - } - case event := <-api.eventListener: - if err := api.streamEvent(ws, event); err != nil { - return - } - case <-api.quit: - log.Info("Stopping websocket events streamer ...") - return - } - } -} - -func (api *RestAPI) streamReader(ws *websocket.Conn) { - defer ws.Close() - ws.SetReadLimit(512) - ws.SetReadDeadline(time.Now().Add(pongWait)) - ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) - for { - _, _, err := ws.ReadMessage() - if err != nil { - log.Debug("Closing websocket reader.") - break - } - } -} - func (api *RestAPI) showEvents(w http.ResponseWriter, r *http.Request) { var err error if api.useWebsocket { - ws, err := api.upgrader.Upgrade(w, r, nil) - if err != nil { - if _, ok := err.(websocket.HandshakeError); !ok { - log.Error("Error while updating api.rest connection to websocket: %s", err) - } - return - } - - log.Debug("Websocket streaming started for %s", r.RemoteAddr) - - go api.streamWriter(ws, w, r) - api.streamReader(ws) + api.startStreamingEvents(w, r) } else { - events := session.I.Events.Sorted() nevents := len(events) nmax := nevents @@ -210,12 +174,49 @@ func (api *RestAPI) sessionRoute(w http.ResponseWriter, r *http.Request) { if api.checkAuth(r) == false { setAuthFailed(w, r) - } else if r.Method == "GET" { - api.showSession(w, r) + return } else if r.Method == "POST" { api.runSessionCommand(w, r) - } else { + return + } else if r.Method != "GET" { http.Error(w, "Bad Request", 400) + return + } + + path := r.URL.String() + switch { + case path == "/api/session": + api.showSession(w, r) + + case path == "/api/session/env": + api.showEnv(w, r) + + case path == "/api/session/gateway": + api.showGateway(w, r) + + case path == "/api/session/interface": + api.showInterface(w, r) + + case strings.HasPrefix(path, "/api/session/lan"): + api.showLan(w, r) + + case path == "/api/session/options": + api.showOptions(w, r) + + case path == "/api/session/packets": + api.showPackets(w, r) + + case path == "/api/session/started-at": + api.showStartedAt(w, r) + + case strings.HasPrefix(path, "/api/session/ble"): + api.showBle(w, r) + + case strings.HasPrefix(path, "/api/session/wifi"): + api.showWiFi(w, r) + + default: + http.Error(w, "Not Found", 404) } } @@ -224,7 +225,10 @@ func (api *RestAPI) eventsRoute(w http.ResponseWriter, r *http.Request) { if api.checkAuth(r) == false { setAuthFailed(w, r) - } else if r.Method == "GET" { + return + } + + if r.Method == "GET" { api.showEvents(w, r) } else if r.Method == "DELETE" { api.clearEvents(w, r) diff --git a/modules/api_rest_ws.go b/modules/api_rest_ws.go new file mode 100644 index 00000000..b32a8bed --- /dev/null +++ b/modules/api_rest_ws.go @@ -0,0 +1,116 @@ +package modules + +import ( + "encoding/json" + "net/http" + "strings" + "time" + + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/session" + + "github.com/gorilla/websocket" +) + +const ( + // Time allowed to write an event to the client. + writeWait = 10 * time.Second + // Time allowed to read the next pong message from the client. + pongWait = 60 * time.Second + // Send pings to client with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 +) + +func (api *RestAPI) streamEvent(ws *websocket.Conn, event session.Event) error { + msg, err := json.Marshal(event) + if err != nil { + log.Error("Error while creating websocket message: %s", err) + return err + } + + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, msg); err != nil { + if !strings.Contains(err.Error(), "closed connection") { + log.Error("Error while writing websocket message: %s", err) + return err + } + } + + return nil +} + +func (api *RestAPI) sendPing(ws *websocket.Conn) error { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + log.Error("Error while writing websocket ping message: %s", err) + return err + } + return nil +} + +func (api *RestAPI) streamWriter(ws *websocket.Conn, w http.ResponseWriter, r *http.Request) { + defer ws.Close() + + // first we stream what we already have + events := session.I.Events.Sorted() + n := len(events) + if n > 0 { + log.Debug("Sending %d events.", n) + for _, event := range events { + if err := api.streamEvent(ws, event); err != nil { + return + } + } + } + + session.I.Events.Clear() + + log.Debug("Listening for events and streaming to ws endpoint ...") + + pingTicker := time.NewTicker(pingPeriod) + + for { + select { + case <-pingTicker.C: + if err := api.sendPing(ws); err != nil { + return + } + case event := <-api.eventListener: + if err := api.streamEvent(ws, event); err != nil { + return + } + case <-api.quit: + log.Info("Stopping websocket events streamer ...") + return + } + } +} + +func (api *RestAPI) streamReader(ws *websocket.Conn) { + defer ws.Close() + ws.SetReadLimit(512) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, _, err := ws.ReadMessage() + if err != nil { + log.Debug("Closing websocket reader.") + break + } + } +} + +func (api *RestAPI) startStreamingEvents(w http.ResponseWriter, r *http.Request) { + ws, err := api.upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Error("Error while updating api.rest connection to websocket: %s", err) + } + return + } + + log.Debug("Websocket streaming started for %s", r.RemoteAddr) + + go api.streamWriter(ws, w, r) + api.streamReader(ws) +} 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/modules/wifi_deauth.go b/modules/wifi_deauth.go index f5ca6247..602701a0 100644 --- a/modules/wifi_deauth.go +++ b/modules/wifi_deauth.go @@ -6,6 +6,7 @@ import ( "time" "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/network" "github.com/bettercap/bettercap/packets" ) @@ -62,7 +63,7 @@ func (w *WiFiModule) startDeauth(to net.HardwareAddr) error { if ap, found := w.Session.WiFi.Get(bssid); found == true { clients := ap.Clients() log.Info("Deauthing %d clients from AP %s ...", len(clients), ap.ESSID()) - w.onChannel(mhz2chan(ap.Frequency), func() { + w.onChannel(network.Dot11Freq2Chan(ap.Frequency), func() { for _, c := range clients { if w.Running() == false { break @@ -81,7 +82,7 @@ func (w *WiFiModule) startDeauth(to net.HardwareAddr) error { break } else if c, found := ap.Get(bssid); found == true { log.Info("Deauthing client %s from AP %s ...", c.HwAddress, ap.ESSID()) - w.onChannel(mhz2chan(ap.Frequency), func() { + w.onChannel(network.Dot11Freq2Chan(ap.Frequency), func() { w.sendDeauthPacket(ap.HW, c.HW) }) return nil diff --git a/modules/wifi_hopping.go b/modules/wifi_hopping.go new file mode 100644 index 00000000..6e0fddff --- /dev/null +++ b/modules/wifi_hopping.go @@ -0,0 +1,58 @@ +package modules + +import ( + "time" + + "github.com/bettercap/bettercap/log" + "github.com/bettercap/bettercap/network" +) + +func (w *WiFiModule) onChannel(channel int, cb func()) { + prev := w.stickChan + w.stickChan = channel + + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { + log.Warning("Error while hopping to channel %d: %s", channel, err) + } else { + log.Debug("Hopped on channel %d", channel) + } + + cb() + + w.stickChan = prev +} + +func (w *WiFiModule) channelHopper() { + w.reads.Add(1) + defer w.reads.Done() + + log.Info("Channel hopper started.") + for w.Running() == true { + delay := w.hopPeriod + // if we have both 2.4 and 5ghz capabilities, we have + // more channels, therefore we need to increase the time + // we hop on each one otherwise me lose information + if len(w.frequencies) > 14 { + delay = delay * 2 + } + + frequencies := w.frequencies + for _, frequency := range frequencies { + channel := network.Dot11Freq2Chan(frequency) + // stick to the access point channel as long as it's selected + // or as long as we're deauthing on it + if w.stickChan != 0 { + channel = w.stickChan + } + + if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { + log.Warning("Error while hopping to channel %d: %s", channel, err) + } + + time.Sleep(delay) + if w.Running() == false { + return + } + } + } +} diff --git a/modules/wifi_recon.go b/modules/wifi_recon.go index f6e56cdc..965b3959 100644 --- a/modules/wifi_recon.go +++ b/modules/wifi_recon.go @@ -3,6 +3,8 @@ package modules import ( "fmt" "net" + "strconv" + "strings" "sync" "time" @@ -30,16 +32,18 @@ type WiFiProbe struct { type WiFiModule struct { session.SessionModule - handle *pcap.Handle - channel int - hopPeriod time.Duration - frequencies []int - ap *network.AccessPoint - stickChan int - skipBroken bool - pktSourceChan chan gopacket.Packet - writes *sync.WaitGroup - reads *sync.WaitGroup + handle *pcap.Handle + source string + channel int + hopPeriod time.Duration + frequencies []int + ap *network.AccessPoint + stickChan int + skipBroken bool + pktSourceChan chan gopacket.Packet + pktSourceChanClosed bool + writes *sync.WaitGroup + reads *sync.WaitGroup } func NewWiFiModule(s *session.Session) *WiFiModule { @@ -74,7 +78,7 @@ func NewWiFiModule(s *session.Session) *WiFiModule { return err } else if ap, found := w.Session.WiFi.Get(bssid.String()); found == true { w.ap = ap - w.stickChan = mhz2chan(ap.Frequency) + w.stickChan = network.Dot11Freq2Chan(ap.Frequency) return nil } return fmt.Errorf("Could not find station with BSSID %s", args[0]) @@ -85,6 +89,10 @@ func NewWiFiModule(s *session.Session) *WiFiModule { func(args []string) error { w.ap = nil w.stickChan = 0 + var err error + if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } return nil })) @@ -110,13 +118,43 @@ func NewWiFiModule(s *session.Session) *WiFiModule { return w.Show("rssi") })) - w.AddParam(session.NewIntParameter("wifi.recon.channel", + w.AddHandler(session.NewModuleHandler("wifi.recon.channel", `wifi\.recon\.channel[\s]+([0-9]+(?:[, ]+[0-9]+)*|clear)`, + "WiFi channels (comma separated) or 'clear' for channel hopping.", + func(args []string) error { + newfrequencies := w.frequencies[:0] + + if len(args) > 0 && args[0] != "clear" { + channels := strings.Split(args[0], ",") + for _, c := range channels { + trimmed := strings.Trim(c, " ") + channel, err := strconv.Atoi(trimmed) + if err != nil { + return err + } + newfrequencies = append(newfrequencies, network.Dot11Chan2Freq(channel)) + } + } else { + // No channels setted, retrieve frequencies supported by the card + if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } else { + newfrequencies = frequencies + } + } + + w.frequencies = newfrequencies + + return nil + })) + + w.AddParam(session.NewStringParameter("wifi.source.file", "", - "WiFi channel or empty for channel hopping.")) + "", + "If set, the wifi module will read from this pcap file instead of the hardware interface.")) w.AddParam(session.NewIntParameter("wifi.hop.period", "250", - "If channel hopping is enabled (empty wifi.recon.channel), this is the time in millseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available).")) + "If channel hopping is enabled (empty wifi.recon.channel), this is the time in milliseconds the algorithm will hop on every channel (it'll be doubled if both 2.4 and 5.0 bands are available).")) w.AddParam(session.NewBoolParameter("wifi.skip-broken", "true", @@ -137,35 +175,34 @@ func (w WiFiModule) Author() string { return "Gianluca Braga && Simone Margaritelli >" } -func mhz2chan(freq int) int { - // ambo! - if freq <= 2472 { - return ((freq - 2412) / 5) + 1 - } else if freq == 2484 { - return 14 - } else if freq >= 5035 && freq <= 5865 { - return ((freq - 5035) / 5) + 7 - } - return 0 -} - func (w *WiFiModule) Configure() error { var hopPeriod int + var err error - ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name()) - if err != nil { + if err, w.source = w.StringParam("wifi.source.file"); err != nil { return err } - defer ihandle.CleanUp() - if err = ihandle.SetRFMon(true); err != nil { - return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err) - } else if err = ihandle.SetSnapLen(65536); err != nil { - return err - } else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil { - return err - } else if w.handle, err = ihandle.Activate(); err != nil { - return err + if w.source != "" { + if w.handle, err = pcap.OpenOffline(w.source); err != nil { + return err + } + } else { + ihandle, err := pcap.NewInactiveHandle(w.Session.Interface.Name()) + if err != nil { + return err + } + defer ihandle.CleanUp() + + if err = ihandle.SetRFMon(true); err != nil { + return fmt.Errorf("Error while setting interface %s in monitor mode: %s", core.Bold(w.Session.Interface.Name()), err) + } else if err = ihandle.SetSnapLen(65536); err != nil { + return err + } else if err = ihandle.SetTimeout(pcap.BlockForever); err != nil { + return err + } else if w.handle, err = ihandle.Activate(); err != nil { + return err + } } if err, w.skipBroken = w.BoolParam("wifi.skip-broken"); err != nil { @@ -176,69 +213,39 @@ func (w *WiFiModule) Configure() error { w.hopPeriod = time.Duration(hopPeriod) * time.Millisecond - if err, w.channel = w.IntParam("wifi.recon.channel"); err == nil { - if err = network.SetInterfaceChannel(w.Session.Interface.Name(), w.channel); err != nil { - return err - } - log.Info("WiFi recon active on channel %d.", w.channel) - } else { - w.channel = 0 - // we need to start somewhere, this is just to check if - // this OS supports switching channel programmatically. - if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil { - return err - } - log.Info("WiFi recon active with channel hopping.") - } + if w.source == "" { + // No channels setted, retrieve frequencies supported by the card + if len(w.frequencies) == 0 { + if w.frequencies, err = network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { + return err + } - if frequencies, err := network.GetSupportedFrequencies(w.Session.Interface.Name()); err != nil { - return err - } else { - w.frequencies = frequencies + // we need to start somewhere, this is just to check if + // this OS supports switching channel programmatically. + if err = network.SetInterfaceChannel(w.Session.Interface.Name(), 1); err != nil { + return err + } + log.Info("WiFi recon active with channel hopping.") + } } return nil } -func (w *WiFiModule) onChannel(channel int, cb func()) { - prev := w.stickChan - w.stickChan = channel - - if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { - log.Warning("Error while hopping to channel %d: %s", channel, err) - } else { - log.Debug("Hopped on channel %d", channel) - } - - cb() - - w.stickChan = prev -} - -func isZeroBSSID(bssid net.HardwareAddr) bool { - for _, b := range bssid { - if b != 0x00 { - return false - } - } - return true -} - -func isBroadcastBSSID(bssid net.HardwareAddr) bool { - for _, b := range bssid { - if b != 0xff { - return false - } - } - return true -} - func (w *WiFiModule) discoverAccessPoints(radiotap *layers.RadioTap, dot11 *layers.Dot11, packet gopacket.Packet) { // search for Dot11InformationElementIDSSID if ok, ssid := packets.Dot11ParseIDSSID(packet); ok == true { - if isZeroBSSID(dot11.Address3) == false && isBroadcastBSSID(dot11.Address3) == false { - bssid := dot11.Address3.String() - frequency := int(radiotap.ChannelFrequency) + from := dot11.Address3 + if network.IsZeroMac(from) == false && network.IsBroadcastMac(from) == false { + var frequency int + bssid := from.String() + + if found, channel := packets.Dot11ParseDSSet(packet); found { + frequency = network.Dot11Chan2Freq(channel) + } else { + frequency = int(radiotap.ChannelFrequency) + } + w.Session.WiFi.AddIfNew(ssid, bssid, frequency, radiotap.DBMAntennaSignal) } } @@ -317,40 +324,6 @@ func (w *WiFiModule) updateStats(dot11 *layers.Dot11, packet gopacket.Packet) { } } -func (w *WiFiModule) channelHopper() { - w.reads.Add(1) - defer w.reads.Done() - - log.Info("Channel hopper started.") - for w.Running() == true { - delay := w.hopPeriod - // if we have both 2.4 and 5ghz capabilities, we have - // more channels, therefore we need to increase the time - // we hop on each one otherwise me lose information - if len(w.frequencies) > 14 { - delay = 500 * time.Millisecond - } - - for _, frequency := range w.frequencies { - channel := mhz2chan(frequency) - // stick to the access point channel as long as it's selected - // or as long as we're deauthing on it - if w.stickChan != 0 { - channel = w.stickChan - } - - if err := network.SetInterfaceChannel(w.Session.Interface.Name(), channel); err != nil { - log.Warning("Error while hopping to channel %d: %s", channel, err) - } - - time.Sleep(delay) - if w.Running() == false { - return - } - } - } -} - func (w *WiFiModule) stationPruner() { w.reads.Add(1) defer w.reads.Done() @@ -386,7 +359,7 @@ func (w *WiFiModule) Start() error { w.SetRunning(true, func() { // start channel hopper if needed - if w.channel == 0 { + if w.channel == 0 && w.source == "" { go w.channelHopper() } @@ -423,6 +396,7 @@ func (w *WiFiModule) Start() error { w.updateStats(dot11, packet) } } + w.pktSourceChanClosed = true }) return nil @@ -433,7 +407,9 @@ func (w *WiFiModule) Stop() error { // wait any pending write operation w.writes.Wait() // signal the main for loop we want to exit - w.pktSourceChan <- nil + if w.pktSourceChanClosed == false { + w.pktSourceChan <- nil + } // close the pcap handle to make the main for exit w.handle.Close() // close the pcap handle to make the main for exit diff --git a/modules/wifi_show.go b/modules/wifi_show.go index f84ed639..827e4b92 100644 --- a/modules/wifi_show.go +++ b/modules/wifi_show.go @@ -17,7 +17,8 @@ func (w *WiFiModule) isApSelected() bool { return w.ap != nil } -func (w *WiFiModule) getRow(station *network.Station) []string { +func (w *WiFiModule) getRow(station *network.Station) ([]string, bool) { + include := false sinceStarted := time.Since(w.Session.StartedAt) sinceFirstSeen := time.Since(station.FirstSeen) @@ -61,16 +62,27 @@ func (w *WiFiModule) getRow(station *network.Station) []string { recvd = humanize.Bytes(station.Received) } + if w.source == "" { + for _, frequencies := range w.frequencies { + if frequencies == station.Frequency { + include = true + break + } + } + } else { + include = true + } + if w.isApSelected() { return []string{ fmt.Sprintf("%d dBm", station.RSSI), bssid, /* station.Vendor, */ - strconv.Itoa(mhz2chan(station.Frequency)), + strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)), sent, recvd, seen, - } + }, include } else { // this is ugly, but necessary in order to have this // method handle both access point and clients @@ -88,12 +100,12 @@ func (w *WiFiModule) getRow(station *network.Station) []string { ssid, /* station.Vendor, */ encryption, - strconv.Itoa(mhz2chan(station.Frequency)), + strconv.Itoa(network.Dot11Freq2Chan(station.Frequency)), clients, sent, recvd, seen, - } + }, include } } @@ -123,7 +135,9 @@ func (w *WiFiModule) Show(by string) error { rows := make([][]string, 0) for _, s := range stations { - rows = append(rows, w.getRow(s)) + if row, include := w.getRow(s); include == true { + rows = append(rows, row) + } } nrows := len(rows) diff --git a/network/net.go b/network/net.go index 1c463aec..bc85ec82 100644 --- a/network/net.go +++ b/network/net.go @@ -26,6 +26,24 @@ var ( IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$") ) +func IsZeroMac(mac net.HardwareAddr) bool { + for _, b := range mac { + if b != 0x00 { + return false + } + } + return true +} + +func IsBroadcastMac(mac net.HardwareAddr) bool { + for _, b := range mac { + if b != 0xff { + return false + } + } + return true +} + func NormalizeMac(mac string) string { var parts []string if strings.ContainsRune(mac, '-') { diff --git a/network/wifi.go b/network/wifi.go index c7ba2481..64ec588d 100644 --- a/network/wifi.go +++ b/network/wifi.go @@ -7,6 +7,29 @@ import ( "time" ) +func Dot11Freq2Chan(freq int) int { + if freq <= 2472 { + return ((freq - 2412) / 5) + 1 + } else if freq == 2484 { + return 14 + } else if freq >= 5035 && freq <= 5865 { + return ((freq - 5035) / 5) + 7 + } + return 0 +} + +func Dot11Chan2Freq(channel int) int { + if channel <= 13 { + return ((channel - 1) * 5) + 2412 + } else if channel == 14 { + return 2484 + } else if channel <= 173 { + return ((channel - 7) * 5) + 5035 + } + + return 0 +} + type APNewCallback func(ap *AccessPoint) type APLostCallback func(ap *AccessPoint) @@ -133,6 +156,20 @@ func (w *WiFi) Get(mac string) (*AccessPoint, bool) { return ap, found } +func (w *WiFi) GetClient(mac string) (*Station, bool) { + w.Lock() + defer w.Unlock() + + mac = NormalizeMac(mac) + for _, ap := range w.aps { + if client, found := ap.Get(mac); found == true { + return client, true + } + } + + return nil, false +} + func (w *WiFi) Clear() error { w.aps = make(map[string]*AccessPoint) return nil diff --git a/packets/dot11.go b/packets/dot11.go index 919b9108..ae27126b 100644 --- a/packets/dot11.go +++ b/packets/dot11.go @@ -126,3 +126,20 @@ func Dot11IsDataFor(dot11 *layers.Dot11, station net.HardwareAddr) bool { // packet going to this specific BSSID? return bytes.Compare(dot11.Address1, station) == 0 } + +func Dot11ParseDSSet(packet gopacket.Packet) (bool, int) { + channel := 0 + found := false + for _, layer := range packet.Layers() { + info, ok := layer.(*layers.Dot11InformationElement) + if ok == true { + if info.ID == layers.Dot11InformationElementIDDSSet { + channel, _ = Dot11InformationElementIDDSSetDecode(info.Info) + found = true + break + } + } + } + + return found, channel +} diff --git a/packets/dot11_types.go b/packets/dot11_types.go index bd85d2e1..6df708af 100644 --- a/packets/dot11_types.go +++ b/packets/dot11_types.go @@ -211,3 +211,11 @@ func Dot11InformationElementRSNInfoDecode(buf []byte) (rsn RSNInfo, err error) { return } + +func Dot11InformationElementIDDSSetDecode(buf []byte) (channel int, err error) { + if err = canParse("DSSet.channel", buf, 1); err == nil { + channel = int(buf[0]) + } + + return +} 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