mirror of
https://github.com/bettercap/bettercap
synced 2025-08-19 04:59:25 -07:00
new: exposing enumerated BLE services for each device via api.rest module
This commit is contained in:
parent
9c171735da
commit
8115490d3a
2 changed files with 116 additions and 73 deletions
|
@ -10,6 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/network"
|
||||||
"github.com/bettercap/gatt"
|
"github.com/bettercap/gatt"
|
||||||
|
|
||||||
"github.com/evilsocket/islazy/tui"
|
"github.com/evilsocket/islazy/tui"
|
||||||
|
@ -276,7 +277,17 @@ func (mod *BLERecon) showServices(p gatt.Peripheral, services []*gatt.Service) {
|
||||||
wantsToWrite := mod.writeUUID != nil
|
wantsToWrite := mod.writeUUID != nil
|
||||||
foundToWrite := false
|
foundToWrite := false
|
||||||
|
|
||||||
|
mod.currDevice.Services = make([]network.BLEService, 0)
|
||||||
|
|
||||||
for _, svc := range services {
|
for _, svc := range services {
|
||||||
|
service := network.BLEService{
|
||||||
|
UUID: svc.UUID().String(),
|
||||||
|
Name: svc.Name(),
|
||||||
|
Handle: svc.Handle(),
|
||||||
|
EndHandle: svc.EndHandle(),
|
||||||
|
Characteristics: make([]network.BLECharacteristic, 0),
|
||||||
|
}
|
||||||
|
|
||||||
mod.Session.Events.Add("ble.device.service.discovered", svc)
|
mod.Session.Events.Add("ble.device.service.discovered", svc)
|
||||||
|
|
||||||
name := svc.Name()
|
name := svc.Name()
|
||||||
|
@ -298,83 +309,95 @@ func (mod *BLERecon) showServices(p gatt.Peripheral, services []*gatt.Service) {
|
||||||
chars, err := p.DiscoverCharacteristics(nil, svc)
|
chars, err := p.DiscoverCharacteristics(nil, svc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mod.Error("error while enumerating chars for service %s: %s", svc.UUID(), err)
|
mod.Error("error while enumerating chars for service %s: %s", svc.UUID(), err)
|
||||||
continue
|
} else {
|
||||||
}
|
for _, ch := range chars {
|
||||||
|
props, isReadable, isWritable, withResponse := parseProperties(ch)
|
||||||
|
|
||||||
for _, ch := range chars {
|
char := network.BLECharacteristic{
|
||||||
mod.Session.Events.Add("ble.device.characteristic.discovered", ch)
|
UUID: ch.UUID().String(),
|
||||||
|
Name: ch.Name(),
|
||||||
|
Handle: ch.VHandle(),
|
||||||
|
Properties: props,
|
||||||
|
}
|
||||||
|
|
||||||
name = ch.Name()
|
mod.Session.Events.Add("ble.device.characteristic.discovered", ch)
|
||||||
if name == "" {
|
|
||||||
name = " " + ch.UUID().String()
|
|
||||||
} else {
|
|
||||||
name = fmt.Sprintf(" %s (%s)", tui.Green(name), tui.Dim(ch.UUID().String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
props, isReadable, isWritable, withResponse := parseProperties(ch)
|
name = ch.Name()
|
||||||
|
if name == "" {
|
||||||
if wantsToWrite && mod.writeUUID.Equal(ch.UUID()) {
|
name = " " + ch.UUID().String()
|
||||||
foundToWrite = true
|
|
||||||
if isWritable {
|
|
||||||
mod.Info("writing %d bytes to characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
|
||||||
} else {
|
} else {
|
||||||
mod.Warning("attempt to write %d bytes to non writable characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
name = fmt.Sprintf(" %s (%s)", tui.Green(name), tui.Dim(ch.UUID().String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.WriteCharacteristic(ch, mod.writeData, !withResponse); err != nil {
|
if wantsToWrite && mod.writeUUID.Equal(ch.UUID()) {
|
||||||
mod.Error("error while writing: %s", err)
|
foundToWrite = true
|
||||||
}
|
if isWritable {
|
||||||
}
|
mod.Info("writing %d bytes to characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
||||||
|
|
||||||
sz := 0
|
|
||||||
raw := ([]byte)(nil)
|
|
||||||
err := error(nil)
|
|
||||||
if isReadable {
|
|
||||||
if raw, err = p.ReadCharacteristic(ch); raw != nil {
|
|
||||||
sz = len(raw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data := ""
|
|
||||||
multi := ([]string)(nil)
|
|
||||||
if err != nil {
|
|
||||||
data = tui.Red(err.Error())
|
|
||||||
} else if ch.Name() == "Appearance" && sz >= 2 {
|
|
||||||
data = parseAppearance(raw)
|
|
||||||
} else if ch.Name() == "PnP ID" && sz >= 7 {
|
|
||||||
multi = parsePNPID(raw)
|
|
||||||
} else if ch.Name() == "Peripheral Preferred Connection Parameters" && sz >= 8 {
|
|
||||||
multi = parseConnectionParams(raw)
|
|
||||||
} else if ch.Name() == "Peripheral Privacy Flag" && sz >= 1 {
|
|
||||||
data = parsePrivacyFlag(raw)
|
|
||||||
} else {
|
|
||||||
data = parseRawData(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
if multi == nil {
|
|
||||||
rows = append(rows, []string{
|
|
||||||
fmt.Sprintf("%04x", ch.VHandle()),
|
|
||||||
name,
|
|
||||||
strings.Join(props, ", "),
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
for i, m := range multi {
|
|
||||||
if i == 0 {
|
|
||||||
rows = append(rows, []string{
|
|
||||||
fmt.Sprintf("%04x", ch.VHandle()),
|
|
||||||
name,
|
|
||||||
strings.Join(props, ", "),
|
|
||||||
m,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
rows = append(rows, []string{"", "", "", m})
|
mod.Warning("attempt to write %d bytes to non writable characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.WriteCharacteristic(ch, mod.writeData, !withResponse); err != nil {
|
||||||
|
mod.Error("error while writing: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sz := 0
|
||||||
|
raw := ([]byte)(nil)
|
||||||
|
err := error(nil)
|
||||||
|
if isReadable {
|
||||||
|
if raw, err = p.ReadCharacteristic(ch); raw != nil {
|
||||||
|
sz = len(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := ""
|
||||||
|
multi := ([]string)(nil)
|
||||||
|
if err != nil {
|
||||||
|
data = tui.Red(err.Error())
|
||||||
|
} else if ch.Name() == "Appearance" && sz >= 2 {
|
||||||
|
data = parseAppearance(raw)
|
||||||
|
} else if ch.Name() == "PnP ID" && sz >= 7 {
|
||||||
|
multi = parsePNPID(raw)
|
||||||
|
} else if ch.Name() == "Peripheral Preferred Connection Parameters" && sz >= 8 {
|
||||||
|
multi = parseConnectionParams(raw)
|
||||||
|
} else if ch.Name() == "Peripheral Privacy Flag" && sz >= 1 {
|
||||||
|
data = parsePrivacyFlag(raw)
|
||||||
|
} else {
|
||||||
|
data = parseRawData(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
if multi == nil {
|
||||||
|
char.Data = data
|
||||||
|
rows = append(rows, []string{
|
||||||
|
fmt.Sprintf("%04x", ch.VHandle()),
|
||||||
|
name,
|
||||||
|
strings.Join(props, ", "),
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
char.Data = multi
|
||||||
|
for i, m := range multi {
|
||||||
|
if i == 0 {
|
||||||
|
rows = append(rows, []string{
|
||||||
|
fmt.Sprintf("%04x", ch.VHandle()),
|
||||||
|
name,
|
||||||
|
strings.Join(props, ", "),
|
||||||
|
m,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
rows = append(rows, []string{"", "", "", m})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.Characteristics = append(service.Characteristics, char)
|
||||||
}
|
}
|
||||||
|
// blank row after every service, bleah style
|
||||||
|
rows = append(rows, []string{"", "", "", ""})
|
||||||
}
|
}
|
||||||
// blank row after every service, bleah style
|
|
||||||
rows = append(rows, []string{"", "", "", ""})
|
mod.currDevice.Services = append(mod.currDevice.Services, service)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wantsToWrite && !foundToWrite {
|
if wantsToWrite && !foundToWrite {
|
||||||
|
|
|
@ -10,22 +10,40 @@ import (
|
||||||
"github.com/bettercap/gatt"
|
"github.com/bettercap/gatt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BLECharacteristic struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Handle uint16 `json:"handle"`
|
||||||
|
Properties []string `json:"properties"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BLEService struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Handle uint16 `json:"handle"`
|
||||||
|
EndHandle uint16 `json:"end_handle"`
|
||||||
|
Characteristics []BLECharacteristic `json:"characteristics"`
|
||||||
|
}
|
||||||
|
|
||||||
type BLEDevice struct {
|
type BLEDevice struct {
|
||||||
LastSeen time.Time
|
LastSeen time.Time
|
||||||
Vendor string
|
Vendor string
|
||||||
RSSI int
|
RSSI int
|
||||||
Device gatt.Peripheral
|
Device gatt.Peripheral
|
||||||
Advertisement *gatt.Advertisement
|
Advertisement *gatt.Advertisement
|
||||||
|
Services []BLEService
|
||||||
}
|
}
|
||||||
|
|
||||||
type bleDeviceJSON struct {
|
type bleDeviceJSON struct {
|
||||||
LastSeen time.Time `json:"last_seen"`
|
LastSeen time.Time `json:"last_seen"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
MAC string `json:"mac"`
|
MAC string `json:"mac"`
|
||||||
Vendor string `json:"vendor"`
|
Vendor string `json:"vendor"`
|
||||||
RSSI int `json:"rssi"`
|
RSSI int `json:"rssi"`
|
||||||
Connectable bool `json:"connectable"`
|
Connectable bool `json:"connectable"`
|
||||||
Flags string `json:"flags"`
|
Flags string `json:"flags"`
|
||||||
|
Services []BLEService `json:"services"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBLEDevice(p gatt.Peripheral, a *gatt.Advertisement, rssi int) *BLEDevice {
|
func NewBLEDevice(p gatt.Peripheral, a *gatt.Advertisement, rssi int) *BLEDevice {
|
||||||
|
@ -39,6 +57,7 @@ func NewBLEDevice(p gatt.Peripheral, a *gatt.Advertisement, rssi int) *BLEDevice
|
||||||
Vendor: vendor,
|
Vendor: vendor,
|
||||||
Advertisement: a,
|
Advertisement: a,
|
||||||
RSSI: rssi,
|
RSSI: rssi,
|
||||||
|
Services: make([]BLEService, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +78,7 @@ func (d *BLEDevice) MarshalJSON() ([]byte, error) {
|
||||||
RSSI: d.RSSI,
|
RSSI: d.RSSI,
|
||||||
Connectable: d.Advertisement.Connectable,
|
Connectable: d.Advertisement.Connectable,
|
||||||
Flags: d.Advertisement.Flags.String(),
|
Flags: d.Advertisement.Flags.String(),
|
||||||
|
Services: d.Services,
|
||||||
}
|
}
|
||||||
return json.Marshal(doc)
|
return json.Marshal(doc)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue