From ec260a31927ef77295eaa07ba370b58b416f47f5 Mon Sep 17 00:00:00 2001 From: We-unite <3205135446@qq.com> Date: Fri, 26 Jul 2024 17:23:53 +0800 Subject: Fix execve before fork & Fix regex to match "exit" There's 2 bugs from ancestor commits: - In the 'things_left' tag commit(the grandpa of this commit), we add a function that allows execve comes before fork, but when it happens, I forget to insert the basic info (pid, ppid, etc.), as a result of which it doesn't work in the designed way. Now it is well, insert execve with pid and ppid, so that the fork event can find it and finish other info. However, we shouldn't make start_stamp in this case, so that it's also a flag. I've not removed the unused execve info, waiting for the future. - In the parent commit, the syscallRegex is changed, because when we add more syscalls to be watched, we need more info about their params but not only the first one. Instead of keeping using single a0 to get the first param, i use argsRegex for all the params. But this change causes mismatch of syscallRegex. Now it's fixed. --- .gitignore | 2 + src/deal.go | 226 ++++++++++++++++++++++++++++++-------------------------- src/global.go | 3 +- src/organize.go | 47 +++++++----- 4 files changed, 156 insertions(+), 122 deletions(-) diff --git a/.gitignore b/.gitignore index 9fe8ea9..bfe22fb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ old/go.* */*.log */*.json !logs/*.log + +__debug_bin* diff --git a/src/deal.go b/src/deal.go index db6fc26..a9861a5 100644 --- a/src/deal.go +++ b/src/deal.go @@ -16,32 +16,18 @@ const ( var mongoMutex sync.Mutex var pidCol mongoClient +var docRes []bson.M +var err error + func deal() { defer wg.Done() var cooked Event var ok bool - var err error - var res []bson.M - - if err = pidCol.Connect(dbName, pidColName); err != nil { - fmt.Printf("Error connecting the mongodb: %v\n", err) - } - if err = pidCol.Drop(); err != nil { - fmt.Printf("Error drop the mongodb: %v\n", err) - } - - err = pidCol.InsertOne(bson.M{ - "ppid": 1, - "pid": containerdPid, - "cwd": "/", - "children": bson.M{}, - }) - if err != nil { - fmt.Printf("Err containerd: %v", err) + if err = initMongo(); err != nil { + fmt.Printf("Error while initing the mongodb: %v\n", err) return } - fmt.Printf("Containerd: %d\n", containerdPid) defer pidCol.Disconnect() for { @@ -50,91 +36,12 @@ func deal() { break } - switch syscallTable[cooked.syscall] { - case "clone": - // 有无父进程在观察中 - res, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) - if err != nil || len(res) != 1 { - break - } - - // 自身是否已经记录 - res, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) - if err != nil { - fmt.Printf("Err finding: %v\n", err) - break - } - mongoMutex.Lock() - if len(res) != 0 { - // 进程原本就存在,换言之别的消息先到了 - // 所有先行抵达的消息必须保留execve/children字段 - // 此处不再更新 - // 以防把原有信息更没了 - pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ - "start_timestamp": cooked.timestamp, - "ppid": cooked.ppid, - "pid": cooked.pid, - "cwd": cooked.cwd, - // "execve": []bson.M{}, - "args": cooked.argv, - // "children": []bson.M{}, - }) - } else { - // 这进程本是新修的 - pidCol.InsertOne(bson.M{ - "start_timestamp": cooked.timestamp, - "ppid": cooked.ppid, - "pid": cooked.pid, - "cwd": cooked.cwd, - "execve": []bson.M{}, - "args": cooked.argv, - "children": []bson.M{}, - }) - } - - pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{ - "$push": bson.M{ - "children": cooked.pid, - }, - }) - mongoMutex.Unlock() - case "execve": - // 父进程在不在?不在扔 - res, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) - if err != nil || len(res) != 1 { - break - } - - // 首先检查进程是否存在,如不存在则为之创建 - res, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) - if err != nil { - break - } - mongoMutex.Lock() - if len(res) == 1 { - // 自身已在,直接记录 - pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ - "$push": bson.M{ - "execve": bson.M{ - "timestamp": cooked.timestamp, - "args": cooked.argv, - }, - }, - }) - } else { - // 先fork抵达,插入 - pidCol.InsertOne(bson.M{ - "children": []bson.M{}, - "exe_args": []bson.M{ - { - "timestamp": cooked.timestamp, - "execve": cooked.argv, - }, - }, - }) - } - mongoMutex.Unlock() - case "exit", "exit_group": + switch cooked.tag { + case NEWPID: + dealNewPid(cooked) + case EXECVE: + dealExecve(cooked) + case PIDEXIT: go deletePid(cooked) } } @@ -163,3 +70,114 @@ func deletePid(cooked Event) { }) mongoMutex.Unlock() } + +func initMongo() error { + var err error + if err = pidCol.Connect(dbName, pidColName); err != nil { + return err + } + if err = pidCol.Drop(); err != nil { + return err + } + + err = pidCol.InsertOne(bson.M{ + "ppid": 1, + "pid": containerdPid, + "cwd": "/", + "children": bson.M{}, + }) + if err != nil { + return err + } + fmt.Printf("Containerd: %d\n", containerdPid) + return nil +} + +func dealNewPid(cooked Event) { + // 有无父进程在观察中 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) + if err != nil || len(docRes) != 1 { + return + } + + // 自身是否已经记录 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) + if err != nil { + fmt.Printf("Err finding: %v\n", err) + return + } + mongoMutex.Lock() + if len(docRes) != 0 { + // 进程原本就存在,换言之别的消息先到了 + // 所有先行抵达的消息必须保留execve/children字段 + // 此处不再更新 + // 以防把原有信息更没了 + pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ + "start_timestamp": cooked.timestamp, + "ppid": cooked.ppid, + "pid": cooked.pid, + "cwd": cooked.cwd, + // "execve": []bson.M{}, + "args": cooked.argv, + // "children": []bson.M{}, + }) + } else { + // 这进程本是新修的 + pidCol.InsertOne(bson.M{ + "start_timestamp": cooked.timestamp, + "ppid": cooked.ppid, + "pid": cooked.pid, + "cwd": cooked.cwd, + "execve": []bson.M{}, + "args": cooked.argv, + "children": []bson.M{}, + }) + } + + pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{ + "$push": bson.M{ + "children": cooked.pid, + }, + }) + mongoMutex.Unlock() +} + +func dealExecve(cooked Event) { + // 父进程在不在?不在扔 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) + if err != nil || len(docRes) != 1 { + return + } + + // 首先检查进程是否存在,如不存在则为之创建 + docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) + if err != nil { + return + } + mongoMutex.Lock() + if len(docRes) == 1 { + // 自身已在,直接记录 + pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{ + "$push": bson.M{ + "execve": bson.M{ + "timestamp": cooked.timestamp, + "execArgs": cooked.argv, + }, + }, + }) + } else { + // 先fork抵达,插入 + pidCol.InsertOne(bson.M{ + "ppid": cooked.ppid, + "pid": cooked.pid, + "children": []bson.M{}, + "execve": []bson.M{ + { + "timestamp": cooked.timestamp, + "execArgs": cooked.argv, + }, + }, + }) + } + mongoMutex.Unlock() +} diff --git a/src/global.go b/src/global.go index 3ddbc79..7401dc5 100644 --- a/src/global.go +++ b/src/global.go @@ -10,6 +10,7 @@ type eventType int const ( NEWPID eventType = iota PIDEXIT + EXECVE FILEOPEN FILEWRITE TYPENUM @@ -29,7 +30,7 @@ type Event struct { } func (et eventType) String() string { - names := []string{"newPid", "pidExit", "open", "write", "typeNum"} + names := []string{"NEWPID", "PIDEXIT", "EXECVE", "FILEOPEN", "FILEWRITE", "TYPENUM"} if et < NEWPID || et > TYPENUM { return "Unknown" } diff --git a/src/organize.go b/src/organize.go index 679f361..2489961 100644 --- a/src/organize.go +++ b/src/organize.go @@ -21,11 +21,11 @@ var ok bool var event Event var pEvent *Event var eventId, argc int -var err [6]error +var errs [6]error // 要用的正则匹配列表 var ( - syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?(exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?$`) + 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="(.*?)"`) @@ -76,17 +76,17 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) { var a [4]uint64 // 捕获基础信息 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])) - if string(match[5]) == "" { + 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, err[3] = strconv.Atoi(string(match[5])) + exit, errs[3] = strconv.Atoi(string(match[4])) } - event.ppid, err[4] = strconv.Atoi(string(match[6])) - event.pid, err[5] = strconv.Atoi(string(match[7])) + event.ppid, errs[4] = strconv.Atoi(string(match[5])) + event.pid, errs[5] = strconv.Atoi(string(match[6])) // 捕获参数 if !argsRegex.Match(rawEvent.Data) { @@ -95,7 +95,7 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) { } argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1) for i := 0; i < 4; i++ { - a[i], err[0] = strconv.ParseUint(string(argsMatch[i][2]), 16, 64) + a[i], errs[0] = strconv.ParseUint(string(argsMatch[i][2]), 16, 64) } switch syscallTable[event.syscall] { @@ -136,6 +136,18 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) { syscallParam: a, pathName: "", }) + 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 "exit", "exit_group": eventTable.Store(eventId, &Event{ tag: PIDEXIT, @@ -157,14 +169,14 @@ func execve(rawEvent libaudit.RawAuditMessage) { } match := execveRegex.FindSubmatch(rawEvent.Data) - eventId, err[0] = strconv.Atoi(string(match[1])) - argc, err[1] = strconv.Atoi(string(match[2])) + 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 err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) { + 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 { @@ -185,7 +197,7 @@ func cwd(rawEvent libaudit.RawAuditMessage) { } match := cwdRegex.FindSubmatch(rawEvent.Data) - eventId, err[0] = strconv.Atoi(string(match[1])) + eventId, errs[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return @@ -200,7 +212,7 @@ func proctitle(rawEvent libaudit.RawAuditMessage) { var cmdline string match := proctitleRegex.FindSubmatch(rawEvent.Data) - eventId, err[0] = strconv.Atoi(string(match[1])) + eventId, errs[0] = strconv.Atoi(string(match[1])) tmp, ok = eventTable.Load(eventId) if !ok { return @@ -225,13 +237,14 @@ func eoe(rawEvent libaudit.RawAuditMessage) { } match := eoeRegex.FindSubmatch(rawEvent.Data) - eventId, err[0] = strconv.Atoi(string(match[1])) + 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) // 死人别占地 } @@ -240,7 +253,7 @@ func path(rawEvent libaudit.RawAuditMessage) { return } match := pathRegex.FindSubmatch(rawEvent.Data) - eventId, err[0] = strconv.Atoi(string(match[1])) + eventId, errs[0] = strconv.Atoi(string(match[1])) name := string(match[2]) tmp, ok = eventTable.Load(eventId) -- cgit v1.2.3-70-g09d2