aboutsummaryrefslogtreecommitdiffstats
path: root/godo.go
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 /godo.go
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.
Diffstat (limited to 'godo.go')
-rw-r--r--godo.go535
1 files changed, 0 insertions, 535 deletions
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}