summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--godo.go535
-rw-r--r--src/audit.go84
-rw-r--r--src/basefunc.go115
-rw-r--r--src/deal.go97
-rw-r--r--src/global.go10
-rw-r--r--src/go.mod (renamed from go.mod)0
-rw-r--r--src/go.sum (renamed from go.sum)0
-rw-r--r--src/godo.go116
-rw-r--r--src/organize.go124
-rw-r--r--src/receive.go29
11 files changed, 580 insertions, 535 deletions
diff --git a/.gitignore b/.gitignore
index 8bbe3e4..8a2ac54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
1.vscode/* 1.vscode/*
2godo 2godo
3 3
4old/*
5!old/*.*
6old/*.log
7old/*.json
8old/go.* \ No newline at end of file
diff --git a/godo.go b/godo.go
deleted file mode 100644
index 1ae336b..0000000
--- a/godo.go
+++ /dev/null
@@ -1,535 +0,0 @@
1package main
2
3import (
4 "bufio"
5 "flag"
6 "fmt"
7 "io"
8 "log"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "regexp"
13 "strconv"
14 "strings"
15 "sync"
16 "time"
17
18 "github.com/elastic/go-libaudit/v2"
19 "github.com/elastic/go-libaudit/v2/auparse"
20 "github.com/mohae/deepcopy"
21)
22
23var (
24 fs = flag.NewFlagSet("audit", flag.ExitOnError)
25 diag = fs.String("diag", "", "dump raw information from kernel to file")
26 rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)")
27 backlog = fs.Uint("backlog", 8192, "backlog limit")
28 immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)")
29 receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+")
30)
31
32type Event struct {
33 timestamp time.Time
34 pid, ppid int
35 syscall int
36 argc int
37 argv []string
38 cwd string
39}
40
41type process struct {
42 timestamp time.Time
43 pid, ppid int
44 argv []string
45 cwd string
46 rootfs string
47 children []int
48}
49
50var pids sync.Map // 古希腊掌管进程的神,int->*process
51var wg sync.WaitGroup // 掌管协程
52var rawChan chan interface{} // 从接收到整理的管道
53var cookedChan chan Event // 整理好的信息的管道
54var syscallTable [500]string //记录一下系统调用
55
56var containerdPid int
57
58func main() {
59 // 检查用户身份,并添加auditd规则,监听所有syscall
60 if os.Geteuid() != 0 {
61 fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid())
62 return
63 }
64
65 // 所有的系统调用号与名称的关系
66 err := figureOutSyscalls()
67 if err != nil {
68 fmt.Printf("Error figuring out syscall numbers: %v\n", err)
69 }
70
71 syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"}
72 var auditCmd *exec.Cmd
73 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则
74 auditCmd.Run()
75 // 设置监听规则
76 for i := 0; i < len(syscall); i++ {
77 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall[i])
78 auditCmd.Run()
79 }
80
81 // 查找pid
82 containerdPid, err = getPid()
83 if err != nil {
84 fmt.Printf("Error finding containerd: %v\n", err)
85 return
86 }
87 // 数据结构初始化
88 // pids = make(map[int]*process)
89 // containers = make(map[string]int)
90
91 // 创世之神,1号进程
92 // pids[1] = &process{rootfs: "/", children: make([]int, 0)}
93 // pids[1].children = append(pids[1].children, containerdPid)
94 // 1号进程还是不要在进程树上直接出现了,不然它的小儿子们都会出现
95
96 // /usr/bin/containerd,也就是我们最关注的进程
97 // pids[containerdPid] = &process{rootfs: "/", children: make([]int, 0)}
98 pids.Store(containerdPid, &process{
99 ppid: 1,
100 pid: containerdPid,
101 argv: make([]string, 0),
102 cwd: "/",
103 rootfs: "/",
104 children: make([]int, 0),
105 })
106 p, ok := pids.Load(containerdPid)
107 if !ok {
108 fmt.Printf("???\n")
109 return
110 }
111 p.(*process).argv = append(p.(*process).argv, "/usr/bin/containerd")
112
113 // 开始运行,解析命令行参数后监听
114 if err := fs.Parse(os.Args[1:]); err != nil {
115 log.Fatal(err)
116 }
117
118 if err := read(); err != nil {
119 log.Fatalf("error: %v", err)
120 }
121}
122
123func figureOutSyscalls() error {
124 NRRegex := regexp.MustCompile(`#define __NR_(.*?) (\d+)$`)
125 file, err := os.Open("/usr/include/asm/unistd_64.h")
126 if err != nil {
127 return err
128 }
129 defer file.Close()
130
131 scanner := bufio.NewScanner(file)
132 for scanner.Scan() {
133 line := scanner.Text()
134 if NRRegex.MatchString(line) {
135 match := NRRegex.FindStringSubmatch(line)
136 num, err := strconv.Atoi(match[2])
137 if err != nil {
138 return err
139 }
140 syscallTable[num] = match[1]
141 }
142 }
143 return nil
144}
145
146func getPid() (int, error) {
147 // 指定要搜索的关键词
148 keyword := "/usr/bin/containerd"
149
150 // 获取/proc目录下的所有子目录
151 procDir, err := filepath.Glob("/proc/*")
152 if err != nil {
153 return 0, err
154 }
155
156 // 遍历子目录,查找包含关键词的进程
157 for _, dir := range procDir {
158 pid, err := strconv.Atoi(filepath.Base(dir))
159 if err != nil {
160 continue // 跳过非PID的目录
161 }
162
163 // 检查进程是否包含关键词
164 if containsKeyword(pid, keyword) {
165 return pid, nil
166 }
167 }
168 err = fmt.Errorf("Error: no containerd process found.")
169 return 0, err
170}
171
172func containsKeyword(pid int, keyword string) bool {
173 // 构造完整的进程命令路径
174 cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
175
176 // 打开文件
177 file, err := os.Open(cmdPath)
178 if err != nil {
179 return false
180 }
181 defer file.Close()
182
183 // 读取文件内容
184 scanner := bufio.NewScanner(file)
185 scanner.Split(bufio.ScanLines)
186 for scanner.Scan() {
187 line := scanner.Text()
188 if strings.Contains(line, keyword) {
189 return true
190 }
191 }
192 return false
193}
194
195func getTimeFromStr(timeStr string) (time.Time, error) {
196 timestampFloat, err := strconv.ParseFloat(timeStr, 64)
197 if err != nil {
198 return time.Unix(0, 0), err
199 }
200 secs := int64(timestampFloat)
201 nsecs := int64((timestampFloat - float64(secs)) * 1e9)
202
203 // 只精确到毫秒就够了
204 t := time.Unix(secs, nsecs).Truncate(time.Millisecond)
205 return t, nil
206}
207
208func hexToAscii(hexString string) string {
209 bytes := []byte{}
210 for i := 0; i < len(hexString); i += 2 {
211 hexPair := hexString[i : i+2]
212 // 将十六进制数转换为十进制数
213 decimal, err := strconv.ParseInt(hexPair, 16, 8)
214 if err != nil {
215 return "Invalid hex string"
216 }
217 char := byte(decimal)
218 bytes = append(bytes, char)
219 }
220
221 asciiString := strings.ReplaceAll(string(bytes), "\000", " ")
222
223 return asciiString
224}
225
226func read() error {
227 // Write netlink response to a file for further analysis or for writing
228 // tests cases.
229 var diagWriter io.Writer
230 if *diag != "" {
231 f, err := os.OpenFile(*diag, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o600)
232 if err != nil {
233 return err
234 }
235 defer f.Close()
236 diagWriter = f
237 }
238
239 log.Println("starting netlink client")
240
241 var err error
242 var client *libaudit.AuditClient
243 if *receiveOnly {
244 client, err = libaudit.NewMulticastAuditClient(diagWriter)
245 if err != nil {
246 return fmt.Errorf("failed to create receive-only audit client: %w", err)
247 }
248 defer client.Close()
249 } else {
250 client, err = libaudit.NewAuditClient(diagWriter)
251 if err != nil {
252 return fmt.Errorf("failed to create audit client: %w", err)
253 }
254 defer client.Close()
255
256 status, err := client.GetStatus()
257 if err != nil {
258 return fmt.Errorf("failed to get audit status: %w", err)
259 }
260 log.Printf("received audit status=%+v", status)
261
262 if status.Enabled == 0 {
263 log.Println("enabling auditing in the kernel")
264 if err = client.SetEnabled(true, libaudit.WaitForReply); err != nil {
265 return fmt.Errorf("failed to set enabled=true: %w", err)
266 }
267 }
268
269 if status.RateLimit != uint32(*rate) {
270 log.Printf("setting rate limit in kernel to %v", *rate)
271 if err = client.SetRateLimit(uint32(*rate), libaudit.NoWait); err != nil {
272 return fmt.Errorf("failed to set rate limit to unlimited: %w", err)
273 }
274 }
275
276 if status.BacklogLimit != uint32(*backlog) {
277 log.Printf("setting backlog limit in kernel to %v", *backlog)
278 if err = client.SetBacklogLimit(uint32(*backlog), libaudit.NoWait); err != nil {
279 return fmt.Errorf("failed to set backlog limit: %w", err)
280 }
281 }
282
283 if status.Enabled != 2 && *immutable {
284 log.Printf("setting kernel settings as immutable")
285 if err = client.SetImmutable(libaudit.NoWait); err != nil {
286 return fmt.Errorf("failed to set kernel as immutable: %w", err)
287 }
288 }
289
290 log.Printf("sending message to kernel registering our PID (%v) as the audit daemon", os.Getpid())
291 if err = client.SetPID(libaudit.NoWait); err != nil {
292 return fmt.Errorf("failed to set audit PID: %w", err)
293 }
294 }
295
296 // 各协程至此开始
297 // return receive(client)
298 rawChan = make(chan interface{})
299 cookedChan = make(chan Event)
300 wg.Add(1)
301 go receive(client)
302 wg.Add(1)
303 go orgnaze()
304 wg.Add(1)
305 go deal()
306
307 wg.Wait()
308 time.Sleep(2 * time.Second)
309 return nil
310}
311
312func receive(r *libaudit.AuditClient) error {
313 defer wg.Done()
314 defer close(rawChan)
315 for {
316 rawEvent, err := r.Receive(false)
317 if err != nil {
318 return fmt.Errorf("receive failed: %w", err)
319 }
320
321 // Messages from 1300-2999 are valid audit messages.
322 if rawEvent.Type < auparse.AUDIT_USER_AUTH ||
323 rawEvent.Type > auparse.AUDIT_LAST_USER_MSG2 {
324 continue
325 }
326
327 rawEventMessage := deepcopy.Copy(*rawEvent)
328 rawChan <- rawEventMessage
329 }
330}
331
332func orgnaze() {
333 defer wg.Done()
334 defer close(cookedChan)
335 // 接收信息
336 var raw interface{}
337 var ok bool
338 var rawEvent libaudit.RawAuditMessage
339 // 事件信息
340 var eventId, argc int
341 var err [6]error
342 var event, cooked Event
343 // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
344 eventTable := make(map[int]*Event)
345 // 要用的正则匹配列表
346 syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?ppid=(\d+) pid=(\d+).*?$`)
347 execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
348 argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
349 cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
350 proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
351 eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
352 for {
353 raw, ok = <-rawChan
354 if !ok {
355 break
356 }
357 rawEvent = raw.(libaudit.RawAuditMessage)
358
359 // type Event struct {
360 // timestamp time.Time
361 // pid, ppid int
362 // syscall int
363 // argc int
364 // args []string
365 // cwd string
366 // }
367 switch rawEvent.Type {
368 case auparse.AUDIT_SYSCALL:
369 if syscallRegex.Match(rawEvent.Data) {
370 match := syscallRegex.FindSubmatch(rawEvent.Data)
371 event.timestamp, err[0] = getTimeFromStr(string(match[1]))
372 eventId, err[1] = strconv.Atoi(string(match[2]))
373 event.syscall, err[2] = strconv.Atoi(string(match[3]))
374 event.ppid, err[3] = strconv.Atoi(string(match[4]))
375 event.pid, err[4] = strconv.Atoi(string(match[5]))
376 eventTable[eventId] = &Event{
377 timestamp: event.timestamp,
378 syscall: event.syscall,
379 ppid: event.ppid,
380 pid: event.pid,
381 argc: 0,
382 argv: make([]string, 0),
383 cwd: "",
384 }
385 }
386 case auparse.AUDIT_EXECVE:
387 if execveRegex.Match(rawEvent.Data) {
388 match := execveRegex.FindSubmatch(rawEvent.Data)
389 eventId, err[0] = strconv.Atoi(string(match[1]))
390 argc, err[1] = strconv.Atoi(string(match[2]))
391 if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) {
392 match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
393 for i := 0; i < argc; i++ {
394 if len(match[i][2]) == 0 {
395 // 代表着匹配到的是十六进制数
396 str := hexToAscii(string(match[i][3]))
397 eventTable[eventId].argv = append(eventTable[eventId].argv, str)
398 } else {
399 eventTable[eventId].argv = append(eventTable[eventId].argv, string(match[i][2]))
400 }
401 }
402 eventTable[eventId].argc = argc
403 }
404 }
405 // case auparse.AUDIT_PATH:
406 case auparse.AUDIT_CWD:
407 if cwdRegex.Match(rawEvent.Data) {
408 match := cwdRegex.FindSubmatch(rawEvent.Data)
409 eventId, err[0] = strconv.Atoi(string(match[1]))
410 eventTable[eventId].cwd = string(match[2])
411 }
412 case auparse.AUDIT_PROCTITLE:
413 if proctitleRegex.Match(rawEvent.Data) {
414 var cmdline string
415 var pEvent *Event
416 match := proctitleRegex.FindSubmatch(rawEvent.Data)
417 eventId, err[0] = strconv.Atoi(string(match[1]))
418 pEvent = eventTable[eventId]
419 if pEvent.argc == 0 {
420 // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
421 if match[3] == nil {
422 // PROCTITLE写的是十六进制,转换为字符串
423 cmdline = hexToAscii(string(match[4]))
424 } else {
425 cmdline = string(match[3])
426 }
427 pEvent.argv = strings.Split(cmdline, " ")
428 pEvent.argc = len(eventTable[eventId].argv)
429 }
430 }
431 case auparse.AUDIT_EOE:
432 if eoeRegex.Match(rawEvent.Data) {
433 match := eoeRegex.FindSubmatch(rawEvent.Data)
434 eventId, err[0] = strconv.Atoi(string(match[1]))
435 // ATTENTION: 事件整理完毕,即刻发出,是否合理呢?
436 cooked = *eventTable[eventId] // 应当采用深拷贝吗?有待实验
437 cookedChan <- cooked
438 delete(eventTable, eventId) //发出之后就从信息表扔掉,死人别占地
439 }
440 default:
441 // ATTENTION: 这里也需要做防护
442 }
443 }
444}
445
446func deal() {
447 defer wg.Done()
448 var cooked Event
449 var ok bool
450 for {
451 cooked, ok = <-cookedChan
452 if !ok {
453 break
454 }
455 // type Event struct {
456 // timestamp time.Time
457 // pid, ppid int
458 // syscall int
459 // argc int
460 // args []string
461 // cwd string
462 // }
463 // type process struct {
464 // timestamp time.Time
465 // pid, ppid int
466 // argv []string
467 // cwd string
468 // rootfs string
469 // children []int
470 // }
471 switch syscallTable[cooked.syscall] {
472 case "fork", "vfork", "clone":
473 ppid := cooked.ppid
474 pid := cooked.pid
475 parent, ok := pids.Load(ppid)
476 if !ok {
477 break
478 }
479 parent.(*process).children = append(parent.(*process).children, pid)
480 pids.Store(pid, &process{
481 timestamp: cooked.timestamp,
482 pid: cooked.pid,
483 ppid: cooked.ppid,
484 argv: cooked.argv,
485 cwd: cooked.cwd,
486 children: make([]int, 0),
487 })
488 fmt.Printf("%v syscall=%d, ppid=%d, pid=%d, cwd=\"%s\", argc=%d, ", cooked.timestamp, cooked.syscall, cooked.ppid, cooked.pid, cooked.cwd, cooked.argc)
489 for i := 0; i < cooked.argc; i++ {
490 fmt.Printf("arg[%d]=\"%s\", ", i, cooked.argv[i])
491 }
492 fmt.Printf("\n")
493 case "exit", "exit_group":
494 _, ok := pids.Load(cooked.pid)
495 if !ok {
496 break
497 }
498 go deletePid(cooked)
499 }
500 }
501}
502
503func deletePid(cooked Event) {
504 time.Sleep(1 * time.Second)
505 Process, ok := pids.Load(cooked.pid)
506 if !ok {
507 return
508 }
509 pProcess := Process.(*process)
510
511 // 先从爹那里注销户籍
512 parent, ok := pids.Load(pProcess.ppid)
513 if ok {
514 pParent := parent.(*process)
515 for i, child := range pParent.children {
516 if child == pProcess.pid {
517 pParent.children = append(pParent.children[:i], pParent.children[i+1:]...)
518 break
519 }
520 }
521 }
522
523 // 子进程需要收容
524 for i := 0; i < len(pProcess.children); i++ {
525 child, ok := pids.Load(pProcess.children[i])
526 if ok {
527 child.(*process).ppid = 1
528 }
529 }
530
531 // 可以去死了
532 pids.Delete(cooked.pid)
533 _, ok = pids.Load(cooked.pid)
534 fmt.Printf("%v Goodbye, %d! ok = %v\n", time.Now(), cooked.pid, ok)
535}
diff --git a/src/audit.go b/src/audit.go
new file mode 100644
index 0000000..ed48691
--- /dev/null
+++ b/src/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/src/basefunc.go b/src/basefunc.go
new file mode 100644
index 0000000..5fff3e8
--- /dev/null
+++ b/src/basefunc.go
@@ -0,0 +1,115 @@
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "path/filepath"
8 "regexp"
9 "strconv"
10 "strings"
11 "time"
12)
13
14func figureOutSyscalls() error {
15 NRRegex := regexp.MustCompile(`#define __NR_(.*?) (\d+)$`)
16 file, err := os.Open("/usr/include/asm/unistd_64.h")
17 if err != nil {
18 return err
19 }
20 defer file.Close()
21
22 scanner := bufio.NewScanner(file)
23 for scanner.Scan() {
24 line := scanner.Text()
25 if NRRegex.MatchString(line) {
26 match := NRRegex.FindStringSubmatch(line)
27 num, err := strconv.Atoi(match[2])
28 if err != nil {
29 return err
30 }
31 syscallTable[num] = match[1]
32 }
33 }
34 return nil
35}
36
37func getPid() (int, error) {
38 // 指定要搜索的关键词
39 keyword := "/usr/bin/containerd"
40
41 // 获取/proc目录下的所有子目录
42 procDir, err := filepath.Glob("/proc/*")
43 if err != nil {
44 return 0, err
45 }
46
47 // 遍历子目录,查找包含关键词的进程
48 for _, dir := range procDir {
49 pid, err := strconv.Atoi(filepath.Base(dir))
50 if err != nil {
51 continue // 跳过非PID的目录
52 }
53
54 // 检查进程是否包含关键词
55 if containsKeyword(pid, keyword) {
56 return pid, nil
57 }
58 }
59 err = fmt.Errorf("Error: no containerd process found.")
60 return 0, err
61}
62
63func containsKeyword(pid int, keyword string) bool {
64 // 构造完整的进程命令路径
65 cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
66
67 // 打开文件
68 file, err := os.Open(cmdPath)
69 if err != nil {
70 return false
71 }
72 defer file.Close()
73
74 // 读取文件内容
75 scanner := bufio.NewScanner(file)
76 scanner.Split(bufio.ScanLines)
77 for scanner.Scan() {
78 line := scanner.Text()
79 if strings.Contains(line, keyword) {
80 return true
81 }
82 }
83 return false
84}
85
86func getTimeFromStr(timeStr string) (time.Time, error) {
87 timestampFloat, err := strconv.ParseFloat(timeStr, 64)
88 if err != nil {
89 return time.Unix(0, 0), err
90 }
91 secs := int64(timestampFloat)
92 nsecs := int64((timestampFloat - float64(secs)) * 1e9)
93
94 // 只精确到毫秒就够了
95 t := time.Unix(secs, nsecs).Truncate(time.Millisecond)
96 return t, nil
97}
98
99func hexToAscii(hexString string) string {
100 bytes := []byte{}
101 for i := 0; i < len(hexString); i += 2 {
102 hexPair := hexString[i : i+2]
103 // 将十六进制数转换为十进制数
104 decimal, err := strconv.ParseInt(hexPair, 16, 8)
105 if err != nil {
106 return "Invalid hex string"
107 }
108 char := byte(decimal)
109 bytes = append(bytes, char)
110 }
111
112 asciiString := strings.ReplaceAll(string(bytes), "\000", " ")
113
114 return asciiString
115}
diff --git a/src/deal.go b/src/deal.go
new file mode 100644
index 0000000..fd9f788
--- /dev/null
+++ b/src/deal.go
@@ -0,0 +1,97 @@
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func deal() {
9 defer wg.Done()
10 var cooked Event
11 var ok bool
12 for {
13 cooked, ok = <-cookedChan
14 if !ok {
15 break
16 }
17 // type Event struct {
18 // timestamp time.Time
19 // pid, ppid int
20 // syscall int
21 // argc int
22 // args []string
23 // cwd string
24 // }
25 // type process struct {
26 // timestamp time.Time
27 // pid, ppid int
28 // argv []string
29 // cwd string
30 // rootfs string
31 // children []int
32 // }
33 switch syscallTable[cooked.syscall] {
34 case "fork", "vfork", "clone":
35 ppid := cooked.ppid
36 pid := cooked.pid
37 parent, ok := pids.Load(ppid)
38 if !ok {
39 break
40 }
41 parent.(*process).children = append(parent.(*process).children, pid)
42 pids.Store(pid, &process{
43 timestamp: cooked.timestamp,
44 pid: cooked.pid,
45 ppid: cooked.ppid,
46 argv: cooked.argv,
47 cwd: cooked.cwd,
48 children: make([]int, 0),
49 })
50 fmt.Printf("%v syscall=%d, ppid=%d, pid=%d, cwd=\"%s\", argc=%d, ", cooked.timestamp, cooked.syscall, cooked.ppid, cooked.pid, cooked.cwd, cooked.argc)
51 for i := 0; i < cooked.argc; i++ {
52 fmt.Printf("arg[%d]=\"%s\", ", i, cooked.argv[i])
53 }
54 fmt.Printf("\n")
55 case "exit", "exit_group":
56 _, ok := pids.Load(cooked.pid)
57 if !ok {
58 break
59 }
60 go deletePid(cooked)
61 }
62 }
63}
64
65func deletePid(cooked Event) {
66 time.Sleep(1 * time.Second)
67 Process, ok := pids.Load(cooked.pid)
68 if !ok {
69 return
70 }
71 pProcess := Process.(*process)
72
73 // 先从爹那里注销户籍
74 parent, ok := pids.Load(pProcess.ppid)
75 if ok {
76 pParent := parent.(*process)
77 for i, child := range pParent.children {
78 if child == pProcess.pid {
79 pParent.children = append(pParent.children[:i], pParent.children[i+1:]...)
80 break
81 }
82 }
83 }
84
85 // 子进程需要收容
86 for i := 0; i < len(pProcess.children); i++ {
87 child, ok := pids.Load(pProcess.children[i])
88 if ok {
89 child.(*process).ppid = 1
90 }
91 }
92
93 // 可以去死了
94 pids.Delete(cooked.pid)
95 _, ok = pids.Load(cooked.pid)
96 fmt.Printf("%v Goodbye, %d! ok = %v\n", time.Now(), cooked.pid, ok)
97}
diff --git a/src/global.go b/src/global.go
new file mode 100644
index 0000000..4e08866
--- /dev/null
+++ b/src/global.go
@@ -0,0 +1,10 @@
1package main
2
3import "sync"
4
5var pids sync.Map // 古希腊掌管进程的神,int->*process
6var wg sync.WaitGroup // 掌管协程
7var rawChan chan interface{} // 从接收到整理的管道
8var cookedChan chan Event // 整理好的信息的管道
9var syscallTable [500]string //记录一下系统调用
10var containerdPid int
diff --git a/go.mod b/src/go.mod
index 2969b32..2969b32 100644
--- a/go.mod
+++ b/src/go.mod
diff --git a/go.sum b/src/go.sum
index 7ce498a..7ce498a 100644
--- a/go.sum
+++ b/src/go.sum
diff --git a/src/godo.go b/src/godo.go
new file mode 100644
index 0000000..6f73893
--- /dev/null
+++ b/src/godo.go
@@ -0,0 +1,116 @@
1package main
2
3import (
4 "flag"
5 "fmt"
6 "log"
7 "os"
8 "os/exec"
9 "time"
10
11 "github.com/elastic/go-libaudit/v2"
12)
13
14var (
15 fs = flag.NewFlagSet("audit", flag.ExitOnError)
16 diag = fs.String("diag", "", "dump raw information from kernel to file")
17 rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)")
18 backlog = fs.Uint("backlog", 8192, "backlog limit")
19 immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)")
20 receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+")
21)
22
23type Event struct {
24 timestamp time.Time
25 pid, ppid int
26 syscall int
27 argc int
28 argv []string
29 cwd string
30}
31
32type process struct {
33 timestamp time.Time
34 pid, ppid int
35 argv []string
36 cwd string
37 rootfs string
38 children []int
39}
40
41func main() {
42 // 检查用户身份,并添加auditd规则,监听所有syscall
43 if os.Geteuid() != 0 {
44 fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid())
45 return
46 }
47
48 // 所有的系统调用号与名称的关系
49 err := figureOutSyscalls()
50 if err != nil {
51 fmt.Printf("Error figuring out syscall numbers: %v\n", err)
52 }
53
54 syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"}
55 var auditCmd *exec.Cmd
56 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则
57 auditCmd.Run()
58 // 设置监听规则
59 for i := 0; i < len(syscall); i++ {
60 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall[i])
61 auditCmd.Run()
62 }
63
64 // 查找pid
65 containerdPid, err = getPid()
66 if err != nil {
67 fmt.Printf("Error finding containerd: %v\n", err)
68 return
69 }
70
71 // 创世之神,1号进程
72 // pids[1] = &process{rootfs: "/", children: make([]int, 0)}
73 // pids[1].children = append(pids[1].children, containerdPid)
74 // 1号进程还是不要在进程树上直接出现了,不然它的小儿子们都会出现
75
76 // /usr/bin/containerd,也就是我们最关注的进程
77 // pids[containerdPid] = &process{rootfs: "/", children: make([]int, 0)}
78 pids.Store(containerdPid, &process{
79 ppid: 1,
80 pid: containerdPid,
81 argv: make([]string, 0),
82 cwd: "/",
83 rootfs: "/",
84 children: make([]int, 0),
85 })
86 p, ok := pids.Load(containerdPid)
87 if !ok {
88 fmt.Printf("???\n")
89 return
90 }
91 p.(*process).argv = append(p.(*process).argv, "/usr/bin/containerd")
92
93 // 开始运行,解析命令行参数后监听
94 if err := fs.Parse(os.Args[1:]); err != nil {
95 log.Fatal(err)
96 }
97
98 if err := read(); err != nil {
99 log.Fatalf("error: %v", err)
100 }
101}
102
103func coroutine(client *libaudit.AuditClient) {
104 // 各协程至此开始
105 rawChan = make(chan interface{})
106 cookedChan = make(chan Event)
107 wg.Add(1)
108 go receive(client)
109 wg.Add(1)
110 go orgnaze()
111 wg.Add(1)
112 go deal()
113
114 wg.Wait()
115 time.Sleep(2 * time.Second)
116}
diff --git a/src/organize.go b/src/organize.go
new file mode 100644
index 0000000..025d8c0
--- /dev/null
+++ b/src/organize.go
@@ -0,0 +1,124 @@
1package main
2
3import (
4 "regexp"
5 "strconv"
6 "strings"
7
8 "github.com/elastic/go-libaudit/v2"
9 "github.com/elastic/go-libaudit/v2/auparse"
10)
11
12func orgnaze() {
13 defer wg.Done()
14 defer close(cookedChan)
15 // 接收信息
16 var raw interface{}
17 var ok bool
18 var rawEvent libaudit.RawAuditMessage
19 // 事件信息
20 var eventId, argc int
21 var err [6]error
22 var event, cooked Event
23 // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
24 eventTable := make(map[int]*Event)
25 // 要用的正则匹配列表
26 syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?ppid=(\d+) pid=(\d+).*?$`)
27 execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
28 argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
29 cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
30 proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
31 eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
32 for {
33 raw, ok = <-rawChan
34 if !ok {
35 break
36 }
37 rawEvent = raw.(libaudit.RawAuditMessage)
38
39 // type Event struct {
40 // timestamp time.Time
41 // pid, ppid int
42 // syscall int
43 // argc int
44 // args []string
45 // cwd string
46 // }
47 switch rawEvent.Type {
48 case auparse.AUDIT_SYSCALL:
49 if syscallRegex.Match(rawEvent.Data) {
50 match := syscallRegex.FindSubmatch(rawEvent.Data)
51 event.timestamp, err[0] = getTimeFromStr(string(match[1]))
52 eventId, err[1] = strconv.Atoi(string(match[2]))
53 event.syscall, err[2] = strconv.Atoi(string(match[3]))
54 event.ppid, err[3] = strconv.Atoi(string(match[4]))
55 event.pid, err[4] = strconv.Atoi(string(match[5]))
56 eventTable[eventId] = &Event{
57 timestamp: event.timestamp,
58 syscall: event.syscall,
59 ppid: event.ppid,
60 pid: event.pid,
61 argc: 0,
62 argv: make([]string, 0),
63 cwd: "",
64 }
65 }
66 case auparse.AUDIT_EXECVE:
67 if execveRegex.Match(rawEvent.Data) {
68 match := execveRegex.FindSubmatch(rawEvent.Data)
69 eventId, err[0] = strconv.Atoi(string(match[1]))
70 argc, err[1] = strconv.Atoi(string(match[2]))
71 if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) {
72 match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
73 for i := 0; i < argc; i++ {
74 if len(match[i][2]) == 0 {
75 // 代表着匹配到的是十六进制数
76 str := hexToAscii(string(match[i][3]))
77 eventTable[eventId].argv = append(eventTable[eventId].argv, str)
78 } else {
79 eventTable[eventId].argv = append(eventTable[eventId].argv, string(match[i][2]))
80 }
81 }
82 eventTable[eventId].argc = argc
83 }
84 }
85 // case auparse.AUDIT_PATH:
86 case auparse.AUDIT_CWD:
87 if cwdRegex.Match(rawEvent.Data) {
88 match := cwdRegex.FindSubmatch(rawEvent.Data)
89 eventId, err[0] = strconv.Atoi(string(match[1]))
90 eventTable[eventId].cwd = string(match[2])
91 }
92 case auparse.AUDIT_PROCTITLE:
93 if proctitleRegex.Match(rawEvent.Data) {
94 var cmdline string
95 var pEvent *Event
96 match := proctitleRegex.FindSubmatch(rawEvent.Data)
97 eventId, err[0] = strconv.Atoi(string(match[1]))
98 pEvent = eventTable[eventId]
99 if pEvent.argc == 0 {
100 // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
101 if match[3] == nil {
102 // PROCTITLE写的是十六进制,转换为字符串
103 cmdline = hexToAscii(string(match[4]))
104 } else {
105 cmdline = string(match[3])
106 }
107 pEvent.argv = strings.Split(cmdline, " ")
108 pEvent.argc = len(eventTable[eventId].argv)
109 }
110 }
111 case auparse.AUDIT_EOE:
112 if eoeRegex.Match(rawEvent.Data) {
113 match := eoeRegex.FindSubmatch(rawEvent.Data)
114 eventId, err[0] = strconv.Atoi(string(match[1]))
115 // ATTENTION: 事件整理完毕,即刻发出,是否合理呢?
116 cooked = *eventTable[eventId] // 应当采用深拷贝吗?有待实验
117 cookedChan <- cooked
118 delete(eventTable, eventId) //发出之后就从信息表扔掉,死人别占地
119 }
120 default:
121 // ATTENTION: 这里也需要做防护
122 }
123 }
124}
diff --git a/src/receive.go b/src/receive.go
new file mode 100644
index 0000000..c0dea00
--- /dev/null
+++ b/src/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}