bettercap/modules/ble/ble_enum.go

162 lines
3.8 KiB
Go

package ble
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/bettercap/bettercap/v2/network"
"github.com/evilsocket/islazy/tui"
"tinygo.org/x/bluetooth"
)
func parseRawData(raw []byte) string {
s := ""
for _, b := range raw {
if strconv.IsPrint(rune(b)) {
s += tui.Yellow(string(b))
} else {
s += tui.Dim(fmt.Sprintf("%02x", b))
}
}
return s
}
func (mod *BLERecon) startEnumeration(address string) error {
knownDev, found := mod.Session.BLE.Get(address)
if !found || knownDev == nil {
return fmt.Errorf("device with address %s not found", address)
} else if err := mod.adapter.StopScan(); err != nil {
mod.Warning("error stopping previous scan: %v", err)
}
mod.current.ResetWrite()
defer func() {
// make sure to restart scan when we're done
mod.Info("restoring ble scan")
if err := mod.adapter.Scan(mod.onDevice); err != nil {
mod.Error("error restoring ble scan: %v", err)
}
mod.Info("ble scan restored")
}()
mod.Info("connecting to %s ...", address)
addr := bluetooth.Address{}
addr.Set(address)
tm := bluetooth.NewDuration(time.Second * time.Duration(mod.connTimeout))
device, err := mod.adapter.Connect(addr, bluetooth.ConnectionParams{ConnectionTimeout: tm})
if err != nil {
mod.Error("error connecting to %s: %v", address, err)
return err
}
mod.Session.Events.Add("ble.device.connected", knownDev)
mod.Info("connected to %s", address)
srvcs, err := device.DiscoverServices(nil)
if err != nil {
return fmt.Errorf("could not discover services for %s: %v", address, err)
}
columns := []string{"Service > Characteristics", "Properties", "Data"}
rows := make([][]string, 0)
knownDev.ResetServices()
for _, svc := range srvcs {
service := network.NewBLEService(strings.ToLower(svc.UUID().String()))
mod.Session.Events.Add("ble.device.service.discovered", svc)
name := ""
if service.Name == "" {
name = svc.UUID().String()
} else {
name = fmt.Sprintf("%s (%s)", tui.Green(service.Name), tui.Dim(svc.UUID().String()))
}
row := []string{
name,
"",
"",
}
rows = append(rows, row)
chars, err := svc.DiscoverCharacteristics(nil)
if err != nil {
mod.Error("error while enumerating chars for service %s: %s", svc.UUID(), err)
} else {
for _, ch := range chars {
char := network.NewBLECharacteristic(strings.ToLower(ch.UUID().String()))
if mtu, err := ch.GetMTU(); err != nil {
mod.Warning("can't read %v mtu: %v", ch.UUID(), err)
} else {
char.MTU = mtu
}
mod.Session.Events.Add("ble.device.characteristic.discovered", ch)
name := char.Name
if name == "" {
name = " " + ch.UUID().String()
} else {
name = fmt.Sprintf(" %s (%s)", tui.Green(name), tui.Dim(ch.UUID().String()))
}
data := ""
multi := ([]string)(nil)
raw := make([]byte, 255)
if n, err := ch.Read(raw); err == nil {
raw = raw[0:n]
char.Properties = []string{"READ"}
data = parseRawData(raw)
}
if multi == nil {
char.Data = data
rows = append(rows, []string{
name,
strings.Join(char.Properties, ", "),
data,
})
} else {
char.Data = multi
for i, m := range multi {
if i == 0 {
rows = append(rows, []string{
name,
strings.Join(char.Properties, ", "),
m,
})
} else {
rows = append(rows, []string{"", "", "", m})
}
}
}
service.Characteristics = append(service.Characteristics, char)
}
// blank row after every service, bleah style
rows = append(rows, []string{"", "", ""})
}
knownDev.AddService(service)
}
tui.Table(mod.Session.Events.Stdout, columns, rows)
if err := device.Disconnect(); err != nil {
mod.Warning("error disconnecting from %s: %v", address, err)
} else {
mod.Info("disconnected from %s", address)
}
return nil
}