diff --git a/cmd.go b/cmd.go new file mode 100644 index 0000000..5fdbc85 --- /dev/null +++ b/cmd.go @@ -0,0 +1,289 @@ +package main + +import ( + "fmt" + cli "github.com/c-bata/go-prompt" + . "github.com/logrusorgru/aurora" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" + "protomolecule/src/eros" + projVars "protomolecule/src/vars" + "strings" +) + +var suggestions = []cli.Suggest{ + // Main Commands + // {"scan", + // "Start and stop scans"}, + + {"scan start", + "Start default scan"}, + + {"scan start attack", + "Start scan that also attempts to connect to all discovered BLE devices"}, + + {"scan start tracking", + "Start continuous RSSI tracking of all BLE devices within range"}, + + {"eros recall mac", + "Retrieve device info from database by MAC address"}, + + {"eros recall all", + "Retrieve info on all devices"}, + /* + {"", + ""}, + */ + {"exit", + "Exit ProtoMolecule"}, + + {"debug", + "Toggle debug messages"}, + + {"help", + "in-app documentation"}, +} + +func completer(in cli.Document) []cli.Suggest { + c := in.GetWordBeforeCursor() + if c == "" { + return []cli.Suggest{} + } + + return cli.FilterHasPrefix(suggestions, c, true) +} + +// commands that we will list when the user runs "help" +var cmds = []string{ + "scan", + "adapter", +} + +// Interpret is where we will actuall define our commands +func executor(cmd string) { + cmd = strings.TrimSpace(cmd) + + var args []string + args = strings.Split(cmd, " ") + + getArgs := func(args []string) string { + var ret string + for i, a := range args { + if i == 0 { + ret = a + continue + } + ret = ret + " " + a + if i != len(args)-1 { + ret = ret + " " + } + } + return ret + } + + switch args[0] { + + case "exit": + fallthrough + + case "quit": + eros.Slumber() + os.Exit(0) + + case "debug": + if projVars.Debug { + zerolog.SetGlobalLevel(zerolog.InfoLevel) + projVars.Debug = false + log.Info(). + Msg("Debug mode turned " + Sprintf(BrightRed("OFF\n").Bold())) + } else { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + projVars.Debug = true + log.Debug(). + Msg("Debug mode turned " + Sprintf(BrightGreen("ON\n").Bold())) + } + + case "eros": + if len(args) < 2 { + getHelp("eros") + return + } + + switch args[1] { + case "recall": + if len(args) < 3 { + getHelp("eros recall") + return + } + switch args[2] { + case "mac": + if len(args) < 4 { + getHelp("eros recall") + return + } + fromEros, err := eros.Recall(args[3]) + if err != nil { + log.Error().Err(err).Str("target", args[3]). + Msg("Failed to recall MAC") + println() + return + } + + log.Info(). + Str("Name", fromEros.Name). + Str("Addr", fromEros.Addr). + Str("ManuF", fromEros.Manufacturer). //needs to be changed back to fromEros.Manufacturer + Int16("Current_RSSI", fromEros.RSSIlast). + Int("Service_Count", len(fromEros.Services)). + Msg("EROS_RECALL") + } + } + + case "help": + if len(args) < 2 { + getHelp("meta") + return + } + + getHelp(getArgs(args)) + + case "loadnames": + if len(args) < 2 { + log.Error().Msg("missing argument: path to json file with manufacturer UUIDs") + println() + } + + projVars.ManuFile = args[1] + eros.ManufLoad() + + case "adapter": + if len(args) > 1 { + switch args[1] { + case "list": + fmt.Println(getArgs(args) + ": list available bluetooth adapters") + case "use": + fmt.Println(getArgs(args) + ": select given adapter for usage") + default: + log.Error().Msg("unknown sub-command: " + args[1]) + println() + } + } else { + getHelp(getArgs(args)) + } + + /* + case "mode": + + case "attack": + if len(args) > 1 { + switch args[1] { + */ + + case "scan": + if len(args) > 1 { + switch args[1] { + + case "list": + fallthrough + + case "status": + fmt.Println(getArgs(args) + ": start new default BLE scan") + + case "start": + if len(args) > 2 { + switch args[2] { + case "tracking": + projVars.TrackingMode = true + projVars.AttackMode = false + case "attack": + projVars.TrackingMode = false + projVars.AttackMode = true + default: + // getHelp(getArgs(args)) + log.Error().Msg("unknown sub command: " + args[2]) + println() + } + } + + bleScan() + } + + } else { + getHelp(getArgs(args)) + } + + case "rpc": + if len(args) > 1 { + switch args[1] { + + case "status": + fmt.Println(getArgs(args) + ": ") + + case "server": + fmt.Println(getArgs(args) + ": ") + + case "client": + fmt.Println(getArgs(args) + ": ") + } + } else { + getHelp(getArgs(args)) + } + + case "clear": + print("\033[H\033[2J") + //termenv.ClearScreen() + + default: + log.Error().Msg("unknown command: " + cmd) + println() + } + +} + +func getHelp(target string) { + var lines []string + + lines = append(lines, "help: "+target) + + switch target { + + case "meta": + var list string + for _, cmd := range cmds { + list = list + cmd + ", " + } + + fmt.Println("Enabled commands: ") + list = strings.TrimSuffix(list, ", ") + fmt.Println(list) + fmt.Println() + + default: + log.Error().Msg("Help entry not found!") + fmt.Println() + } + +} + +func StartCLI() { + var hist []string + + p := cli.New( + executor, + completer, + cli.OptionPrefix("ProtoMolecule"+"> "), + //cli.OptionPrefixBackgroundColor(cli.Black), + cli.OptionPrefixTextColor(cli.Cyan), + cli.OptionHistory(hist), + cli.OptionSuggestionBGColor(cli.Black), + cli.OptionSuggestionTextColor(cli.White), + cli.OptionSelectedSuggestionBGColor(cli.Black), + cli.OptionSelectedSuggestionTextColor(cli.Cyan), + //prompt.OptionLivePrefix + cli.OptionTitle("ProtoMolecule"), + ) + + p.Run() + +} diff --git a/src/protogen/ble_scan.go b/src/protogen/ble_scan.go index 378c9f7..aa217bc 100644 --- a/src/protogen/ble_scan.go +++ b/src/protogen/ble_scan.go @@ -178,7 +178,7 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc } dev.Services = append(dev.Services, *svc) - sublog.Debug().Str("UUID", svc.UUID).Msg("SERVICE_DISCOVERED") + sublog.Info().Str("UUID", svc.UUID).Msg("SERVICE_DISCOVERED") } if !projVars.AttackMode { @@ -186,17 +186,15 @@ func (s *Scan) resultHandler(scanAdapter *bluetooth.Adapter, result bluetooth.Sc return } - //////////////////////////////////////////////////////////////////////// - //var PreTargetHandling type - //var TargetDeviceService *bluetooth.DeviceService - //var TargetDeviceServiceChar *bluetooth.DeviceCharacteristic - sublog.Info().Str("Adapter", "Attempting Connection").Msg("ADAPTER_STATUS") var connErr error var err error - timeout := bluetooth.NewDuration(time.Duration(5) * time.Second) - dev.Conn, connErr = projVars.ScanAdapter.Connect(result.Address, bluetooth.ConnectionParams{ConnectionTimeout: timeout}) + + // TODO: make timeout values and intervals command line arguments instead of hard coding them + timeout := bluetooth.NewDuration(time.Duration(3) * time.Second) + interval := bluetooth.NewDuration(time.Duration(20) * time.Millisecond) + dev.Conn, connErr = projVars.ScanAdapter.Connect(result.Address, bluetooth.ConnectionParams{ConnectionTimeout: timeout, MinInterval: interval}) if connErr != nil { sublog.Error().Err(connErr).Msg("CONNECT_ERROR")