diff --git a/kern/bash_kern.c b/kern/bash_kern.c index c789061c8..130cc0090 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]; @@ -58,11 +59,14 @@ int uretprobe_bash_readline(struct pt_regs *ctx) { struct event event = {}; event.pid = pid; event.uid = uid; + event.type = BASH_EVENT_TYPE_READLINE; // 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_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, + sizeof(struct event)); return 0; } @@ -89,13 +93,21 @@ int uretprobe_bash_retval(struct pt_regs *ctx) { #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); + if (event_p) + { + event_p->retval = BASH_ERRNO_DEFAULT; + event_p->type = BASH_EVENT_TYPE_RETVAL; + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event_p, + sizeof(struct event)); + bpf_map_delete_elem(&events_t, &pid); + } return 0; } #endif if (event_p) { event_p->retval = retval; + event_p->type = BASH_EVENT_TYPE_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, @@ -103,3 +115,29 @@ int uretprobe_bash_retval(struct pt_regs *ctx) { } return 0; } + +static __always_inline int send_bash_exit_event(struct pt_regs *ctx){ + u64 pid_tgid = bpf_get_current_pid_tgid(); + u32 pid = pid_tgid >> 32; + u64 current_uid_gid = bpf_get_current_uid_gid(); + u32 uid = current_uid_gid; + struct event event = { + .type = BASH_EVENT_TYPE_EXIT_OR_EXEC, + .pid = pid, + .uid = uid, + }; + bpf_get_current_comm(&event.comm, sizeof(event.comm)); + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, + sizeof(struct event)); + return 0; +} + +SEC("uprobe/exec_builtin") +int uprobe_exec_builtin(struct pt_regs *ctx){ + return send_bash_exit_event(ctx); +} + +SEC("uprobe/exit_builtin") +int uprobe_exit_builtin(struct pt_regs *ctx){ + return send_bash_exit_event(ctx); +} \ No newline at end of file diff --git a/kern/common.h b/kern/common.h index ac013dd22..f851a7f9c 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 +#define BASH_EVENT_TYPE_EXIT_OR_EXEC 2 ///////// for TC & XDP ebpf programs in tc.h #define TC_ACT_OK 0 #define ETH_P_IP 0x0800 /* Internet Protocol packet */ diff --git a/user/event/event_bash.go b/user/event/event_bash.go index b4787fddc..bef3c4c16 100644 --- a/user/event/event_bash.go +++ b/user/event/event_bash.go @@ -18,30 +18,35 @@ import ( "bytes" "encoding/binary" "fmt" - - "golang.org/x/sys/unix" ) /* - u32 pid; - u8 line[MAX_DATE_SIZE_BASH]; - u32 Retval; - char Comm[TASK_COMM_LEN]; + u8 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 + BashType uint32 `json:"bashtype"` Pid uint32 `json:"pid"` Uid uint32 `json:"uid"` Line [MaxDataSizeBash]uint8 `json:"line"` Retval uint32 `json:"Retval"` Comm [16]byte `json:"Comm"` + AllLines string } func (be *BashEvent) Decode(payload []byte) (err error) { buf := bytes.NewBuffer(payload) + if err = binary.Read(buf, binary.LittleEndian, &be.BashType); err != nil { + return + } if err = binary.Read(buf, binary.LittleEndian, &be.Pid); err != nil { return } @@ -57,23 +62,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("PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s", be.Pid, be.Uid, be.Comm, be.Retval, be.AllLines) 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("PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s,", be.Pid, be.Uid, be.Comm, be.Retval, dumpByteSlice([]byte(be.AllLines), "")) return s } func (be *BashEvent) Clone() IEventStruct { event := new(BashEvent) - event.eventType = EventTypeOutput + event.eventType = EventTypeModuleData return event } diff --git a/user/module/imodule.go b/user/module/imodule.go index 93af21fb6..854ff0065 100644 --- a/user/module/imodule.go +++ b/user/module/imodule.go @@ -292,7 +292,11 @@ func (m *Module) Dispatcher(e event.IEventStruct) { // If Hex mode is enabled, data in hex format is directly printed for event processor and output events if m.conf.GetHex() { if e.EventType() == event.EventTypeEventProcessor || e.EventType() == event.EventTypeOutput { - m.logger.Println(e.StringHex()) + s := e.StringHex() + if s == "" { + return + } + m.logger.Println(s) return } } @@ -301,7 +305,11 @@ func (m *Module) Dispatcher(e event.IEventStruct) { // they will be handled according to multiple branches of the switch switch e.EventType() { case event.EventTypeOutput: - m.logger.Println(e.String()) + s := e.String() + if s == "" { + return + } + m.logger.Println(s) case event.EventTypeEventProcessor: m.processor.Write(e) case event.EventTypeModuleData: diff --git a/user/module/probe_bash.go b/user/module/probe_bash.go index 09f0c7833..cf5dfa1c9 100644 --- a/user/module/probe_bash.go +++ b/user/module/probe_bash.go @@ -31,12 +31,20 @@ import ( "golang.org/x/sys/unix" ) +const BASH_ERRNO_DEFAULT = 128 +const ( + BASH_EVENT_TYPE_READLINE = 0 + BASH_EVENT_TYPE_RETVAL = 1 + BASH_EVENT_TYPE_EXIT_OR_EXEC = 2 +) + type MBashProbe struct { Module bpfManager *manager.Manager bpfManagerOptions manager.Options eventFuncMaps map[*ebpf.Map]event.IEventStruct eventMaps []*ebpf.Map + lineMap map[string]string } // 对象初始化 @@ -46,6 +54,7 @@ func (b *MBashProbe) Init(ctx context.Context, logger *log.Logger, conf config.I b.Module.SetChild(b) b.eventMaps = make([]*ebpf.Map, 0, 2) b.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) + b.lineMap = make(map[string]string) return nil } @@ -173,7 +182,8 @@ func (b *MBashProbe) setupManagers() { b.logger.Printf("%s\tHOOK binrayPath:%s, FunctionName:%s\n", b.Name(), binaryPath, readlineFuncName) b.logger.Printf("%s\tHOOK binrayPath:%s, FunctionName:execute_command\n", b.Name(), binaryPath) - + b.logger.Printf("%s\tHOOK binrayPath:%s, FunctionName:exit_builtin\n", b.Name(), binaryPath) + b.logger.Printf("%s\tHOOK binrayPath:%s, FunctionName:exec_builtin\n", b.Name(), binaryPath) b.bpfManager = &manager.Manager{ Probes: []*manager.Probe{ { @@ -189,6 +199,18 @@ func (b *MBashProbe) setupManagers() { AttachToFuncName: "execute_command", BinaryPath: binaryPath, // 可能是 /bin/bash 也可能是 readline.so的真实地址 }, + { + Section: "uprobe/exec_builtin", + EbpfFuncName: "uprobe_exec_builtin", + AttachToFuncName: "exec_builtin", + BinaryPath: binaryPath, + }, + { + Section: "uprobe/exit_builtin", + EbpfFuncName: "uprobe_exit_builtin", + AttachToFuncName: "exit_builtin", + BinaryPath: binaryPath, + }, }, Maps: []*manager.Map{ @@ -246,6 +268,51 @@ func (b *MBashProbe) Events() []*ebpf.Map { return b.eventMaps } +func (b *MBashProbe) Dispatcher(eventStruct event.IEventStruct) { + be, ok := eventStruct.(*event.BashEvent) + if !ok { + return + } + b.handleLine(be) +} + +func (b *MBashProbe) handleLine(be *event.BashEvent) { + switch be.BashType { + case BASH_EVENT_TYPE_READLINE: + newline := unix.ByteSliceToString((be.Line[:])) + line := b.lineMap[be.GetUUID()] + if line != "" { + line += "\n" + newline + } else { + line += newline + } + b.lineMap[be.GetUUID()] = line + return + case BASH_EVENT_TYPE_RETVAL: + line := b.lineMap[be.GetUUID()] + delete(b.lineMap, be.GetUUID()) + if line == "" || be.Retval == BASH_ERRNO_DEFAULT { + return + } + be.AllLines = line + case BASH_EVENT_TYPE_EXIT_OR_EXEC: + line := b.lineMap[be.GetUUID()] + delete(b.lineMap, be.GetUUID()) + if line == "" { + return + } + be.Retval = BASH_EVENT_TYPE_EXIT_OR_EXEC // we do not know the return value here + be.AllLines = line + default: + return + } + if b.conf.GetHex() { + b.logger.Println(be.StringHex()) + } else { + b.logger.Println(be.String()) + } +} + func init() { mod := &MBashProbe{} mod.name = ModuleNameBash