mirror of
https://github.com/bettercap/bettercap
synced 2025-08-19 21:13:18 -07:00
new: can.fuzz command
This commit is contained in:
parent
69744e6b63
commit
6f1920f478
5 changed files with 160 additions and 42 deletions
|
@ -79,6 +79,15 @@ func NewCanModule(s *session.Session) *CANModule {
|
|||
return mod.Inject(args[0])
|
||||
}))
|
||||
|
||||
mod.AddHandler(session.NewModuleHandler("can.fuzz ID_OR_NODE_NAME", `(?i)^can\.fuzz\s+(.+)$`,
|
||||
"If an integer frame ID is specified, create a randomized version of it and inject it. If a node name is specified, a random message for the given node will be instead used.",
|
||||
func(args []string) error {
|
||||
if !mod.Running() {
|
||||
return errors.New("can module not running")
|
||||
}
|
||||
return mod.Fuzz(args[0])
|
||||
}))
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
|
|
93
modules/can/can_fuzz.go
Normal file
93
modules/can/can_fuzz.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package can
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"go.einride.tech/can"
|
||||
"go.einride.tech/can/pkg/descriptor"
|
||||
)
|
||||
|
||||
func (mod *CANModule) Fuzz(id string) error {
|
||||
rncSource := rand.NewSource(time.Now().Unix())
|
||||
rng := rand.New(rncSource)
|
||||
|
||||
// let's try as number first
|
||||
frameID, err := strconv.Atoi(id)
|
||||
dataLen := 0
|
||||
frameData := ([]byte)(nil)
|
||||
|
||||
if err != nil {
|
||||
if mod.dbc != nil {
|
||||
// not a number, use as node name
|
||||
fromSender := make([]*descriptor.Message, 0)
|
||||
for _, msg := range mod.dbc.Messages {
|
||||
if msg.SenderNode == id {
|
||||
fromSender = append(fromSender, msg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(fromSender) == 0 {
|
||||
return fmt.Errorf("no messages defined in DBC file for node %s", id)
|
||||
}
|
||||
|
||||
idx := rng.Intn(len(fromSender))
|
||||
selected := fromSender[idx]
|
||||
mod.Info("selected %s > (%d) %s", id, selected.ID, selected.Name)
|
||||
frameID = int(selected.ID)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a DBC
|
||||
if mod.dbc != nil {
|
||||
if message, found := mod.dbc.Message(uint32(frameID)); found {
|
||||
mod.Info("found as %s", message.Name)
|
||||
|
||||
dataLen = int(message.Length)
|
||||
frameData = make([]byte, dataLen)
|
||||
if _, err := rand.Read(frameData); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
avail := []string{}
|
||||
for _, msg := range mod.dbc.Messages {
|
||||
avail = append(avail, fmt.Sprintf("%d (%s)", msg.ID, msg.Name))
|
||||
}
|
||||
return fmt.Errorf("message with id %d not found in DBC file, available ids: %v", frameID, strings.Join(avail, ", "))
|
||||
}
|
||||
} else {
|
||||
dataLen = rng.Intn(int(can.MaxDataLength))
|
||||
frameData = make([]byte, dataLen)
|
||||
|
||||
if _, err := rand.Read(frameData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mod.Warning("no can.dbc_path is set, creating frame with %d bytes of random data", dataLen)
|
||||
}
|
||||
|
||||
frame := can.Frame{
|
||||
ID: uint32(frameID),
|
||||
Length: uint8(dataLen),
|
||||
IsRemote: false,
|
||||
IsExtended: false,
|
||||
}
|
||||
|
||||
copy(frame.Data[:], frameData)
|
||||
|
||||
mod.Info("injecting %s of CAN frame %d ...",
|
||||
humanize.Bytes(uint64(frame.Length)), frame.ID)
|
||||
|
||||
if err := mod.send.TransmitFrame(context.Background(), frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package can
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
@ -57,7 +56,7 @@ func (mod *CANModule) Configure() error {
|
|||
mod.Warning("no can.dbc_path specified, messages won't be parsed")
|
||||
}
|
||||
|
||||
if mod.conn, err = socketcan.DialContext(context.Background(), mod.transport, mod.deviceName); err != nil {
|
||||
if mod.conn, err = socketcan.Dial(mod.transport, mod.deviceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -67,6 +66,52 @@ func (mod *CANModule) Configure() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mod *CANModule) onFrame(frame can.Frame) {
|
||||
msg := Message{
|
||||
Frame: frame,
|
||||
}
|
||||
|
||||
// if we have a DBC database
|
||||
if mod.dbc != nil {
|
||||
// if the database contains this message id
|
||||
if message, found := mod.dbc.Message(frame.ID); found {
|
||||
msg.Name = message.Name
|
||||
|
||||
// find source full info in DBC nodes
|
||||
sourceName := message.SenderNode
|
||||
sourceDesc := ""
|
||||
if sender, found := mod.dbc.Node(message.SenderNode); found {
|
||||
sourceName = sender.Name
|
||||
sourceDesc = sender.Description
|
||||
}
|
||||
|
||||
// add CAN source if new
|
||||
_, msg.Source = mod.Session.CAN.AddIfNew(sourceName, sourceDesc, frame.Data[:])
|
||||
|
||||
msg.Signals = make(map[string]string)
|
||||
|
||||
// parse signals
|
||||
for _, signal := range message.Signals {
|
||||
var value string
|
||||
|
||||
if signal.Length <= 32 && signal.IsFloat {
|
||||
value = fmt.Sprintf("%f", signal.UnmarshalFloat(frame.Data))
|
||||
} else if signal.Length == 1 {
|
||||
value = fmt.Sprintf("%v", signal.UnmarshalBool(frame.Data))
|
||||
} else if signal.IsSigned {
|
||||
value = fmt.Sprintf("%d", signal.UnmarshalSigned(frame.Data))
|
||||
} else {
|
||||
value = fmt.Sprintf("%d", signal.UnmarshalUnsigned(frame.Data))
|
||||
}
|
||||
|
||||
msg.Signals[signal.Name] = str.Trim(fmt.Sprintf("%s %s", value, signal.Unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod.Session.Events.Add("can.message", msg)
|
||||
}
|
||||
|
||||
func (mod *CANModule) Start() error {
|
||||
if err := mod.Configure(); err != nil {
|
||||
return err
|
||||
|
@ -77,44 +122,7 @@ func (mod *CANModule) Start() error {
|
|||
|
||||
for mod.recv.Receive() {
|
||||
frame := mod.recv.Frame()
|
||||
msg := Message{
|
||||
Frame: frame,
|
||||
}
|
||||
|
||||
if mod.dbc != nil {
|
||||
if message, found := mod.dbc.Message(frame.ID); found {
|
||||
msg.Name = message.Name
|
||||
|
||||
sourceName := message.SenderNode
|
||||
sourceDesc := ""
|
||||
if sender, found := mod.dbc.Node(message.SenderNode); found {
|
||||
sourceName = sender.Name
|
||||
sourceDesc = sender.Description
|
||||
}
|
||||
|
||||
_, msg.Source = mod.Session.CAN.AddIfNew(sourceName, sourceDesc, frame.Data[:])
|
||||
|
||||
msg.Signals = make(map[string]string)
|
||||
|
||||
for _, signal := range message.Signals {
|
||||
var value string
|
||||
|
||||
if signal.Length <= 32 && signal.IsFloat {
|
||||
value = fmt.Sprintf("%f", signal.UnmarshalFloat(frame.Data))
|
||||
} else if signal.Length == 1 {
|
||||
value = fmt.Sprintf("%v", signal.UnmarshalBool(frame.Data))
|
||||
} else if signal.IsSigned {
|
||||
value = fmt.Sprintf("%d", signal.UnmarshalSigned(frame.Data))
|
||||
} else {
|
||||
value = fmt.Sprintf("%d", signal.UnmarshalUnsigned(frame.Data))
|
||||
}
|
||||
|
||||
msg.Signals[signal.Name] = str.Trim(fmt.Sprintf("%s %s", value, signal.Unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod.Session.Events.Add("can.message", msg)
|
||||
mod.onFrame(frame)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package can
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bettercap/bettercap/v2/network"
|
||||
|
@ -27,6 +28,7 @@ func (mod *CANModule) getRow(dev *network.CANDevice) []string {
|
|||
return []string{
|
||||
dev.Name,
|
||||
dev.Description,
|
||||
fmt.Sprintf("%d", dev.Frames),
|
||||
humanize.Bytes(dev.Read),
|
||||
seen,
|
||||
}
|
||||
|
@ -40,7 +42,7 @@ func (mod *CANModule) Show() (err error) {
|
|||
rows = append(rows, mod.getRow(dev))
|
||||
}
|
||||
|
||||
tui.Table(mod.Session.Events.Stdout, []string{"Name", "Description", "Data", "Seen"}, rows)
|
||||
tui.Table(mod.Session.Events.Stdout, []string{"Name", "Description", "Frames", "Data", "Seen"}, rows)
|
||||
|
||||
if len(rows) > 0 {
|
||||
mod.Session.Refresh()
|
||||
|
|
|
@ -11,6 +11,7 @@ type CANDevice struct {
|
|||
LastSeen time.Time
|
||||
Name string
|
||||
Description string
|
||||
Frames uint64
|
||||
Read uint64
|
||||
}
|
||||
|
||||
|
@ -18,7 +19,8 @@ type canDeviceJSON struct {
|
|||
LastSeen time.Time `json:"last_seen"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Read uint64 `json:"description"`
|
||||
Frames uint64 `json:"frames"`
|
||||
Read uint64 `json:"read"`
|
||||
}
|
||||
|
||||
func NewCANDevice(name string, description string, payload []byte) *CANDevice {
|
||||
|
@ -27,6 +29,7 @@ func NewCANDevice(name string, description string, payload []byte) *CANDevice {
|
|||
Name: name,
|
||||
Description: description,
|
||||
Read: uint64(len(payload)),
|
||||
Frames: 1,
|
||||
}
|
||||
|
||||
return dev
|
||||
|
@ -41,6 +44,7 @@ func (dev *CANDevice) MarshalJSON() ([]byte, error) {
|
|||
Name: dev.Name,
|
||||
Description: dev.Description,
|
||||
Read: dev.Read,
|
||||
Frames: dev.Frames,
|
||||
}
|
||||
|
||||
return json.Marshal(doc)
|
||||
|
@ -54,4 +58,6 @@ func (dev *CANDevice) AddPayload(payload []byte) {
|
|||
if payload != nil && sz > 0 {
|
||||
dev.Read += uint64(sz)
|
||||
}
|
||||
|
||||
dev.Frames += 1
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue