package main import ( "fmt" "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+).*?$`) 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+)\):.*?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) switch rawEvent.Type { case auparse.AUDIT_SYSCALL: go syscallRaw(rawEvent) case auparse.AUDIT_EXECVE: go execve(rawEvent) case auparse.AUDIT_CWD: go cwd(rawEvent) case auparse.AUDIT_PATH: go path(rawEvent) case auparse.AUDIT_PROCTITLE: go proctitle(rawEvent) case auparse.AUDIT_EOE: go eoe(rawEvent) default: // ATTENTION: 这里也需要做防护 } } } func syscallRaw(rawEvent libaudit.RawAuditMessage) { if !syscallRegex.Match(rawEvent.Data) { return } var exit int var a [4]uint64 // 捕获基础信息 match := syscallRegex.FindSubmatch(rawEvent.Data) event.timestamp, errs[0] = getTimeFromStr(string(match[1])) eventId, errs[1] = strconv.Atoi(string(match[2])) event.syscall, errs[2] = strconv.Atoi(string(match[3])) if string(match[4]) == "" { // exit没捕获到 exit = 0 } else { exit, errs[3] = strconv.Atoi(string(match[4])) } event.ppid, errs[4] = strconv.Atoi(string(match[5])) event.pid, errs[5] = strconv.Atoi(string(match[6])) // 捕获参数 if !argsRegex.Match(rawEvent.Data) { fmt.Printf("Error: don't get args in syscall event!\n") return } argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1) for i := 0; i < 4; i++ { a[i], errs[0] = 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, pathName: "", }) 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, // pathName: "", }) 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, // pathName: "", }) } } func execve(rawEvent libaudit.RawAuditMessage) { if !execveRegex.Match(rawEvent.Data) { return } match := execveRegex.FindSubmatch(rawEvent.Data) eventId, errs[0] = strconv.Atoi(string(match[1])) argc, errs[1] = strconv.Atoi(string(match[2])) tmp, ok = eventTable.Load(eventId) if !ok { return } pEvent = tmp.(*Event) if errs[0] == nil && errs[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 } } func cwd(rawEvent libaudit.RawAuditMessage) { if !cwdRegex.Match(rawEvent.Data) { return } match := cwdRegex.FindSubmatch(rawEvent.Data) eventId, errs[0] = 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, errs[0] = 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, errs[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return } cooked := *(tmp.(*Event)) cookedChan <- cooked // fmt.Printf("Send: %10d\t%v\t%7d\t%7d\n", eventId, cooked.tag, cooked.ppid, cooked.pid) eventTable.Delete(eventId) // 死人别占地 } func path(rawEvent libaudit.RawAuditMessage) { if !pathRegex.Match(rawEvent.Data) { return } match := pathRegex.FindSubmatch(rawEvent.Data) eventId, errs[0] = strconv.Atoi(string(match[1])) name := string(match[2]) tmp, ok = eventTable.Load(eventId) if !ok { return } pEvent = tmp.(*Event) // 先看看是不是文件操作 if pEvent.tag != FILEOPEN { return } if name[0] == '/' { pEvent.pathName = name } else { pEvent.pathName += "/" + name } }