aboutsummaryrefslogtreecommitdiffstats
path: root/listener
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2024-08-09 13:56:37 +0800
committerWe-unite <3205135446@qq.com>2024-08-12 14:16:51 +0800
commit3e49a044d22635157916651f0acb5a062397b34b (patch)
tree254cd9a2605fa003f4579e7c5510e6e2aea19375 /listener
parentea32e017e579f168d87732893335c38d539ac2f1 (diff)
downloadgodo-3e49a044d22635157916651f0acb5a062397b34b.tar.gz
godo-3e49a044d22635157916651f0acb5a062397b34b.zip
Add db structure, fix filePath, start filtering
This commit I made several changes: - Use structure instead of simple bson.M(interface{}). bson.M has some shortcomings: 1) It makes the database in chaos and hard to read, but this's not important; 2) Some entrys may has more or less content than others, which makes it hard to decode and filt. So I design new data structure to encode and decode. Hopes that there's no bugs. - Fix the way to calculate file path. The original method is to add all the PATH entries together, that's totally wrong! PATH entry has several types, as it shows in "objtype". I can't find it in the kernel src code, so what i know is just "PARENT" means the dir the file is in, while the filename itself has the path, so we whould ignore all "PARENT"s. When the src code is found, we should check it again. - Fix bugs in updating. The update function of mongodb is set to required to has a '$' such as 'set'/'push', so when we update a whole doc, we should use replace but not update function. And, we should never ignore the error infomation it gives us. Hope that there's no more bugs for this Big Change. Now its' time to write filter as well as viewer. Best wishes with NO BUGS!
Diffstat (limited to 'listener')
-rw-r--r--listener/audit.go84
-rw-r--r--listener/basefunc.go129
-rw-r--r--listener/deal.go269
-rw-r--r--listener/global.go84
-rw-r--r--listener/go.mod26
-rw-r--r--listener/go.sum96
-rw-r--r--listener/go.work6
-rw-r--r--listener/go.work.sum4
-rw-r--r--listener/godo.go176
-rw-r--r--listener/mongo.go122
m---------listener/netlink0
-rw-r--r--listener/organize.go291
-rw-r--r--listener/receive.go29
13 files changed, 1316 insertions, 0 deletions
diff --git a/listener/audit.go b/listener/audit.go
new file mode 100644
index 0000000..ed48691
--- /dev/null
+++ b/listener/audit.go
@@ -0,0 +1,84 @@
1package main
2
3import (
4 "fmt"
5 "io"
6 "log"
7 "os"
8
9 "github.com/elastic/go-libaudit/v2"
10)
11
12func read() error {
13 // Write netlink response to a file for further analysis or for writing
14 // tests cases.
15 var diagWriter io.Writer
16 if *diag != "" {
17 f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o600)
18 if err != nil {
19 return err
20 }
21 defer f.Close()
22 diagWriter = f
23 }
24
25 log.Println("starting netlink client")
26
27 var err error
28 var client *libaudit.AuditClient
29 if *receiveOnly {
30 client, err = libaudit.NewMulticastAuditClient(diagWriter)
31 if err != nil {
32 return fmt.Errorf("failed to create receive-only audit client: %w", err)
33 }
34 defer client.Close()
35 } else {
36 client, err = libaudit.NewAuditClient(diagWriter)
37 if err != nil {
38 return fmt.Errorf("failed to create audit client: %w", err)
39 }
40 defer client.Close()
41
42 status, err := client.GetStatus()
43 if err != nil {
44 return fmt.Errorf("failed to get audit status: %w", err)
45 }
46 log.Printf("received audit status=%+v", status)
47
48 if status.Enabled == 0 {
49 log.Println("enabling auditing in the kernel")
50 if err = client.SetEnabled(true, libaudit.WaitForReply); err != nil {
51 return fmt.Errorf("failed to set enabled=true: %w", err)
52 }
53 }
54
55 if status.RateLimit != uint32(*rate) {
56 log.Printf("setting rate limit in kernel to %v", *rate)
57 if err = client.SetRateLimit(uint32(*rate), libaudit.NoWait); err != nil {
58 return fmt.Errorf("failed to set rate limit to unlimited: %w", err)
59 }
60 }
61
62 if status.BacklogLimit != uint32(*backlog) {
63 log.Printf("setting backlog limit in kernel to %v", *backlog)
64 if err = client.SetBacklogLimit(uint32(*backlog), libaudit.NoWait); err != nil {
65 return fmt.Errorf("failed to set backlog limit: %w", err)
66 }
67 }
68
69 if status.Enabled != 2 && *immutable {
70 log.Printf("setting kernel settings as immutable")
71 if err = client.SetImmutable(libaudit.NoWait); err != nil {
72 return fmt.Errorf("failed to set kernel as immutable: %w", err)
73 }
74 }
75
76 log.Printf("sending message to kernel registering our PID (%v) as the audit daemon", os.Getpid())
77 if err = client.SetPID(libaudit.NoWait); err != nil {
78 return fmt.Errorf("failed to set audit PID: %w", err)
79 }
80 }
81
82 coroutine(client)
83 return nil
84}
diff --git a/listener/basefunc.go b/listener/basefunc.go
new file mode 100644
index 0000000..2f39507
--- /dev/null
+++ b/listener/basefunc.go
@@ -0,0 +1,129 @@
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "strconv"
10 "strings"
11 "time"
12)
13
14func figureOutSyscalls() error {
15 cmd := exec.Command("ausyscall", "--dump")
16 stdout, err := cmd.StdoutPipe()
17 if err != nil {
18 return err
19 }
20
21 if err := cmd.Start(); err != nil {
22 return err
23 }
24
25 scanner := bufio.NewScanner(stdout)
26 for i := 0; scanner.Scan(); i++ {
27 if i == 0 {
28 continue
29 }
30 line := scanner.Text()
31 parts := strings.Split(line, "\t")
32 if len(parts) != 2 {
33 return fmt.Errorf("invalid ausyscall format")
34 }
35 num, err := strconv.Atoi(parts[0])
36 if err != nil {
37 return err
38 }
39 syscallTable[num] = parts[1]
40 }
41
42 if err := scanner.Err(); err != nil {
43 return err
44 }
45 if err := cmd.Wait(); err != nil {
46 return err
47 }
48 return nil
49}
50
51func getPid() (int, error) {
52 // 指定要搜索的关键词
53 keyword := "/usr/bin/containerd"
54
55 // 获取/proc目录下的所有子目录
56 procDir, err := filepath.Glob("/proc/*")
57 if err != nil {
58 return 0, err
59 }
60
61 // 遍历子目录,查找包含关键词的进程
62 for _, dir := range procDir {
63 pid, err := strconv.Atoi(filepath.Base(dir))
64 if err != nil {
65 continue // 跳过非PID的目录
66 }
67
68 // 检查进程是否包含关键词
69 if containsKeyword(pid, keyword) {
70 return pid, nil
71 }
72 }
73 err = fmt.Errorf("Error: no containerd process found.")
74 return 0, err
75}
76
77func containsKeyword(pid int, keyword string) bool {
78 // 构造完整的进程命令路径
79 cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
80
81 // 打开文件
82 file, err := os.Open(cmdPath)
83 if err != nil {
84 return false
85 }
86 defer file.Close()
87
88 // 读取文件内容
89 scanner := bufio.NewScanner(file)
90 scanner.Split(bufio.ScanLines)
91 for scanner.Scan() {
92 line := scanner.Text()
93 if strings.Contains(line, keyword) {
94 return true
95 }
96 }
97 return false
98}
99
100func getTimeFromStr(timeStr string) (time.Time, error) {
101 timestampFloat, err := strconv.ParseFloat(timeStr, 64)
102 if err != nil {
103 return time.Unix(0, 0), err
104 }
105 secs := int64(timestampFloat)
106 nsecs := int64((timestampFloat - float64(secs)) * 1e9)
107
108 // 只精确到毫秒就够了
109 t := time.Unix(secs, nsecs).Truncate(time.Millisecond)
110 return t, nil
111}
112
113func hexToAscii(hexString string) string {
114 bytes := []byte{}
115 for i := 0; i < len(hexString); i += 2 {
116 hexPair := hexString[i : i+2]
117 // 将十六进制数转换为十进制数
118 decimal, err := strconv.ParseInt(hexPair, 16, 8)
119 if err != nil {
120 return "Invalid hex string"
121 }
122 char := byte(decimal)
123 bytes = append(bytes, char)
124 }
125
126 asciiString := strings.ReplaceAll(string(bytes), "\000", " ")
127
128 return asciiString
129}
diff --git a/listener/deal.go b/listener/deal.go
new file mode 100644
index 0000000..8f77431
--- /dev/null
+++ b/listener/deal.go
@@ -0,0 +1,269 @@
1package main
2
3import (
4 "fmt"
5 "os"
6 "syscall"
7 "time"
8
9 "go.mongodb.org/mongo-driver/bson"
10)
11
12const (
13 dbName string = "test"
14 pidColName string = "pids"
15 fdColName string = "fds"
16 fileColName string = "files"
17)
18
19var pidCol, fdCol, fileCol mongoClient
20var err error
21
22func deal() {
23 defer wg.Done()
24 var cooked Event
25 var ok bool
26
27 if err = pidCol.init(dbName, pidColName); err != nil {
28 fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err)
29 return
30 }
31 err = pidCol.InsertOne(Process{
32 Ppid: 1,
33 Pid: containerdPid,
34 Cwd: "/",
35 Children: make([]int, 0),
36 Star: true,
37 })
38 if err != nil {
39 fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err)
40 return
41 }
42
43 if err = fdCol.init(dbName, fdColName); err != nil {
44 fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err)
45 return
46 }
47 if err = fileCol.init(dbName, fileColName); err != nil {
48 fmt.Fprintf(os.Stderr, "Error while initing the mongodb: %v\n", err)
49 }
50
51 defer pidCol.Disconnect()
52 defer fdCol.Disconnect()
53 defer fileCol.Disconnect()
54
55 for {
56 cooked, ok = <-cookedChan
57 if !ok {
58 break
59 }
60
61 switch cooked.tag {
62 case NEWPID:
63 go dealNewPid(cooked)
64 case EXECVE:
65 go dealExecve(cooked)
66 case PIDEXIT:
67 go deletePid(cooked)
68 case FILEOPEN:
69 go fileOpen(cooked)
70 case FILEWRITE:
71 go fileWrite(cooked)
72 case FILECLOSE:
73 go fileClose(cooked)
74 case PIVOTROOT:
75 go pivotRoot(cooked)
76 }
77 }
78}
79
80func deletePid(cooked Event) {
81 // 在这套逻辑里,孩子是不需要收容的,因为我们根本就不看ppid来工作
82 // 父节点那里也不需要销户
83 // 理论上这里需要关闭所有文件描述符,但为了处理效率,留给后续流程
84 err := pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{
85 "$set": bson.M{
86 "exit_timestamp": cooked.timestamp,
87 "exit_code": cooked.exit_code,
88 "exit_signal": cooked.exit_signal,
89 },
90 })
91 if err != nil {
92 fmt.Fprintf(os.Stderr, "Err updating: %v\n", err)
93 }
94}
95
96func dealNewPid(cooked Event) {
97 // 自身是否已经记录
98 var docRes []Process
99 err = pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes)
100 if err != nil {
101 fmt.Fprintf(os.Stderr, "Err finding: %v\n", err)
102 return
103 }
104
105 if len(docRes) != 0 {
106 // 进程原本就存在,换言之别的消息先到了
107 // 所有先行抵达的消息必须保留execve/children字段
108 docRes[0].Ppid = cooked.ppid
109 docRes[0].ParentTgid = cooked.parentTgid
110 docRes[0].Pid = cooked.pid
111 docRes[0].Tgid = cooked.tgid
112 docRes[0].Cwd = cooked.cwd
113 docRes[0].Comm = cooked.comm
114 docRes[0].Args = cooked.argv
115
116 err := pidCol.ReplaceOne(bson.M{"pid": cooked.pid}, docRes[0])
117 if err != nil {
118 fmt.Fprintf(os.Stderr, "Err replaceing: %v\n", err)
119 }
120 } else {
121 // 这进程本是新修的
122 err := pidCol.InsertOne(Process{
123 StartTimestamp: cooked.timestamp,
124 Ppid: cooked.ppid,
125 ParentTgid: cooked.parentTgid,
126 Pid: cooked.pid,
127 Tgid: cooked.tgid,
128 Args: cooked.argv,
129 Comm: cooked.comm,
130 Cwd: cooked.cwd,
131 Execve: make([]Exec, 0),
132 Children: make([]int, 0),
133 })
134 if err != nil {
135 fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err)
136 }
137 }
138
139 err := pidCol.UpdateOne(bson.M{"pid": cooked.ppid}, bson.M{
140 "$push": bson.M{
141 "children": cooked.pid,
142 },
143 })
144 if err != nil {
145 fmt.Fprintf(os.Stderr, "Err updating: %v\n", err)
146 }
147}
148
149func dealExecve(cooked Event) {
150 var docRes []Process
151 // 首先检查进程是否存在,如不存在则为之创建
152 err = pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes)
153 if err != nil {
154 return
155 }
156
157 if len(docRes) == 1 {
158 // 自身已在,直接记录
159 docRes[0].Execve = append(docRes[0].Execve, Exec{
160 Timestamp: cooked.timestamp,
161 ExecArgs: cooked.argv,
162 })
163
164 err := pidCol.ReplaceOne(bson.M{"pid": cooked.pid}, docRes[0])
165 if err != nil {
166 fmt.Fprintf(os.Stderr, "Err replacing: %v\n", err)
167 }
168 } else {
169 // 先fork抵达,插入
170 process := Process{
171 Ppid: cooked.ppid,
172 Pid: cooked.pid,
173 Execve: make([]Exec, 0),
174 Children: make([]int, 0),
175 }
176 process.Execve = append(process.Execve, Exec{
177 Timestamp: cooked.timestamp,
178 ExecArgs: cooked.argv,
179 })
180
181 err := pidCol.InsertOne(process)
182 if err != nil {
183 fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err)
184 }
185 }
186}
187
188func fileOpen(cooked Event) {
189 // 权限检查过了,不必再查
190 file := File{
191 OpenTimestamp: cooked.timestamp,
192 FileName: cooked.srcPath,
193 Pid: cooked.pid,
194 Fd: cooked.exit_code,
195 Flags: cooked.syscallParam,
196 Written: make([]time.Time, 0),
197 }
198 if cooked.syscallParam[1]&syscall.O_TRUNC != 0 {
199 // 文件以清空方式打开
200 file.Written = append(file.Written, cooked.timestamp)
201 }
202
203 err := fdCol.InsertOne(file)
204 if err != nil {
205 fmt.Fprintf(os.Stderr, "Err inserting: %v\n", err)
206 }
207}
208
209func fileClose(cooked Event) {
210 res, err := fdCol.FindOneAndDelete(bson.M{"pid": cooked.pid, "fd": cooked.syscallParam[0]})
211 if err != nil {
212 return
213 }
214 res["close_timestamp"] = cooked.timestamp
215
216 err = fileCol.InsertOne(res)
217 if err != nil {
218 fmt.Fprintf(os.Stderr, "Err inserting files: %v\n", err)
219 }
220}
221
222func fileWrite(cooked Event) {
223 var res []File
224 err := fdCol.Finddoc(bson.M{
225 "pid": cooked.pid,
226 "fd": cooked.syscallParam[0],
227 }, &res)
228 if err != nil {
229 fmt.Fprintf(os.Stderr, "Err closing fd %d of pid %d: %v\n", cooked.syscallParam[0], cooked.pid, err)
230 }
231 if len(res) == 0 {
232 return
233 }
234
235 err = fdCol.UpdateOne(bson.M{
236 "pid": cooked.pid,
237 "fd": cooked.syscallParam[0],
238 }, bson.M{"$push": bson.M{"written": cooked.timestamp}})
239 if err != nil {
240 fmt.Fprintf(os.Stderr, "Err updating: %v\n", err)
241 }
242}
243
244func pivotRoot(cooked Event) {
245 var docRes []Process
246 // docker的根目录信息,记录
247 err := pidCol.Finddoc(bson.M{"pid": cooked.pid}, &docRes)
248 if err != nil {
249 fmt.Fprintf(os.Stderr, "Err finding: %v\n", err)
250 return
251 }
252
253 if len(docRes) == 0 {
254 // fork还没到,等一下
255 pidCol.InsertOne(bson.M{
256 "start_timestamp": cooked.timestamp,
257 "ppid": cooked.ppid,
258 "pid": cooked.pid,
259 "rootfs": "cwd",
260 })
261 } else {
262 // 读取已有的工作目录
263 pidCol.UpdateOne(bson.M{"pid": cooked.pid}, bson.M{
264 "$set": bson.M{
265 "rootfs": docRes[0].Cwd,
266 },
267 })
268 }
269}
diff --git a/listener/global.go b/listener/global.go
new file mode 100644
index 0000000..11b18bf
--- /dev/null
+++ b/listener/global.go
@@ -0,0 +1,84 @@
1package main
2
3import (
4 "sync"
5 "time"
6)
7
8type eventType int
9
10const (
11 NEWPID eventType = iota
12 PIDEXIT
13 EXECVE
14 FILEOPEN
15 FILECLOSE
16 FILEWRITE
17 PIVOTROOT
18 TYPENUM
19)
20
21func (et eventType) String() string {
22 names := []string{"NEWPID", "PIDEXIT", "EXECVE", "FILEOPEN", "FILECLOSE", "FILEWRITE", "PIVOTROOT", "TYPENUM"}
23 if et < NEWPID || et > TYPENUM {
24 return "Unknown"
25 }
26 return names[et]
27}
28
29type Event struct {
30 tag eventType
31 timestamp time.Time
32 pid, tgid int
33 ppid, parentTgid int
34 syscall int
35 syscallParam [4]uint64
36 argc int
37 argv []string
38 comm string
39 cwd string
40 exit_code int
41 exit_signal int
42 srcPath string
43 destPath string
44}
45
46var wg sync.WaitGroup // 掌管协程
47var rawChan chan interface{} // 从接收到整理的管道
48var cookedChan chan Event // 整理好的信息的管道
49var syscallTable [500]string //记录一下系统调用
50var containerdPid int
51
52// 插入到数据库的结构
53type Exec struct {
54 Timestamp time.Time `bson:"timestamp"`
55 ExecArgs []string `bson:"execArgs"`
56}
57
58type Process struct {
59 Star bool `bson:"star"`
60 StartTimestamp time.Time `bson:"start_timestamp"`
61 Ppid int `bson:"ppid"`
62 ParentTgid int `bson:"parentTgid"`
63 Pid int `bson:"pid"`
64 Tgid int `bson:"tgid"`
65 Args []string `bson:"args"`
66 Comm string `bson:"comm"`
67 RootFS string `bson:"rootfs"`
68 Cwd string `bson:"cwd"`
69 Children []int `bson:"children"`
70 Execve []Exec `bson:"execve"`
71 ExitCode int `bson:"exit_code"`
72 ExitSignal int `bson:"exit_signal"`
73 ExitTimestamp time.Time `bson:"exit_timestamp"`
74}
75
76type File struct {
77 OpenTimestamp time.Time `bson:"timestamp"`
78 FileName string `bson:"fileName"`
79 Pid int `bson:"pid"`
80 Fd int `bson:"fd"`
81 Flags [4]uint64 `bson:"flags"`
82 Written []time.Time `bson:"written"`
83 CloseTimestamp time.Time `bson:"close_timestamp"`
84}
diff --git a/listener/go.mod b/listener/go.mod
new file mode 100644
index 0000000..ed40331
--- /dev/null
+++ b/listener/go.mod
@@ -0,0 +1,26 @@
1module godo
2
3go 1.21.5
4
5require (
6 github.com/elastic/go-libaudit/v2 v2.5.0
7 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
8 go.mongodb.org/mongo-driver v1.16.0
9)
10
11require (
12 github.com/golang/snappy v0.0.4 // indirect
13 github.com/klauspost/compress v1.13.6 // indirect
14 github.com/montanaflynn/stats v0.7.1 // indirect
15 github.com/xdg-go/pbkdf2 v1.0.0 // indirect
16 github.com/xdg-go/scram v1.1.2 // indirect
17 github.com/xdg-go/stringprep v1.0.4 // indirect
18 github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
19 go.uber.org/atomic v1.7.0 // indirect
20 go.uber.org/multierr v1.7.0 // indirect
21 golang.org/x/crypto v0.22.0 // indirect
22 golang.org/x/sync v0.7.0 // indirect
23 golang.org/x/sys v0.19.0 // indirect
24 golang.org/x/text v0.14.0 // indirect
25 gopkg.in/yaml.v3 v3.0.1 // indirect
26)
diff --git a/listener/go.sum b/listener/go.sum
new file mode 100644
index 0000000..9164cd3
--- /dev/null
+++ b/listener/go.sum
@@ -0,0 +1,96 @@
1github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4github.com/elastic/go-libaudit/v2 v2.5.0 h1:5OK919QRnGtcjVBz3n/cs5F42im1mPlVTA9TyIn2K54=
5github.com/elastic/go-libaudit/v2 v2.5.0/go.mod h1:AjlnhinP+kKQuUJoXLVrqxBM8uyhQmkzoV6jjsCFP4Q=
6github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4=
7github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
8github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
9github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
10github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
11github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
12github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
13github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
14github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
15github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
16github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
17github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
18github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
19github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
20github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
21github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
22github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
23github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
24github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
25github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
26github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
27github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
28github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
29github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
30github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
31github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
32github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
33github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
34github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
35github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
36go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
37go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
38go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
39go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
40go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
41go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
42golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
43golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
44golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
45golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
46golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
47golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
48golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
49golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
50golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
51golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
52golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
53golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
54golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
55golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
56golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
57golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
58golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
59golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
60golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
61golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
62golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
63golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
64golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
65golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
66golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
68golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
69golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
70golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
71golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
72golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
73golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
74golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
75golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
76golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
77golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
78golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
79golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
80golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
81golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
82golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
83golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
84golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
85golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
86golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
87golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
88golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
89golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
90golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
91gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
92gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
93gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
94gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
95gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
96gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/listener/go.work b/listener/go.work
new file mode 100644
index 0000000..5b6c957
--- /dev/null
+++ b/listener/go.work
@@ -0,0 +1,6 @@
1go 1.21.5
2
3use (
4 ./netlink
5 ./
6) \ No newline at end of file
diff --git a/listener/go.work.sum b/listener/go.work.sum
new file mode 100644
index 0000000..8201e39
--- /dev/null
+++ b/listener/go.work.sum
@@ -0,0 +1,4 @@
1golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
2golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
3golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
4golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
diff --git a/listener/godo.go b/listener/godo.go
new file mode 100644
index 0000000..efe9585
--- /dev/null
+++ b/listener/godo.go
@@ -0,0 +1,176 @@
1package main
2
3import (
4 "bufio"
5 "flag"
6 "fmt"
7 "log"
8 "netlink"
9 "os"
10 "os/exec"
11 "strings"
12 "syscall"
13 "time"
14
15 "github.com/elastic/go-libaudit/v2"
16)
17
18var (
19 fs = flag.NewFlagSet("audit", flag.ExitOnError)
20 diag = fs.String("diag", "", "dump raw information from kernel to file")
21 rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)")
22 backlog = fs.Uint("backlog", 8192, "backlog limit")
23 immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)")
24 receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+")
25)
26
27func main() {
28 // 检查用户身份,并添加auditd规则,监听所有syscall
29 if os.Geteuid() != 0 {
30 fmt.Fprintf(os.Stderr, "Err: Please run me as root, %d!\n", os.Getegid())
31 return
32 }
33
34 // 所有的系统调用号与名称的关系
35 err := figureOutSyscalls()
36 if err != nil {
37 fmt.Fprintf(os.Stderr, "Error figuring out syscall numbers: %v\n", err)
38 }
39
40 exec.Command("auditctl", "-D").Run()
41 exec.Command("auditctl", "-b", "1000000000").Run()
42 exec.Command("auditctl", "--reset-lost").Run()
43
44 var auditCmd *exec.Cmd
45
46 pidSyscall := []string{"execve", "pivot_root"}
47 // // 设置监听规则
48 for i := 0; i < len(pidSyscall); i++ {
49 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i])
50 auditCmd.Run()
51 }
52
53 // 监听文件的消息
54 fileSyscall := []string{"open", "close", "write"}
55 // fileSyscall := []string{"open", "write", "creat", "unlink", "opendir", "mkdir", "rmdir", "chmod", "fchmod", "chown", "fchown", "lchown", "flock"}
56 for i := 0; i < len(fileSyscall); i++ {
57 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", fileSyscall[i])
58 auditCmd.Run()
59 }
60
61 // 查找pid
62 containerdPid, err = getPid()
63 if err != nil {
64 fmt.Fprintf(os.Stderr, "Error finding containerd: %v\n", err)
65 return
66 }
67
68 // 开始运行,解析命令行参数后监听
69 if err := fs.Parse(os.Args[1:]); err != nil {
70 log.Fatal(err)
71 }
72
73 if err := read(); err != nil {
74 log.Fatalf("error: %v", err)
75 }
76}
77
78func coroutine(client *libaudit.AuditClient) {
79 // 各协程至此开始
80 rawChan = make(chan interface{}, 65536)
81 cookedChan = make(chan Event, 65536)
82
83 wg.Add(1)
84 go procWatch()
85
86 wg.Add(1)
87 go receive(client)
88 wg.Add(1)
89 go orgnaze()
90 wg.Add(1)
91 go deal()
92
93 wg.Wait()
94 time.Sleep(2 * time.Second)
95}
96
97func procWatch() error {
98 ns, err := netlink.NewNetlinkSocket(syscall.NETLINK_CONNECTOR, 12345)
99 if err != nil {
100 fmt.Fprintf(os.Stderr, "Error creating socket: %v\n", err)
101 return err
102 }
103 defer ns.Close()
104 for {
105 res, err := ns.Receive(20)
106 if err != nil {
107 fmt.Fprintf(os.Stderr, "Error recv: %v\n", err)
108 continue
109 }
110 for i := 0; i < len(res); i++ {
111 procEvent := netlink.ParseProcEvent(res[i].Data)
112 switch procEvent.What {
113 case netlink.PROC_EVENT_FORK:
114 data := procEvent.Data.(netlink.ProcEventFork)
115 cooked := Event{
116 tag: NEWPID,
117 timestamp: time.Now(),
118 pid: int(data.ChildPid),
119 tgid: int(data.ChildTgid),
120 ppid: int(data.ParentPid),
121 parentTgid: int(data.ParentTgid),
122 }
123 checkProc(&cooked)
124 cookedChan <- cooked
125 case netlink.PROC_EVENT_EXIT:
126 data := procEvent.Data.(netlink.ProcEventExit)
127 cooked := Event{
128 tag: PIDEXIT,
129 timestamp: time.Now(),
130 pid: int(data.ProcessPid),
131 exit_code: int(data.ExitCode),
132 exit_signal: int(data.ExitSignal),
133 }
134 cookedChan <- cooked
135 default:
136 }
137 }
138 }
139}
140
141func checkProc(pCooked *Event) {
142 fileName := fmt.Sprintf("/proc/%d/task/%d/", pCooked.tgid, pCooked.pid)
143 fd, err := os.Open(fileName + "cmdline")
144 if err != nil {
145 fmt.Fprintf(os.Stderr, "Err: %v\n", err)
146 return
147 }
148
149 scanner := bufio.NewScanner(fd)
150 scanner.Split(bufio.ScanLines)
151 for scanner.Scan() {
152 line := scanner.Text()
153 pCooked.argv = append(pCooked.argv, strings.Split(line, "\x00")...)
154 }
155 pCooked.argc = len(pCooked.argv)
156 fd.Close()
157
158 fd, err = os.Open(fileName + "comm")
159 if err != nil {
160 fmt.Fprintf(os.Stderr, "Err: %v\n", err)
161 return
162 }
163 scanner = bufio.NewScanner(fd)
164 scanner.Split(bufio.ScanLines)
165 for scanner.Scan() {
166 line := scanner.Text()
167 pCooked.comm = line
168 }
169 fd.Close()
170
171 pCooked.cwd, err = os.Readlink(fileName + "cwd")
172 if err != nil {
173 fmt.Fprintf(os.Stderr, "Err: %v\n", err)
174 pCooked.cwd = ""
175 }
176}
diff --git a/listener/mongo.go b/listener/mongo.go
new file mode 100644
index 0000000..a51350e
--- /dev/null
+++ b/listener/mongo.go
@@ -0,0 +1,122 @@
1package main
2
3import (
4 "context"
5 "fmt"
6 "reflect"
7 "time"
8
9 "go.mongodb.org/mongo-driver/bson"
10 "go.mongodb.org/mongo-driver/mongo"
11 "go.mongodb.org/mongo-driver/mongo/options"
12)
13
14type mongoClient struct {
15 dbName, colName string
16 client *mongo.Client
17 col *mongo.Collection
18}
19
20func (mc *mongoClient) init(dbName, colName string) error {
21 var err error
22 if err = mc.Connect(dbName, colName); err != nil {
23 return err
24 }
25 if err = mc.Drop(); err != nil {
26 return err
27 }
28
29 return nil
30}
31
32func (mc *mongoClient) Connect(dbName, colName string) error {
33 var err error
34 mc.client, err = mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
35
36 if err != nil {
37 return err
38 }
39
40 ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
41 err = mc.client.Connect(ctx)
42 if err != nil {
43 return err
44 }
45
46 mc.col = mc.client.Database(dbName).Collection(colName)
47 mc.dbName = dbName
48 mc.colName = colName
49 return nil
50}
51
52func (mc *mongoClient) InsertOne(document interface{}) error {
53 _, err := mc.col.InsertOne(context.Background(), document)
54 return err
55}
56
57func (mc *mongoClient) UpdateOne(filter, update interface{}) error {
58 _, err := mc.col.UpdateOne(context.Background(), filter, update)
59 return err
60}
61
62func (mc *mongoClient) UpdateMany(filter, update interface{}) error {
63 _, err := mc.col.UpdateMany(context.Background(), filter, update)
64 return err
65}
66
67func (mc *mongoClient) ReplaceOne(filter, new interface{}) error {
68 _, err := mc.col.ReplaceOne(context.Background(), filter, new)
69 return err
70}
71
72func (mc *mongoClient) Finddoc(filter bson.M, results interface{}) error {
73 sliceValue := reflect.ValueOf(results)
74
75 if sliceValue.Kind() != reflect.Ptr || sliceValue.Elem().Kind() != reflect.Slice {
76 return fmt.Errorf("Error: result argument must be pointer to slice")
77 }
78 cur, err := mc.col.Find(context.TODO(), filter)
79 if err != nil {
80 return err
81 }
82 defer cur.Close(context.TODO())
83
84 elemType := sliceValue.Elem().Type().Elem()
85
86 sliceValue = sliceValue.Elem()
87
88 for cur.Next(context.TODO()) {
89 elem := reflect.New(elemType).Interface()
90 err := cur.Decode(elem)
91 if err != nil {
92 return err
93 }
94 sliceValue = reflect.Append(sliceValue, reflect.ValueOf(elem).Elem())
95 }
96
97 reflect.ValueOf(results).Elem().Set(sliceValue)
98 return nil
99}
100
101func (mc *mongoClient) FindOneAndDelete(filter bson.M) (bson.M, error) {
102 res := mc.col.FindOneAndDelete(context.Background(), filter)
103 var result bson.M
104 err := res.Decode(&result)
105 return result, err
106}
107
108func (mc *mongoClient) Drop() error {
109 return mc.col.Drop(context.Background())
110}
111
112func (mc *mongoClient) Disconnect() error {
113 err := mc.client.Disconnect(context.Background())
114 if err != nil {
115 return err
116 }
117 mc.col = nil
118 mc.client = nil
119 mc.dbName = ""
120 mc.colName = ""
121 return nil
122}
diff --git a/listener/netlink b/listener/netlink
new file mode 160000
Subproject e53c2724725c5991cdd9ea088c26832c5c9fcf0
diff --git a/listener/organize.go b/listener/organize.go
new file mode 100644
index 0000000..0c05eb4
--- /dev/null
+++ b/listener/organize.go
@@ -0,0 +1,291 @@
1package main
2
3import (
4 "fmt"
5 "os"
6 "regexp"
7 "strconv"
8 "strings"
9 "sync"
10 "syscall"
11
12 "github.com/elastic/go-libaudit/v2"
13 "github.com/elastic/go-libaudit/v2/auparse"
14)
15
16// 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
17var eventTable sync.Map
18
19// 事件信息
20var tmp any
21var ok bool
22var event Event
23var pEvent *Event
24var eventId, argc int
25
26// 要用的正则匹配列表
27var (
28 syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+)(?:.*?exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?subj=(.*?):(.*?):(.*?):(.*?) .*?$`)
29 execveRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
30 argsRegex = regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
31 pathRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): item=(\d+) name="(.*?)" .*objtype=([A-Z]+) `)
32 cwdRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
33 proctitleRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
34 eoeRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
35)
36
37func orgnaze() {
38 defer wg.Done()
39 defer close(cookedChan)
40 // 接收信息
41 var raw interface{}
42 var rawEvent libaudit.RawAuditMessage
43
44 for {
45 raw, ok = <-rawChan
46 if !ok {
47 break
48 }
49 rawEvent = raw.(libaudit.RawAuditMessage)
50 // fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data)
51
52 switch rawEvent.Type {
53 case auparse.AUDIT_SYSCALL:
54 syscallRaw(rawEvent)
55 case auparse.AUDIT_EXECVE:
56 execve(rawEvent)
57 case auparse.AUDIT_CWD:
58 cwd(rawEvent)
59 case auparse.AUDIT_PATH:
60 path(rawEvent)
61 case auparse.AUDIT_PROCTITLE:
62 proctitle(rawEvent)
63 case auparse.AUDIT_EOE:
64 eoe(rawEvent)
65 default:
66 }
67 }
68}
69
70func syscallRaw(rawEvent libaudit.RawAuditMessage) {
71 if !syscallRegex.Match(rawEvent.Data) {
72 return
73 }
74
75 var exit int
76 var a [4]uint64
77 var subj [4]string
78 // 捕获基础信息
79 match := syscallRegex.FindSubmatch(rawEvent.Data)
80 event.timestamp, _ = getTimeFromStr(string(match[1]))
81 eventId, _ = strconv.Atoi(string(match[2]))
82 event.syscall, _ = strconv.Atoi(string(match[3]))
83 if string(match[4]) == "" {
84 // exit没捕获到
85 exit = 0
86 } else {
87 exit, _ = strconv.Atoi(string(match[4]))
88 }
89 event.ppid, _ = strconv.Atoi(string(match[5]))
90 event.pid, _ = strconv.Atoi(string(match[6]))
91
92 // 几个subj,说不定会有用
93 for i := 0; i < 4; i++ {
94 subj[i] = string(match[7+i])
95 }
96
97 // 捕获参数
98 if !argsRegex.Match(rawEvent.Data) {
99 fmt.Fprintf(os.Stderr, "Error: don't get args in syscall event!\n")
100 return
101 }
102 argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
103 for i := 0; i < 4; i++ {
104 a[i], _ = strconv.ParseUint(string(argsMatch[i][3]), 16, 64)
105 }
106
107 switch syscallTable[event.syscall] {
108 case "execve":
109 eventTable.Store(eventId, &Event{
110 tag: EXECVE,
111 timestamp: event.timestamp,
112 syscall: event.syscall,
113 // exit_code: a[0], // 为啥这么写?
114 ppid: event.ppid,
115 pid: event.pid,
116 argc: 0,
117 argv: make([]string, 0),
118 cwd: "",
119 })
120 case "open":
121 // 检查打开的权限
122 if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 {
123 break
124 }
125 // TRUNC应该被直接标记为改变,而不是打开
126 eventTable.Store(eventId, &Event{
127 tag: FILEOPEN,
128 timestamp: event.timestamp,
129 syscall: event.syscall,
130 exit_code: exit,
131 ppid: event.ppid,
132 pid: event.pid,
133 argc: 0,
134 argv: make([]string, 0),
135 cwd: "",
136 syscallParam: a,
137 srcPath: "",
138 })
139 case "write":
140 eventTable.Store(eventId, &Event{
141 tag: FILEWRITE,
142 timestamp: event.timestamp,
143 syscall: event.syscall,
144 exit_code: exit,
145 ppid: event.ppid,
146 pid: event.pid,
147 argc: 0,
148 argv: make([]string, 0),
149 cwd: "",
150 syscallParam: a,
151 })
152 case "close":
153 // 文件关闭
154 eventTable.Store(eventId, &Event{
155 tag: FILECLOSE,
156 timestamp: event.timestamp,
157 syscall: event.syscall,
158 exit_code: exit,
159 ppid: event.ppid,
160 pid: event.pid,
161 argc: 0,
162 argv: make([]string, 0),
163 cwd: "",
164 syscallParam: a,
165 })
166 case "pivot_root":
167 if subj[2] == "container_runtime_t" {
168 eventTable.Store(eventId, &Event{
169 tag: PIVOTROOT,
170 timestamp: event.timestamp,
171 syscall: event.syscall,
172 ppid: event.ppid,
173 pid: event.pid,
174 syscallParam: a,
175 })
176 }
177 }
178}
179
180func execve(rawEvent libaudit.RawAuditMessage) {
181 if !execveRegex.Match(rawEvent.Data) {
182 return
183 }
184
185 match := execveRegex.FindSubmatch(rawEvent.Data)
186 eventId, _ = strconv.Atoi(string(match[1]))
187 argc, _ = strconv.Atoi(string(match[2]))
188 tmp, ok = eventTable.Load(eventId)
189 if !ok {
190 return
191 }
192 pEvent = tmp.(*Event)
193 if argsRegex.Match(rawEvent.Data) {
194 match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
195 for i := 0; i < argc; i++ {
196 if len(match[i][2]) == 0 {
197 // 代表着匹配到的是十六进制数
198 str := hexToAscii(string(match[i][3]))
199 pEvent.argv = append(pEvent.argv, str)
200 } else {
201 pEvent.argv = append(pEvent.argv, string(match[i][2]))
202 }
203 }
204 pEvent.argc = argc
205 }
206}
207
208func cwd(rawEvent libaudit.RawAuditMessage) {
209 if !cwdRegex.Match(rawEvent.Data) {
210 return
211 }
212
213 match := cwdRegex.FindSubmatch(rawEvent.Data)
214 eventId, _ = strconv.Atoi(string(match[1]))
215 tmp, ok = eventTable.Load(eventId)
216 if !ok {
217 return
218 }
219 tmp.(*Event).cwd = string(match[2])
220}
221
222func proctitle(rawEvent libaudit.RawAuditMessage) {
223 if !proctitleRegex.Match(rawEvent.Data) {
224 return
225 }
226
227 var cmdline string
228 match := proctitleRegex.FindSubmatch(rawEvent.Data)
229 eventId, _ = strconv.Atoi(string(match[1]))
230 tmp, ok = eventTable.Load(eventId)
231 if !ok {
232 return
233 }
234 pEvent = tmp.(*Event)
235 if pEvent.argc == 0 {
236 // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
237 if match[3] == nil {
238 // PROCTITLE写的是十六进制,转换为字符串
239 cmdline = hexToAscii(string(match[4]))
240 } else {
241 cmdline = string(match[3])
242 }
243 pEvent.argv = strings.Split(cmdline, " ")
244 pEvent.argc = len(pEvent.argv)
245 }
246}
247
248func eoe(rawEvent libaudit.RawAuditMessage) {
249 if !eoeRegex.Match(rawEvent.Data) {
250 return
251 }
252
253 match := eoeRegex.FindSubmatch(rawEvent.Data)
254 eventId, _ = strconv.Atoi(string(match[1]))
255 tmp, ok = eventTable.Load(eventId)
256 if !ok {
257 return
258 }
259 cooked := *(tmp.(*Event))
260 cookedChan <- cooked
261 eventTable.Delete(eventId) // 死人别占地
262}
263
264func path(rawEvent libaudit.RawAuditMessage) {
265 if !pathRegex.Match(rawEvent.Data) {
266 return
267 }
268 match := pathRegex.FindSubmatch(rawEvent.Data)
269 eventId, _ = strconv.Atoi(string(match[1]))
270 // item, _ := strconv.Atoi(string(match[2]))
271 name := string(match[3])
272 objtype := string(match[4])
273
274 tmp, ok = eventTable.Load(eventId)
275 if !ok {
276 return
277 }
278 pEvent = tmp.(*Event)
279
280 // 先看看是不是文件操作,再看是不是所在目录
281 if pEvent.tag != FILEOPEN || objtype == "PARENT" {
282 return
283 }
284
285 if pEvent.cwd == "/" || name[0] == '/' {
286 pEvent.srcPath = name
287 } else {
288 pEvent.srcPath = pEvent.cwd + "/" + name
289 }
290 // ATTENTION: 这里需要做路径简化,留给过滤清洗流程吧
291}
diff --git a/listener/receive.go b/listener/receive.go
new file mode 100644
index 0000000..c0dea00
--- /dev/null
+++ b/listener/receive.go
@@ -0,0 +1,29 @@
1package main
2
3import (
4 "fmt"
5
6 "github.com/elastic/go-libaudit/v2"
7 "github.com/elastic/go-libaudit/v2/auparse"
8 "github.com/mohae/deepcopy"
9)
10
11func receive(r *libaudit.AuditClient) error {
12 defer wg.Done()
13 defer close(rawChan)
14 for {
15 rawEvent, err := r.Receive(false)
16 if err != nil {
17 return fmt.Errorf("receive failed: %w", err)
18 }
19
20 // Messages from 1300-2999 are valid audit messages.
21 if rawEvent.Type < auparse.AUDIT_USER_AUTH ||
22 rawEvent.Type > auparse.AUDIT_LAST_USER_MSG2 {
23 continue
24 }
25
26 rawEventMessage := deepcopy.Copy(*rawEvent)
27 rawChan <- rawEventMessage
28 }
29}