diff options
Diffstat (limited to 'godo.go')
-rw-r--r-- | godo.go | 261 |
1 files changed, 257 insertions, 4 deletions
@@ -9,12 +9,15 @@ import ( | |||
9 | "os" | 9 | "os" |
10 | "os/exec" | 10 | "os/exec" |
11 | "path/filepath" | 11 | "path/filepath" |
12 | "regexp" | ||
12 | "strconv" | 13 | "strconv" |
13 | "strings" | 14 | "strings" |
14 | "sync" | 15 | "sync" |
16 | "time" | ||
15 | 17 | ||
16 | "github.com/elastic/go-libaudit/v2" | 18 | "github.com/elastic/go-libaudit/v2" |
17 | "github.com/elastic/go-libaudit/v2/auparse" | 19 | "github.com/elastic/go-libaudit/v2/auparse" |
20 | "github.com/mohae/deepcopy" | ||
18 | ) | 21 | ) |
19 | 22 | ||
20 | var ( | 23 | var ( |
@@ -26,8 +29,19 @@ var ( | |||
26 | receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+") | 29 | receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+") |
27 | ) | 30 | ) |
28 | 31 | ||
32 | type Event struct { | ||
33 | timestamp time.Time | ||
34 | pid, ppid int | ||
35 | syscall int | ||
36 | argc int | ||
37 | args []string | ||
38 | cwd string | ||
39 | } | ||
40 | |||
29 | type process struct { | 41 | type process struct { |
30 | cmdline string | 42 | cmdline string |
43 | argv []string | ||
44 | cwd string | ||
31 | rootfs string | 45 | rootfs string |
32 | children []int | 46 | children []int |
33 | } | 47 | } |
@@ -35,6 +49,10 @@ type process struct { | |||
35 | var pids map[int]*process //古希腊掌管进程的神 | 49 | var pids map[int]*process //古希腊掌管进程的神 |
36 | var containers map[string]int // 古希腊掌管容器的神 | 50 | var containers map[string]int // 古希腊掌管容器的神 |
37 | var wg sync.WaitGroup // 掌管协程 | 51 | var wg sync.WaitGroup // 掌管协程 |
52 | var rawChan chan interface{} // 从接收到整理的管道,这里不是原始数据类型,下文解释 | ||
53 | var cookedChan chan Event // 整理好的信息的管道 | ||
54 | |||
55 | var syscallTable [500]string //记录一下系统调用 | ||
38 | 56 | ||
39 | func main() { | 57 | func main() { |
40 | // 检查用户身份,并添加auditd规则,监听所有syscall | 58 | // 检查用户身份,并添加auditd规则,监听所有syscall |
@@ -42,7 +60,14 @@ func main() { | |||
42 | fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid()) | 60 | fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid()) |
43 | return | 61 | return |
44 | } | 62 | } |
45 | syscall := [5]string{"fork", "vfork", "execve", "exit", "exit_group"} | 63 | |
64 | // 所有的系统调用号与名称的关系 | ||
65 | err := figureOutSyscalls() | ||
66 | if err != nil { | ||
67 | fmt.Printf("Error figuring out syscall numbers: %v\n", err) | ||
68 | } | ||
69 | |||
70 | syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"} | ||
46 | var auditCmd *exec.Cmd | 71 | var auditCmd *exec.Cmd |
47 | auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 | 72 | auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 |
48 | auditCmd.Run() | 73 | auditCmd.Run() |
@@ -58,9 +83,14 @@ func main() { | |||
58 | fmt.Printf("Error finding containerd: %v\n", err) | 83 | fmt.Printf("Error finding containerd: %v\n", err) |
59 | return | 84 | return |
60 | } | 85 | } |
61 | 86 | // 数据结构初始化 | |
62 | pids = make(map[int]*process) | 87 | pids = make(map[int]*process) |
63 | containers = make(map[string]int) | 88 | containers = make(map[string]int) |
89 | |||
90 | // 创世之神,1号进程 | ||
91 | pids[1] = &process{rootfs: "/", children: make([]int, 0)} | ||
92 | pids[1].children = append(pids[1].children, containerdPid) | ||
93 | // /usr/bin/containerd,也就是我们最关注的进程 | ||
64 | pids[containerdPid] = &process{cmdline: "/usr/bin/cmdline", rootfs: "/", children: make([]int, 0)} | 94 | pids[containerdPid] = &process{cmdline: "/usr/bin/cmdline", rootfs: "/", children: make([]int, 0)} |
65 | 95 | ||
66 | // 开始运行,解析命令行参数后监听 | 96 | // 开始运行,解析命令行参数后监听 |
@@ -73,6 +103,29 @@ func main() { | |||
73 | } | 103 | } |
74 | } | 104 | } |
75 | 105 | ||
106 | func figureOutSyscalls() error { | ||
107 | NRRegex := regexp.MustCompile(`#define __NR_(.*?) (\d+)$`) | ||
108 | file, err := os.Open("/usr/include/asm/unistd_64.h") | ||
109 | if err != nil { | ||
110 | return err | ||
111 | } | ||
112 | defer file.Close() | ||
113 | |||
114 | scanner := bufio.NewScanner(file) | ||
115 | for scanner.Scan() { | ||
116 | line := scanner.Text() | ||
117 | if NRRegex.MatchString(line) { | ||
118 | match := NRRegex.FindStringSubmatch(line) | ||
119 | num, err := strconv.Atoi(match[2]) | ||
120 | if err != nil { | ||
121 | return err | ||
122 | } | ||
123 | syscallTable[num] = match[1] | ||
124 | } | ||
125 | } | ||
126 | return nil | ||
127 | } | ||
128 | |||
76 | func getPid() (int, error) { | 129 | func getPid() (int, error) { |
77 | // 指定要搜索的关键词 | 130 | // 指定要搜索的关键词 |
78 | keyword := "/usr/bin/containerd" | 131 | keyword := "/usr/bin/containerd" |
@@ -122,6 +175,37 @@ func containsKeyword(pid int, keyword string) bool { | |||
122 | return false | 175 | return false |
123 | } | 176 | } |
124 | 177 | ||
178 | func getTimeFromStr(timeStr string) (time.Time, error) { | ||
179 | timestampFloat, err := strconv.ParseFloat(timeStr, 64) | ||
180 | if err != nil { | ||
181 | return time.Unix(0, 0), err | ||
182 | } | ||
183 | secs := int64(timestampFloat) | ||
184 | nsecs := int64((timestampFloat - float64(secs)) * 1e9) | ||
185 | |||
186 | // 只精确到毫秒就够了 | ||
187 | t := time.Unix(secs, nsecs).Truncate(time.Millisecond) | ||
188 | return t, nil | ||
189 | } | ||
190 | |||
191 | func hexToAscii(hexString string) string { | ||
192 | bytes := []byte{} | ||
193 | for i := 0; i < len(hexString); i += 2 { | ||
194 | hexPair := hexString[i : i+2] | ||
195 | // 将十六进制数转换为十进制数 | ||
196 | decimal, err := strconv.ParseInt(hexPair, 16, 8) | ||
197 | if err != nil { | ||
198 | return "Invalid hex string" | ||
199 | } | ||
200 | char := byte(decimal) | ||
201 | bytes = append(bytes, char) | ||
202 | } | ||
203 | |||
204 | asciiString := strings.ReplaceAll(string(bytes), "\000", " ") | ||
205 | |||
206 | return asciiString | ||
207 | } | ||
208 | |||
125 | func read() error { | 209 | func read() error { |
126 | // Write netlink response to a file for further analysis or for writing | 210 | // Write netlink response to a file for further analysis or for writing |
127 | // tests cases. | 211 | // tests cases. |
@@ -192,10 +276,25 @@ func read() error { | |||
192 | } | 276 | } |
193 | } | 277 | } |
194 | 278 | ||
195 | return receive(client) | 279 | // 各协程至此开始 |
280 | // return receive(client) | ||
281 | rawChan = make(chan interface{}) | ||
282 | cookedChan = make(chan Event) | ||
283 | wg.Add(1) | ||
284 | go receive(client) | ||
285 | wg.Add(1) | ||
286 | go orgnaze() | ||
287 | wg.Add(1) | ||
288 | go deal() | ||
289 | |||
290 | wg.Wait() | ||
291 | time.Sleep(2 * time.Second) | ||
292 | return nil | ||
196 | } | 293 | } |
197 | 294 | ||
198 | func receive(r *libaudit.AuditClient) error { | 295 | func receive(r *libaudit.AuditClient) error { |
296 | defer wg.Done() | ||
297 | defer close(rawChan) | ||
199 | for { | 298 | for { |
200 | rawEvent, err := r.Receive(false) | 299 | rawEvent, err := r.Receive(false) |
201 | if err != nil { | 300 | if err != nil { |
@@ -208,6 +307,160 @@ func receive(r *libaudit.AuditClient) error { | |||
208 | continue | 307 | continue |
209 | } | 308 | } |
210 | 309 | ||
211 | fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data) | 310 | rawEventMessage := deepcopy.Copy(*rawEvent) |
311 | rawChan <- rawEventMessage | ||
312 | } | ||
313 | } | ||
314 | |||
315 | func orgnaze() { | ||
316 | defer wg.Done() | ||
317 | defer close(cookedChan) | ||
318 | // 接收信息 | ||
319 | var raw interface{} | ||
320 | var ok bool | ||
321 | var rawEvent libaudit.RawAuditMessage | ||
322 | // 事件信息 | ||
323 | var eventId, argc int | ||
324 | var err [6]error | ||
325 | var event, cooked Event | ||
326 | // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的 | ||
327 | eventTable := make(map[int]*Event) | ||
328 | // 要用的正则匹配列表 | ||
329 | syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?ppid=(\d+) pid=(\d+).*?$`) | ||
330 | execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`) | ||
331 | argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`) | ||
332 | cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`) | ||
333 | proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`) | ||
334 | eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`) | ||
335 | for { | ||
336 | raw, ok = <-rawChan | ||
337 | if !ok { | ||
338 | break | ||
339 | } | ||
340 | rawEvent = raw.(libaudit.RawAuditMessage) | ||
341 | |||
342 | // type Event struct { | ||
343 | // timestamp time.Time | ||
344 | // pid, ppid int | ||
345 | // syscall int | ||
346 | // argc int | ||
347 | // args []string | ||
348 | // cwd string | ||
349 | // } | ||
350 | switch rawEvent.Type { | ||
351 | case auparse.AUDIT_SYSCALL: | ||
352 | if syscallRegex.Match(rawEvent.Data) { | ||
353 | match := syscallRegex.FindSubmatch(rawEvent.Data) | ||
354 | event.timestamp, err[0] = getTimeFromStr(string(match[1])) | ||
355 | eventId, err[1] = strconv.Atoi(string(match[2])) | ||
356 | event.syscall, err[2] = strconv.Atoi(string(match[3])) | ||
357 | event.ppid, err[3] = strconv.Atoi(string(match[4])) | ||
358 | event.pid, err[4] = strconv.Atoi(string(match[5])) | ||
359 | eventTable[eventId] = &Event{ | ||
360 | timestamp: event.timestamp, | ||
361 | syscall: event.syscall, | ||
362 | ppid: event.ppid, | ||
363 | pid: event.pid, | ||
364 | argc: 0, | ||
365 | args: make([]string, 0), | ||
366 | cwd: "", | ||
367 | } | ||
368 | } | ||
369 | case auparse.AUDIT_EXECVE: | ||
370 | if execveRegex.Match(rawEvent.Data) { | ||
371 | match := execveRegex.FindSubmatch(rawEvent.Data) | ||
372 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
373 | argc, err[1] = strconv.Atoi(string(match[2])) | ||
374 | if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) { | ||
375 | match := argsRegex.FindAllSubmatch(rawEvent.Data, -1) | ||
376 | for i := 0; i < argc; i++ { | ||
377 | if len(match[i][2]) == 0 { | ||
378 | // 代表着匹配到的是十六进制数 | ||
379 | str := hexToAscii(string(match[i][3])) | ||
380 | eventTable[eventId].args = append(eventTable[eventId].args, str) | ||
381 | fmt.Printf("Origin: \"%s\", Res: \"%s\"\n", match[i][3], str) | ||
382 | } else { | ||
383 | eventTable[eventId].args = append(eventTable[eventId].args, string(match[i][2])) | ||
384 | } | ||
385 | } | ||
386 | eventTable[eventId].argc = argc | ||
387 | } | ||
388 | } | ||
389 | // case auparse.AUDIT_PATH: | ||
390 | case auparse.AUDIT_CWD: | ||
391 | if cwdRegex.Match(rawEvent.Data) { | ||
392 | match := cwdRegex.FindSubmatch(rawEvent.Data) | ||
393 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
394 | eventTable[eventId].cwd = string(match[2]) | ||
395 | } | ||
396 | case auparse.AUDIT_PROCTITLE: | ||
397 | if proctitleRegex.Match(rawEvent.Data) { | ||
398 | var cmdline string | ||
399 | var pEvent *Event | ||
400 | match := proctitleRegex.FindSubmatch(rawEvent.Data) | ||
401 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
402 | pEvent = eventTable[eventId] | ||
403 | if pEvent.argc == 0 { | ||
404 | // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数 | ||
405 | if match[3] == nil { | ||
406 | // PROCTITLE写的是十六进制,转换为字符串 | ||
407 | cmdline = hexToAscii(string(match[4])) | ||
408 | } else { | ||
409 | cmdline = string(match[3]) | ||
410 | } | ||
411 | pEvent.args = strings.Split(cmdline, " ") | ||
412 | pEvent.argc = len(eventTable[eventId].args) | ||
413 | } | ||
414 | // 当读到proctitle的时候,而且是个新进程最好检查一下cwd,如果还为空,找proc | ||
415 | if pEvent.cwd == "" && (pEvent.syscall == 57 || pEvent.syscall == 58 || pEvent.syscall == 59) { | ||
416 | cwdFilePath := fmt.Sprintf("/proc/%d/cwd", pEvent.pid) | ||
417 | pEvent.cwd, err[1] = os.Readlink(cwdFilePath) | ||
418 | if err[1] != nil { | ||
419 | pEvent.cwd = "" | ||
420 | break | ||
421 | } | ||
422 | } | ||
423 | } | ||
424 | case auparse.AUDIT_EOE: | ||
425 | if eoeRegex.Match(rawEvent.Data) { | ||
426 | match := eoeRegex.FindSubmatch(rawEvent.Data) | ||
427 | eventId, err[0] = strconv.Atoi(string(match[1])) | ||
428 | // TODO: 事件整理完毕,即刻发出,是否合理呢? | ||
429 | cooked = *eventTable[eventId] // 应当采用深拷贝吗?有待实验 | ||
430 | cookedChan <- cooked | ||
431 | delete(eventTable, eventId) //发出之后就从信息表扔掉,死人别占地 | ||
432 | } | ||
433 | default: | ||
434 | // TODO: 这里也需要做防护 | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | |||
439 | func deal() { | ||
440 | defer wg.Done() | ||
441 | var cooked Event | ||
442 | var ok bool | ||
443 | for { | ||
444 | cooked, ok = <-cookedChan | ||
445 | if !ok { | ||
446 | break | ||
447 | } | ||
448 | // type Event struct { | ||
449 | // timestamp time.Time | ||
450 | // pid, ppid int | ||
451 | // syscall int | ||
452 | // argc int | ||
453 | // args []string | ||
454 | // cwd string | ||
455 | // } | ||
456 | fmt.Printf("recv: %v syscall=%d, ppid=%d, pid=%d, cwd=\"%s\", argc=%d, ", cooked.timestamp, cooked.syscall, cooked.ppid, cooked.pid, cooked.cwd, cooked.argc) | ||
457 | if len(cooked.args) != cooked.argc { | ||
458 | fmt.Printf("Fuck!\n") | ||
459 | continue | ||
460 | } | ||
461 | for i := 0; i < cooked.argc; i++ { | ||
462 | fmt.Printf("arg[%d]=\"%s\", ", i, cooked.args[i]) | ||
463 | } | ||
464 | fmt.Printf("\n") | ||
212 | } | 465 | } |
213 | } | 466 | } |