package main import ( "fmt" "os" "regexp" "strconv" "strings" "sync" "syscall" "github.com/elastic/go-libaudit/v2" "github.com/elastic/go-libaudit/v2/auparse" ) // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 var eventTable sync.Map // 事件信息 var tmp any var ok bool var event Event var pEvent *Event var eventId, argc int // var errs [6]error // 要用的正则匹配列表 var ( syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+)(?:.*?exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?subj=(.*?):(.*?):(.*?):(.*?) .*?$`) execveRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) argsRegex = regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) pathRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): item=(\d+) name="(.*?)"`) cwdRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`) proctitleRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`) eoeRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`) ) func orgnaze() { defer wg.Done() defer close(cookedChan) // 接收信息 var raw interface{} var rawEvent libaudit.RawAuditMessage for { raw, ok = <-rawChan if !ok { break } rawEvent = raw.(libaudit.RawAuditMessage) // fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data) switch rawEvent.Type { case auparse.AUDIT_SYSCALL: syscallRaw(rawEvent) case auparse.AUDIT_EXECVE: execve(rawEvent) case auparse.AUDIT_CWD: cwd(rawEvent) case auparse.AUDIT_PATH: path(rawEvent) case auparse.AUDIT_PROCTITLE: proctitle(rawEvent) case auparse.AUDIT_EOE: eoe(rawEvent) default: } } } func syscallRaw(rawEvent libaudit.RawAuditMessage) { if !syscallRegex.Match(rawEvent.Data) { return } var exit int var a [4]uint64 var subj [4]string // 捕获基础信息 match := syscallRegex.FindSubmatch(rawEvent.Data) event.timestamp, _ = getTimeFromStr(string(match[1])) eventId, _ = strconv.Atoi(string(match[2])) event.syscall, _ = strconv.Atoi(string(match[3])) if string(match[4]) == "" { // exit没捕获到 exit = 0 } else { exit, _ = strconv.Atoi(string(match[4])) } event.ppid, _ = strconv.Atoi(string(match[5])) event.pid, _ = strconv.Atoi(string(match[6])) // 几个subj,说不定会有用 for i := 0; i < 4; i++ { subj[i] = string(match[7+i]) } // 捕获参数 if !argsRegex.Match(rawEvent.Data) { fmt.Fprintf(os.Stderr, "Error: don't get args in syscall event!\n") return } argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1) for i := 0; i < 4; i++ { a[i], _ = strconv.ParseUint(string(argsMatch[i][3]), 16, 64) } switch syscallTable[event.syscall] { case "execve": eventTable.Store(eventId, &Event{ tag: EXECVE, timestamp: event.timestamp, syscall: event.syscall, exit_code: a[0], ppid: event.ppid, pid: event.pid, argc: 0, argv: make([]string, 0), cwd: "", }) case "open": // 检查打开的权限 if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 { break } // TRUNC应该被直接标记为改变,而不是打开 eventTable.Store(eventId, &Event{ tag: FILEOPEN, timestamp: event.timestamp, syscall: event.syscall, exit_code: uint64(exit), ppid: event.ppid, pid: event.pid, argc: 0, argv: make([]string, 0), cwd: "", syscallParam: a, srcPath: "", }) case "write": eventTable.Store(eventId, &Event{ tag: FILEWRITE, timestamp: event.timestamp, syscall: event.syscall, exit_code: uint64(exit), ppid: event.ppid, pid: event.pid, argc: 0, argv: make([]string, 0), cwd: "", syscallParam: a, }) case "close": // 文件关闭 eventTable.Store(eventId, &Event{ tag: FILECLOSE, timestamp: event.timestamp, syscall: event.syscall, exit_code: uint64(exit), ppid: event.ppid, pid: event.pid, argc: 0, argv: make([]string, 0), cwd: "", syscallParam: a, }) case "pivot_root": if subj[2] == "container_runtime_t" { eventTable.Store(eventId, &Event{ tag: PIVOTROOT, timestamp: event.timestamp, syscall: event.syscall, ppid: event.ppid, pid: event.pid, syscallParam: a, }) } } } func execve(rawEvent libaudit.RawAuditMessage) { if !execveRegex.Match(rawEvent.Data) { return } match := execveRegex.FindSubmatch(rawEvent.Data) eventId, _ = strconv.Atoi(string(match[1])) argc, _ = strconv.Atoi(string(match[2])) tmp, ok = eventTable.Load(eventId) if !ok { return } pEvent = tmp.(*Event) if argsRegex.Match(rawEvent.Data) { match := argsRegex.FindAllSubmatch(rawEvent.Data, -1) for i := 0; i < argc; i++ { if len(match[i][2]) == 0 { // 代表着匹配到的是十六进制数 str := hexToAscii(string(match[i][3])) pEvent.argv = append(pEvent.argv, str) } else { pEvent.argv = append(pEvent.argv, string(match[i][2])) } } pEvent.argc = argc } } func cwd(rawEvent libaudit.RawAuditMessage) { if !cwdRegex.Match(rawEvent.Data) { return } match := cwdRegex.FindSubmatch(rawEvent.Data) eventId, _ = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return } tmp.(*Event).cwd = string(match[2]) } func proctitle(rawEvent libaudit.RawAuditMessage) { if !proctitleRegex.Match(rawEvent.Data) { return } var cmdline string match := proctitleRegex.FindSubmatch(rawEvent.Data) eventId, _ = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return } pEvent = tmp.(*Event) if pEvent.argc == 0 { // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数 if match[3] == nil { // PROCTITLE写的是十六进制,转换为字符串 cmdline = hexToAscii(string(match[4])) } else { cmdline = string(match[3]) } pEvent.argv = strings.Split(cmdline, " ") pEvent.argc = len(pEvent.argv) } } func eoe(rawEvent libaudit.RawAuditMessage) { if !eoeRegex.Match(rawEvent.Data) { return } match := eoeRegex.FindSubmatch(rawEvent.Data) eventId, _ = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return } cooked := *(tmp.(*Event)) cookedChan <- cooked eventTable.Delete(eventId) // 死人别占地 } func path(rawEvent libaudit.RawAuditMessage) { if !pathRegex.Match(rawEvent.Data) { return } match := pathRegex.FindSubmatch(rawEvent.Data) eventId, _ = strconv.Atoi(string(match[1])) // item, _ := strconv.Atoi(string(match[2])) name := string(match[3]) tmp, ok = eventTable.Load(eventId) if !ok { return } pEvent = tmp.(*Event) // 先看看是不是文件操作 if pEvent.tag != FILEOPEN { return } if name[0] == '/' { pEvent.srcPath = name } else { pEvent.srcPath += "/" + name } }