package main import ( "fmt" "regexp" "strconv" "strings" "sync" "github.com/elastic/go-libaudit/v2" "github.com/elastic/go-libaudit/v2/auparse" ) func orgnaze() { defer wg.Done() defer close(cookedChan) // 接收信息 var raw interface{} var ok bool var rawEvent libaudit.RawAuditMessage // 事件信息 var eventId, argc int var err [6]error var event Event var pEvent *Event var tmp any // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 var eventTable sync.Map // 要用的正则匹配列表 syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?(exit=([-+]?\d+))? a0=([0-9a-fA-F]+).*?ppid=(\d+) pid=(\d+).*?$`) execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) 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+)\)`) for { raw, ok = <-rawChan if !ok { break } rawEvent = raw.(libaudit.RawAuditMessage) switch rawEvent.Type { case auparse.AUDIT_SYSCALL: if syscallRegex.Match(rawEvent.Data) { match := syscallRegex.FindSubmatch(rawEvent.Data) event.timestamp, err[0] = getTimeFromStr(string(match[1])) eventId, err[1] = strconv.Atoi(string(match[2])) event.syscall, err[2] = strconv.Atoi(string(match[3])) var exit int var a0 uint64 if string(match[5]) == "" { // exit没捕获到 exit = 0 } else { exit, err[3] = strconv.Atoi(string(match[5])) } if string(match[6]) == "" { a0 = 0 } else { // 系统调用的第一个参数 // exit和exit_group都是syscall_define1,只有一个参数 // fork没参数,clone几个参数不重要 // execve三个参数咱也不关心 // 所以看第一个就够了 a0, err[4] = strconv.ParseUint(string(match[6]), 16, 64) } event.ppid, err[4] = strconv.Atoi(string(match[7])) event.pid, err[5] = strconv.Atoi(string(match[8])) if syscallTable[event.syscall] == "clone" { if exit == 0 || event.pid > exit { // exit=0是给新进程的返回,没用 // pid>exit,证明有问题,抛弃 break } else { eventTable.Store(eventId, &Event{ timestamp: event.timestamp, syscall: event.syscall, exit_code: 0, ppid: event.pid, pid: exit, argc: 0, argv: make([]string, 0), cwd: "", }) } } else { eventTable.Store(eventId, &Event{ timestamp: event.timestamp, syscall: event.syscall, exit_code: a0, ppid: event.ppid, pid: event.pid, argc: 0, argv: make([]string, 0), cwd: "", }) } } case auparse.AUDIT_EXECVE: if execveRegex.Match(rawEvent.Data) { match := execveRegex.FindSubmatch(rawEvent.Data) eventId, err[0] = strconv.Atoi(string(match[1])) argc, err[1] = strconv.Atoi(string(match[2])) tmp, ok = eventTable.Load(eventId) if !ok { break } pEvent = tmp.(*Event) if err[0] == nil && err[1] == nil && 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 } } case auparse.AUDIT_CWD: if cwdRegex.Match(rawEvent.Data) { match := cwdRegex.FindSubmatch(rawEvent.Data) eventId, err[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { break } tmp.(*Event).cwd = string(match[2]) } case auparse.AUDIT_PROCTITLE: if proctitleRegex.Match(rawEvent.Data) { var cmdline string match := proctitleRegex.FindSubmatch(rawEvent.Data) eventId, err[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { break } 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) } } case auparse.AUDIT_EOE: if eoeRegex.Match(rawEvent.Data) { match := eoeRegex.FindSubmatch(rawEvent.Data) eventId, err[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { break } cooked := *(tmp.(*Event)) cookedChan <- cooked eventTable.Delete(eventId) // 死人别占地 fmt.Printf("%d: %3d %6d %6d\n", eventId, cooked.syscall, cooked.ppid, cooked.pid) } default: // ATTENTION: 这里也需要做防护 } } }