diff --git a/cli/cmd/bash.go b/cli/cmd/bash.go index 3cc02f1e0..0b8def9ec 100644 --- a/cli/cmd/bash.go +++ b/cli/cmd/bash.go @@ -18,11 +18,12 @@ import ( "context" "ecapture/user/config" "ecapture/user/module" - "github.com/spf13/cobra" "log" "os" "os/signal" "syscall" + + "github.com/spf13/cobra" ) var bc = config.NewBashConfig() @@ -55,10 +56,11 @@ func init() { // bashCommandFunc executes the "bash" command. func bashCommandFunc(command *cobra.Command, args []string) { + stopper := make(chan os.Signal, 1) signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) ctx, cancelFun := context.WithCancel(context.TODO()) - + ctx = context.WithValue(ctx, config.CONTEXT_KEY_MODULE_NAME, "bash") mod := module.GetModuleByName(module.ModuleNameBash) logger := log.New(os.Stdout, "bash_", log.LstdFlags) diff --git a/kern/bash_kern.c b/kern/bash_kern.c index c789061c8..e8a4c77b4 100644 --- a/kern/bash_kern.c +++ b/kern/bash_kern.c @@ -15,6 +15,7 @@ #include "ecapture.h" struct event { + u32 type; u32 pid; u32 uid; u8 line[MAX_DATA_SIZE_BASH]; @@ -32,9 +33,9 @@ struct { struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, u32); - __type(value, struct event); + __type(value, u32); __uint(max_entries, 1024); -} events_t SEC(".maps"); +} pid_temp SEC(".maps"); // Force emitting struct event into the ELF. const struct event *unused __attribute__((unused)); @@ -55,15 +56,19 @@ int uretprobe_bash_readline(struct pt_regs *ctx) { } #endif - struct event event = {}; - event.pid = pid; - event.uid = uid; + struct event event = { + .type = BASH_EVENT_TYPE_READLINE, + .pid = pid, + .uid = uid, + .retval = 0, + }; // bpf_printk("!! uretprobe_bash_readline pid:%d",target_pid ); bpf_probe_read_user(&event.line, sizeof(event.line), (void *)PT_REGS_RC(ctx)); bpf_get_current_comm(&event.comm, sizeof(event.comm)); - bpf_map_update_elem(&events_t, &pid, &event, BPF_ANY); - + bpf_map_update_elem(&pid_temp, &pid, &pid, BPF_ANY); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, + sizeof(struct event)); return 0; } SEC("uretprobe/bash_retval") @@ -84,22 +89,19 @@ int uretprobe_bash_retval(struct pt_regs *ctx) { } #endif - struct event *event_p = bpf_map_lookup_elem(&events_t, &pid); - -#ifndef KERNEL_LESS_5_2 - // if target_errno is 128 then we target all - if (target_errno != BASH_ERRNO_DEFAULT && target_errno != retval) { - if (event_p) bpf_map_delete_elem(&events_t, &pid); - return 0; - } -#endif - - if (event_p) { - event_p->retval = retval; - // bpf_map_update_elem(&events_t, &pid, event_p, BPF_ANY); - bpf_map_delete_elem(&events_t, &pid); - bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event_p, + u32 *pid_p = bpf_map_lookup_elem(&pid_temp, &pid); + if (pid_p) { + struct event event_p = { + .type = BASH_EVENT_TYPE_RETVAL, + .pid = pid, + .uid = uid, + .retval = retval, + }; + bpf_get_current_comm(&event_p.comm, sizeof(event_p.comm)); + bpf_map_delete_elem(&pid_temp, &pid); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event_p, sizeof(struct event)); } + return 0; } diff --git a/kern/common.h b/kern/common.h index ac013dd22..16f44f171 100644 --- a/kern/common.h +++ b/kern/common.h @@ -41,6 +41,9 @@ #define SA_DATA_LEN 14 #define BASH_ERRNO_DEFAULT 128 +#define BASH_EVENT_TYPE_READLINE 0 +#define BASH_EVENT_TYPE_RETVAL 1 + ///////// for TC & XDP ebpf programs in tc.h #define TC_ACT_OK 0 #define ETH_P_IP 0x0800 /* Internet Protocol packet */ diff --git a/pkg/event_processor/iworker.go b/pkg/event_processor/iworker.go index 650a7dd04..f526170c1 100644 --- a/pkg/event_processor/iworker.go +++ b/pkg/event_processor/iworker.go @@ -15,19 +15,13 @@ package event_processor import ( - "bytes" "ecapture/user/event" "encoding/hex" - "sync/atomic" "time" ) type IWorker interface { - // 定时器1 ,定时判断没有后续包,则解析输出 - - // 定时器2, 定时判断没后续包,则通知上层销毁自己 - // 收包 Write(event.IEventStruct) error GetUUID() string diff --git a/pkg/event_processor/processor.go b/pkg/event_processor/processor.go index ab3bd8022..50e230efd 100644 --- a/pkg/event_processor/processor.go +++ b/pkg/event_processor/processor.go @@ -15,6 +15,8 @@ package event_processor import ( + "context" + "ecapture/user/config" "ecapture/user/event" "fmt" "log" @@ -28,9 +30,9 @@ const ( type EventProcessor struct { sync.Mutex + ctx context.Context // 收包,来自调用者发来的新事件 incoming chan event.IEventStruct - // key为 PID+UID+COMMON等确定唯一的信息 workerQueue map[string]IWorker @@ -51,11 +53,8 @@ func (this *EventProcessor) init() { // Write event 处理器读取事件 func (this *EventProcessor) Serve() { - for { - select { - case e := <-this.incoming: - this.dispatch(e) - } + for e := range this.incoming { + this.dispatch(e) } } @@ -65,7 +64,12 @@ func (this *EventProcessor) dispatch(e event.IEventStruct) { found, eWorker := this.getWorkerByUUID(uuid) if !found { // ADD a new eventWorker into queue - eWorker = NewEventWorker(e.GetUUID(), this) + + if this.ctx.Value(config.CONTEXT_KEY_MODULE_NAME) == "bash" { + eWorker = NewBashEventWorker(e.GetUUID(), this) + } else { + eWorker = NewEventWorker(e.GetUUID(), this) + } this.addWorkerByUUID(eWorker) } @@ -126,9 +130,9 @@ func (this *EventProcessor) Close() error { return nil } -func NewEventProcessor(logger *log.Logger, isHex bool) *EventProcessor { - var ep *EventProcessor - ep = &EventProcessor{} +func NewEventProcessor(ctx context.Context, logger *log.Logger, isHex bool) *EventProcessor { + ep := &EventProcessor{} + ep.ctx = ctx ep.logger = logger ep.isHex = isHex ep.init() diff --git a/pkg/event_processor/processor_test.go b/pkg/event_processor/processor_test.go index 16572e090..4a6074dd4 100644 --- a/pkg/event_processor/processor_test.go +++ b/pkg/event_processor/processor_test.go @@ -2,6 +2,7 @@ package event_processor import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -41,7 +42,7 @@ func TestEventProcessor_Serve(t *testing.T) { } logger.SetOutput(f) */ - ep := NewEventProcessor(logger, true) + ep := NewEventProcessor(context.Background(), logger, true) go func() { ep.Serve() diff --git a/pkg/event_processor/worker_bashevent.go b/pkg/event_processor/worker_bashevent.go new file mode 100644 index 000000000..0255c9f3a --- /dev/null +++ b/pkg/event_processor/worker_bashevent.go @@ -0,0 +1,79 @@ +package event_processor + +import ( + "ecapture/user/event" + "strings" + + "golang.org/x/sys/unix" +) + +// 特殊处理bashevent +type bashEventWorker struct { + incoming chan event.IEventStruct + status ProcessStatus + UUID string + processor *EventProcessor + line string + retVal uint32 +} + +func NewBashEventWorker(uuid string, processor *EventProcessor) IWorker { + beWorker := &bashEventWorker{} + beWorker.init(uuid, processor) + go func() { + beWorker.Run() + }() + return beWorker +} + +func (ew *bashEventWorker) init(uuid string, processor *EventProcessor) { + ew.incoming = make(chan event.IEventStruct) + ew.status = ProcessStateInit + ew.UUID = uuid + ew.processor = processor +} + +func (bew *bashEventWorker) GetUUID() string { + return bew.UUID +} + +func (bew *bashEventWorker) Write(e event.IEventStruct) error { + bew.incoming <- e + return nil +} + +func (bew *bashEventWorker) Run() { + for e := range bew.incoming { + bashEvent, _ := e.(*event.BashEvent) + line := strings.TrimSpace(unix.ByteSliceToString((bashEvent.Line[:]))) + if (line == "" || line == "\\") && bew.status == ProcessStateInit { + continue + } + bew.line += line + bew.status = ProcessStateProcessing + if bashEvent.Type == 1 { + //retval + bew.retVal = bashEvent.Retval + bew.Close() + return + } + + if strings.HasPrefix(line, "exit") || strings.HasPrefix(line, "exec") { + //无返回值的命令 + bew.Close() + return + } + bew.line += "\n" + } +} + +func (bew *bashEventWorker) Close() { + bew.status = ProcessStateDone + bew.Display() + bew.processor.delWorkerByUUID(bew) +} + +// 输出整个Command内容 +func (bew *bashEventWorker) Display() { + bew.processor.GetLogger().Printf("pid_uid_comm:%s, length:%d, retVal:%v\nline:%v", bew.UUID, len(bew.line), bew.retVal, bew.line) +} diff --git a/pkg/event_processor/worker_defaultevent.go b/pkg/event_processor/worker_defaultevent.go new file mode 100644 index 000000000..72b91d6cc --- /dev/null +++ b/pkg/event_processor/worker_defaultevent.go @@ -0,0 +1,147 @@ +// Copyright 2022 CFC4N . All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package event_processor + +import ( + "bytes" + "ecapture/user/event" + "encoding/hex" + "time" +) + +const ( + MaxTickerCount = 10 // 1 Sencond/(eventWorker.ticker.C) = 10 + MaxChanLen = 16 // 包队列长度 + //MAX_EVENT_LEN = 16 // 事件数组长度 +) + +type eventWorker struct { + // 定时器1 ,定时判断没有后续包,则解析输出 + + // 定时器2, 定时判断没后续包,则通知上层销毁自己 + + incoming chan event.IEventStruct + //events []user.IEventStruct + status ProcessStatus + packetType PacketType + ticker *time.Ticker + tickerCount uint8 + UUID string + processor *EventProcessor + parser IParser + payload *bytes.Buffer +} + +func NewEventWorker(uuid string, processor *EventProcessor) IWorker { + eWorker := &eventWorker{} + eWorker.init(uuid, processor) + go func() { + eWorker.Run() + }() + return eWorker +} + +func (ew *eventWorker) init(uuid string, processor *EventProcessor) { + ew.ticker = time.NewTicker(time.Millisecond * 100) + ew.incoming = make(chan event.IEventStruct, MaxChanLen) + ew.status = ProcessStateInit + ew.UUID = uuid + ew.processor = processor + ew.payload = bytes.NewBuffer(nil) + ew.payload.Reset() +} + +func (ew *eventWorker) GetUUID() string { + return ew.UUID +} + +func (ew *eventWorker) Write(e event.IEventStruct) error { + ew.incoming <- e + return nil +} + +// 输出包内容 +func (ew *eventWorker) Display() { + + // 输出包内容 + b := ew.parserEvents() + defer ew.parser.Reset() + if len(b) <= 0 { + return + } + + if ew.processor.isHex { + b = []byte(hex.Dump(b)) + } + + // TODO 格式化的终端输出 + // 重置状态 + ew.processor.GetLogger().Printf("UUID:%s, Name:%s, Type:%d, Length:%d", ew.UUID, ew.parser.Name(), ew.parser.ParserType(), len(b)) + ew.processor.GetLogger().Println("\n" + string(b)) + //ew.parser.Reset() + // 设定状态、重置包类型 + ew.status = ProcessStateInit + ew.packetType = PacketTypeNull +} + +func (ew *eventWorker) writeEvent(e event.IEventStruct) { + if ew.status != ProcessStateInit { + ew.processor.GetLogger().Printf("write events failed, unknow eventWorker status") + return + } + ew.payload.Write(e.Payload()) +} + +// 解析类型,输出 +func (ew *eventWorker) parserEvents() []byte { + ew.status = ProcessStateProcessing + parser := NewParser(ew.payload.Bytes()) + ew.parser = parser + n, e := ew.parser.Write(ew.payload.Bytes()) + if e != nil { + ew.processor.GetLogger().Printf("ew.parser write payload %d bytes, error:%v", n, e) + } + ew.status = ProcessStateDone + return ew.parser.Display() +} + +func (ew *eventWorker) Run() { + ew.processor.GetLogger().Println("worker run") + for { + select { + case _ = <-ew.ticker.C: + // 输出包 + if ew.tickerCount > MaxTickerCount { + //ew.processor.GetLogger().Printf("eventWorker TickerCount > %d, event closed.", MaxTickerCount) + ew.Close() + return + } + ew.tickerCount++ + case e := <-ew.incoming: + // reset tickerCount + ew.tickerCount = 0 + ew.writeEvent(e) + } + } + +} + +func (ew *eventWorker) Close() { + // 即将关闭, 必须输出结果 + ew.ticker.Stop() + ew.Display() + ew.tickerCount = 0 + ew.processor.delWorkerByUUID(ew) +} diff --git a/user/config/const.go b/user/config/const.go index 3428149ef..6a15b130e 100644 --- a/user/config/const.go +++ b/user/config/const.go @@ -18,3 +18,8 @@ const ( ElfTypeBin uint8 = 1 ElfTypeSo uint8 = 2 ) + +// context info +type contextKey string + +const CONTEXT_KEY_MODULE_NAME contextKey = "module_name" diff --git a/user/event/event_bash.go b/user/event/event_bash.go index b4787fddc..2ac45dfb3 100644 --- a/user/event/event_bash.go +++ b/user/event/event_bash.go @@ -23,16 +23,19 @@ import ( ) /* - u32 pid; - u8 line[MAX_DATE_SIZE_BASH]; - u32 Retval; - char Comm[TASK_COMM_LEN]; + u32 type; + u32 pid; + u32 uid; + u8 line[MAX_DATA_SIZE_BASH]; + u32 retval; + char comm[TASK_COMM_LEN]; */ const MaxDataSizeBash = 256 type BashEvent struct { eventType EventType + Type uint32 `json:"type"` Pid uint32 `json:"pid"` Uid uint32 `json:"uid"` Line [MaxDataSizeBash]uint8 `json:"line"` @@ -42,6 +45,9 @@ type BashEvent struct { func (be *BashEvent) Decode(payload []byte) (err error) { buf := bytes.NewBuffer(payload) + if err = binary.Read(buf, binary.LittleEndian, &be.Type); err != nil { + return + } if err = binary.Read(buf, binary.LittleEndian, &be.Pid); err != nil { return } @@ -57,23 +63,22 @@ func (be *BashEvent) Decode(payload []byte) (err error) { if err = binary.Read(buf, binary.LittleEndian, &be.Comm); err != nil { return } - return nil } func (be *BashEvent) String() string { - s := fmt.Sprintf("PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s", be.Pid, be.Uid, be.Comm, be.Retval, unix.ByteSliceToString((be.Line[:]))) + s := fmt.Sprintf("TYPE:%d, PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s", be.Type, be.Pid, be.Uid, be.Comm, be.Retval, unix.ByteSliceToString((be.Line[:]))) return s } func (be *BashEvent) StringHex() string { - s := fmt.Sprintf("PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s,", be.Pid, be.Uid, be.Comm, be.Retval, dumpByteSlice([]byte(unix.ByteSliceToString((be.Line[:]))), "")) + s := fmt.Sprintf("TYPE:%d, PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s,", be.Type, be.Pid, be.Uid, be.Comm, be.Retval, dumpByteSlice([]byte(unix.ByteSliceToString((be.Line[:]))), "")) return s } func (be *BashEvent) Clone() IEventStruct { event := new(BashEvent) - event.eventType = EventTypeOutput + event.eventType = EventTypeEventProcessor return event } diff --git a/user/module/imodule.go b/user/module/imodule.go index 93af21fb6..67eaf5807 100644 --- a/user/module/imodule.go +++ b/user/module/imodule.go @@ -22,11 +22,12 @@ import ( "ecapture/user/event" "errors" "fmt" + "log" + "strings" + "github.com/cilium/ebpf" "github.com/cilium/ebpf/perf" "github.com/cilium/ebpf/ringbuf" - "log" - "strings" ) type IModule interface { @@ -83,7 +84,7 @@ type Module struct { func (m *Module) Init(ctx context.Context, logger *log.Logger, conf config.IConfig) { m.ctx = ctx m.logger = logger - m.processor = event_processor.NewEventProcessor(logger, conf.GetHex()) + m.processor = event_processor.NewEventProcessor(ctx, logger, conf.GetHex()) m.isKernelLess5_2 = false //set false default kv, _ := kernel.HostVersion() // it's safe to ignore err because we have checked it in main funcition