From 7702207ee9a90b8d996cdf0bfe6f817f8f85a921 Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Mon, 26 Aug 2024 15:12:09 +0200 Subject: [PATCH] new: implemented can.dup and can.dump.inject to read a candump log file --- .gitignore | 3 +- modules/can/can.go | 32 ++++++++------ modules/can/can_dump_reader.go | 76 ++++++++++++++++++++++++++++++++++ modules/can/can_recon.go | 11 ++++- 4 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 modules/can/can_dump_reader.go diff --git a/.gitignore b/.gitignore index 41f70845..54d34c48 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ stage/ /snap .idea -/cover.out \ No newline at end of file +/cover.out +/can-data \ No newline at end of file diff --git a/modules/can/can.go b/modules/can/can.go index 185e3ef3..d47dc8dc 100644 --- a/modules/can/can.go +++ b/modules/can/can.go @@ -2,6 +2,7 @@ package can import ( "errors" + "fmt" "net" "github.com/bettercap/bettercap/v2/session" @@ -13,6 +14,8 @@ type CANModule struct { session.SessionModule deviceName string + dumpName string + dumpInject bool transport string filter string filterExpr *bexpr.Evaluator @@ -30,6 +33,8 @@ func NewCanModule(s *session.Session) *CANModule { filterExpr: nil, transport: "can", deviceName: "can0", + dumpName: "", + dumpInject: false, } mod.AddParam(session.NewStringParameter("can.device", @@ -37,6 +42,15 @@ func NewCanModule(s *session.Session) *CANModule { "", "CAN-bus device.")) + mod.AddParam(session.NewStringParameter("can.dump", + mod.dumpName, + "", + "Load CAN traffic from this candump log file.")) + + mod.AddParam(session.NewBoolParameter("can.dump.inject", + fmt.Sprintf("%v", mod.dumpInject), + "Write CAN traffic read form the candump log file to the selected can.device.")) + mod.AddParam(session.NewStringParameter("can.transport", mod.transport, "", @@ -53,18 +67,6 @@ func NewCanModule(s *session.Session) *CANModule { return mod.Start() })) - mod.AddHandler(session.NewModuleHandler("can.load_dbc PATH", "", - "Start CAN-bus discovery.", - func(args []string) error { - return mod.Start() - })) - - mod.AddHandler(session.NewModuleHandler("can.dbc.load NAME", "can.dbc.load (.+)", - "Load a DBC file from the list of available ones or from disk.", - func(args []string) error { - return mod.dbcLoad(args[0]) - })) - mod.AddHandler(session.NewModuleHandler("can.recon off", "", "Stop CAN-bus discovery.", func(args []string) error { @@ -84,6 +86,12 @@ func NewCanModule(s *session.Session) *CANModule { return mod.Show() })) + mod.AddHandler(session.NewModuleHandler("can.dbc.load NAME", "can.dbc.load (.+)", + "Load a DBC file from the list of available ones or from disk.", + func(args []string) error { + return mod.dbcLoad(args[0]) + })) + mod.AddHandler(session.NewModuleHandler("can.inject FRAME_EXPRESSION", `(?i)^can\.inject\s+([a-fA-F0-9#R]+)$`, "Parse FRAME_EXPRESSION as 'id#data' and inject it as a CAN frame.", func(args []string) error { diff --git a/modules/can/can_dump_reader.go b/modules/can/can_dump_reader.go new file mode 100644 index 00000000..fbdf29ef --- /dev/null +++ b/modules/can/can_dump_reader.go @@ -0,0 +1,76 @@ +package can + +import ( + "bufio" + "context" + "os" + "regexp" + + "github.com/evilsocket/islazy/str" + "go.einride.tech/can" +) + +// (1700623093.260875) can0 7E0#0322128C00000000 +var dumpLineParser = regexp.MustCompile(`(?m)^\(([\d\.]+)\)\s+([^\s]+)\s+(.+)`) + +type dumpEntry struct { + Time string + Device string + Frame string +} + +func (mod *CANModule) startDumpReader() error { + mod.Info("loading CAN dump from %s ...", mod.dumpName) + + file, err := os.Open(mod.dumpName) + if err != nil { + return err + } + defer file.Close() + + entries := make([]dumpEntry, 0) + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := str.Trim(scanner.Text()) + if line != "" { + if m := dumpLineParser.FindStringSubmatch(line); len(m) != 4 { + mod.Warning("unexpected line: '%s' -> %d matches", line, len(m)) + } else { + entries = append(entries, dumpEntry{ + Time: m[1], + Device: m[2], + Frame: m[3], + }) + } + } + } + + if err = scanner.Err(); err != nil { + return err + } + + mod.Info("loaded %d entries from candump log", len(entries)) + + go func() { + mod.Info("candump reader started ...") + + for _, entry := range entries { + frame := can.Frame{} + if err := frame.UnmarshalString(entry.Frame); err != nil { + mod.Error("could not unmarshal CAN frame: %v", err) + continue + } + + if mod.dumpInject { + if err := mod.send.TransmitFrame(context.Background(), frame); err != nil { + mod.Error("could not send CAN frame: %v", err) + } + } else { + mod.onFrame(frame) + } + } + }() + + return nil +} diff --git a/modules/can/can_recon.go b/modules/can/can_recon.go index 54db79c7..ec556818 100644 --- a/modules/can/can_recon.go +++ b/modules/can/can_recon.go @@ -25,6 +25,10 @@ func (mod *CANModule) Configure() error { return session.ErrAlreadyStarted(mod.Name()) } else if err, mod.deviceName = mod.StringParam("can.device"); err != nil { return err + } else if err, mod.dumpName = mod.StringParam("can.dump"); err != nil { + return err + } else if err, mod.dumpInject = mod.BoolParam("can.dump.inject"); err != nil { + return err } else if err, mod.transport = mod.StringParam("can.transport"); err != nil { return err } else if mod.transport != "can" && mod.transport != "udp" { @@ -43,10 +47,15 @@ func (mod *CANModule) Configure() error { if mod.conn, err = socketcan.Dial(mod.transport, mod.deviceName); err != nil { return err } - mod.recv = socketcan.NewReceiver(mod.conn) mod.send = socketcan.NewTransmitter(mod.conn) + if mod.dumpName != "" { + if err = mod.startDumpReader(); err != nil { + return err + } + } + return nil }