mirror of
https://github.com/bettercap/bettercap
synced 2025-08-06 15:03:59 -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"
|
||||
"strings"
|
||||
|
||||
"github.com/bettercap/bettercap/network"
|
||||
"github.com/bettercap/gatt"
|
||||
|
||||
"github.com/evilsocket/islazy/tui"
|
||||
|
@ -276,7 +277,17 @@ func (mod *BLERecon) showServices(p gatt.Peripheral, services []*gatt.Service) {
|
|||
wantsToWrite := mod.writeUUID != nil
|
||||
foundToWrite := false
|
||||
|
||||
mod.currDevice.Services = make([]network.BLEService, 0)
|
||||
|
||||
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)
|
||||
|
||||
name := svc.Name()
|
||||
|
@ -298,83 +309,95 @@ func (mod *BLERecon) showServices(p gatt.Peripheral, services []*gatt.Service) {
|
|||
chars, err := p.DiscoverCharacteristics(nil, svc)
|
||||
if err != nil {
|
||||
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 {
|
||||
mod.Session.Events.Add("ble.device.characteristic.discovered", ch)
|
||||
char := network.BLECharacteristic{
|
||||
UUID: ch.UUID().String(),
|
||||
Name: ch.Name(),
|
||||
Handle: ch.VHandle(),
|
||||
Properties: props,
|
||||
}
|
||||
|
||||
name = ch.Name()
|
||||
if name == "" {
|
||||
name = " " + ch.UUID().String()
|
||||
} else {
|
||||
name = fmt.Sprintf(" %s (%s)", tui.Green(name), tui.Dim(ch.UUID().String()))
|
||||
}
|
||||
mod.Session.Events.Add("ble.device.characteristic.discovered", ch)
|
||||
|
||||
props, isReadable, isWritable, withResponse := parseProperties(ch)
|
||||
|
||||
if wantsToWrite && mod.writeUUID.Equal(ch.UUID()) {
|
||||
foundToWrite = true
|
||||
if isWritable {
|
||||
mod.Info("writing %d bytes to characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
||||
name = ch.Name()
|
||||
if name == "" {
|
||||
name = " " + ch.UUID().String()
|
||||
} 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 {
|
||||
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 {
|
||||
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,
|
||||
})
|
||||
if wantsToWrite && mod.writeUUID.Equal(ch.UUID()) {
|
||||
foundToWrite = true
|
||||
if isWritable {
|
||||
mod.Info("writing %d bytes to characteristics %s ...", len(mod.writeData), mod.writeUUID)
|
||||
} 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 {
|
||||
|
|
|
@ -10,22 +10,40 @@ import (
|
|||
"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 {
|
||||
LastSeen time.Time
|
||||
Vendor string
|
||||
RSSI int
|
||||
Device gatt.Peripheral
|
||||
Advertisement *gatt.Advertisement
|
||||
Services []BLEService
|
||||
}
|
||||
|
||||
type bleDeviceJSON struct {
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
Name string `json:"name"`
|
||||
MAC string `json:"mac"`
|
||||
Vendor string `json:"vendor"`
|
||||
RSSI int `json:"rssi"`
|
||||
Connectable bool `json:"connectable"`
|
||||
Flags string `json:"flags"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
Name string `json:"name"`
|
||||
MAC string `json:"mac"`
|
||||
Vendor string `json:"vendor"`
|
||||
RSSI int `json:"rssi"`
|
||||
Connectable bool `json:"connectable"`
|
||||
Flags string `json:"flags"`
|
||||
Services []BLEService `json:"services"`
|
||||
}
|
||||
|
||||
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,
|
||||
Advertisement: a,
|
||||
RSSI: rssi,
|
||||
Services: make([]BLEService, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +78,7 @@ func (d *BLEDevice) MarshalJSON() ([]byte, error) {
|
|||
RSSI: d.RSSI,
|
||||
Connectable: d.Advertisement.Connectable,
|
||||
Flags: d.Advertisement.Flags.String(),
|
||||
Services: d.Services,
|
||||
}
|
||||
return json.Marshal(doc)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue