summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--godo.go261
4 files changed, 262 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 6cff91d..8bbe3e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
1.vscode/* 1.vscode/*
2godo \ No newline at end of file 2godo
3
diff --git a/go.mod b/go.mod
index c73fc4f..2969b32 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.21.5
4 4
5require ( 5require (
6 github.com/elastic/go-libaudit/v2 v2.5.0 6 github.com/elastic/go-libaudit/v2 v2.5.0
7 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
7 gopkg.in/yaml.v3 v3.0.1 8 gopkg.in/yaml.v3 v3.0.1
8) 9)
9 10
diff --git a/go.sum b/go.sum
index 6880c39..7ce498a 100644
--- a/go.sum
+++ b/go.sum
@@ -7,6 +7,8 @@ github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN
7github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU= 7github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
8github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 8github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
9github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 9github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
10github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
11github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
10github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
11github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
12github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
diff --git a/godo.go b/godo.go
index 6b6f48f..2ba97d1 100644
--- a/godo.go
+++ b/godo.go
@@ -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
20var ( 23var (
@@ -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
32type Event struct {
33 timestamp time.Time
34 pid, ppid int
35 syscall int
36 argc int
37 args []string
38 cwd string
39}
40
29type process struct { 41type 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 {
35var pids map[int]*process //古希腊掌管进程的神 49var pids map[int]*process //古希腊掌管进程的神
36var containers map[string]int // 古希腊掌管容器的神 50var containers map[string]int // 古希腊掌管容器的神
37var wg sync.WaitGroup // 掌管协程 51var wg sync.WaitGroup // 掌管协程
52var rawChan chan interface{} // 从接收到整理的管道,这里不是原始数据类型,下文解释
53var cookedChan chan Event // 整理好的信息的管道
54
55var syscallTable [500]string //记录一下系统调用
38 56
39func main() { 57func 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
106func 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
76func getPid() (int, error) { 129func 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
178func 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
191func 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
125func read() error { 209func 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
198func receive(r *libaudit.AuditClient) error { 295func 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
315func 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
439func 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}