summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2024-07-19 17:02:11 +0800
committerWe-unite <3205135446@qq.com>2024-07-19 17:05:29 +0800
commit7cf8e470471d30fc821a8be350dcb97dc64e5add (patch)
treee747afbc698bba079047f17674eef260a4c6733d
parent2e37dcf708be08dff963ee08cb7a9557f32e690d (diff)
downloadgodo-7cf8e470471d30fc821a8be350dcb97dc64e5add.tar.gz
godo-7cf8e470471d30fc821a8be350dcb97dc64e5add.zip
Depart the whole program into several files.
Put all the src code in only one file is to ugly, so devide it! and mv them into src dir to keep the whole repo clear.
-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}