summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2024-07-26 15:20:45 +0800
committerWe-unite <3205135446@qq.com>2024-07-26 15:20:45 +0800
commitb765715b4795ce4bc8940c7b1a1092a78550de94 (patch)
tree2e81f5f914b59f332e54d6802c942224d5f37ff6
parentfc61a4a525846fa31ee2288df4e82f745bb39c95 (diff)
downloadgodo-b765715b4795ce4bc8940c7b1a1092a78550de94.tar.gz
godo-b765715b4795ce4bc8940c7b1a1092a78550de94.zip
The 1st prompt to record file changed by process
To record it, we must listen to open/write and several syscalls, and now I've add open into the 2nd coroutine. In syscall open, what we should do is to judge the permission flag (the 2nd param in the syscall), to find out if it can write to the file. If so, the exit code is its file descriptor, and when write is called, the audit shows only file descriptor but no file name. So the next step is to add things into 3rd coroutine, to make the whole program running again, and find out bugs.
-rwxr-xr-xold/au.sh24
-rw-r--r--src/deal.go2
-rw-r--r--src/global.go40
-rw-r--r--src/godo.go14
-rw-r--r--src/organize.go361
5 files changed, 286 insertions, 155 deletions
diff --git a/old/au.sh b/old/au.sh
new file mode 100755
index 0000000..66da315
--- /dev/null
+++ b/old/au.sh
@@ -0,0 +1,24 @@
1#!/bin/bash
2
3##########################################################################
4# File Name : au.sh
5# Encoding : utf-8
6# Author : We-unite
7# Email : weunite1848@gmail.com
8# Created Time : 2024-07-25 17:56:49
9##########################################################################
10
11set -e
12
13if [ $EUID -ne 0 ]; then
14 echo -e "Please run as root/sudo"
15 exit 1
16fi
17
18auditctl -D
19
20fileSyscall="open write creat unlink mkdir rmdir chmod fchmod chown fchown lchown flock"
21for syscall in $fileSyscall; do
22 auditctl -a exit,always -F arch=b64 -S $syscall
23done
24
diff --git a/src/deal.go b/src/deal.go
index 783dab8..db6fc26 100644
--- a/src/deal.go
+++ b/src/deal.go
@@ -125,7 +125,7 @@ func deal() {
125 // 先fork抵达,插入 125 // 先fork抵达,插入
126 pidCol.InsertOne(bson.M{ 126 pidCol.InsertOne(bson.M{
127 "children": []bson.M{}, 127 "children": []bson.M{},
128 "execve": []bson.M{ 128 "exe_args": []bson.M{
129 { 129 {
130 "timestamp": cooked.timestamp, 130 "timestamp": cooked.timestamp,
131 "execve": cooked.argv, 131 "execve": cooked.argv,
diff --git a/src/global.go b/src/global.go
index c3001ab..3ddbc79 100644
--- a/src/global.go
+++ b/src/global.go
@@ -5,23 +5,35 @@ import (
5 "time" 5 "time"
6) 6)
7 7
8type eventType int
9
10const (
11 NEWPID eventType = iota
12 PIDEXIT
13 FILEOPEN
14 FILEWRITE
15 TYPENUM
16)
17
8type Event struct { 18type Event struct {
9 timestamp time.Time 19 tag eventType
10 pid, ppid int 20 timestamp time.Time
11 syscall int 21 pid, ppid int
12 exit_code uint64 22 syscall int
13 argc int 23 exit_code uint64
14 argv []string 24 argc int
15 cwd string 25 argv []string
26 cwd string
27 syscallParam [4]uint64
28 pathName string
16} 29}
17 30
18type process struct { 31func (et eventType) String() string {
19 timestamp time.Time 32 names := []string{"newPid", "pidExit", "open", "write", "typeNum"}
20 pid, ppid int 33 if et < NEWPID || et > TYPENUM {
21 argv []string 34 return "Unknown"
22 cwd string 35 }
23 rootfs string 36 return names[et]
24 children []int
25} 37}
26 38
27var wg sync.WaitGroup // 掌管协程 39var wg sync.WaitGroup // 掌管协程
diff --git a/src/godo.go b/src/godo.go
index cc29a01..2a00dad 100644
--- a/src/godo.go
+++ b/src/godo.go
@@ -33,16 +33,24 @@ func main() {
33 fmt.Printf("Error figuring out syscall numbers: %v\n", err) 33 fmt.Printf("Error figuring out syscall numbers: %v\n", err)
34 } 34 }
35 35
36 syscall := [6]string{"fork", "vfork", "clone", "execve", "exit", "exit_group"}
37 var auditCmd *exec.Cmd 36 var auditCmd *exec.Cmd
38 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 37 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则
39 auditCmd.Run() 38 auditCmd.Run()
39
40 pidSyscall := []string{"fork", "vfork", "clone", "execve", "exit", "exit_group"}
40 // 设置监听规则 41 // 设置监听规则
41 for i := 0; i < len(syscall); i++ { 42 for i := 0; i < len(pidSyscall); i++ {
42 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", syscall[i]) 43 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i])
43 auditCmd.Run() 44 auditCmd.Run()
44 } 45 }
45 46
47 // // 监听文件的消息
48 // fileSyscall := []string{"open", "write", "creat", "unlink", "opendir", "mkdir", "rmdir", "chmod", "fchmod", "chown", "fchown", "lchown", "flock"}
49 // for i := 0; i < len(fileSyscall); i++ {
50 // auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i])
51 // auditCmd.Run()
52 // }
53
46 // 查找pid 54 // 查找pid
47 containerdPid, err = getPid() 55 containerdPid, err = getPid()
48 if err != nil { 56 if err != nil {
diff --git a/src/organize.go b/src/organize.go
index d963288..679f361 100644
--- a/src/organize.go
+++ b/src/organize.go
@@ -6,34 +6,40 @@ import (
6 "strconv" 6 "strconv"
7 "strings" 7 "strings"
8 "sync" 8 "sync"
9 "syscall"
9 10
10 "github.com/elastic/go-libaudit/v2" 11 "github.com/elastic/go-libaudit/v2"
11 "github.com/elastic/go-libaudit/v2/auparse" 12 "github.com/elastic/go-libaudit/v2/auparse"
12) 13)
13 14
15// 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
16var eventTable sync.Map
17
18// 事件信息
19var tmp any
20var ok bool
21var event Event
22var pEvent *Event
23var eventId, argc int
24var err [6]error
25
26// 要用的正则匹配列表
27var (
28 syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?(exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?$`)
29 execveRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
30 argsRegex = regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
31 pathRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\):.*?name="(.*?)"`)
32 cwdRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
33 proctitleRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
34 eoeRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
35)
36
14func orgnaze() { 37func orgnaze() {
15 defer wg.Done() 38 defer wg.Done()
16 defer close(cookedChan) 39 defer close(cookedChan)
17 // 接收信息 40 // 接收信息
18 var raw interface{} 41 var raw interface{}
19 var ok bool
20 var rawEvent libaudit.RawAuditMessage 42 var rawEvent libaudit.RawAuditMessage
21 // 事件信息
22 var eventId, argc int
23 var err [6]error
24 var event Event
25 var pEvent *Event
26 var tmp any
27 // 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
28 var eventTable sync.Map
29
30 // 要用的正则匹配列表
31 syscallRegex := regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+).*?(exit=([-+]?\d+))? a0=([0-9a-fA-F]+).*?ppid=(\d+) pid=(\d+).*?$`)
32 execveRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
33 argsRegex := regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
34 cwdRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
35 proctitleRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
36 eoeRegex := regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
37 43
38 for { 44 for {
39 raw, ok = <-rawChan 45 raw, ok = <-rawChan
@@ -44,132 +50,213 @@ func orgnaze() {
44 50
45 switch rawEvent.Type { 51 switch rawEvent.Type {
46 case auparse.AUDIT_SYSCALL: 52 case auparse.AUDIT_SYSCALL:
47 if syscallRegex.Match(rawEvent.Data) { 53 syscallRaw(rawEvent)
48 match := syscallRegex.FindSubmatch(rawEvent.Data)
49 event.timestamp, err[0] = getTimeFromStr(string(match[1]))
50 eventId, err[1] = strconv.Atoi(string(match[2]))
51 event.syscall, err[2] = strconv.Atoi(string(match[3]))
52 var exit int
53 var a0 uint64
54 if string(match[5]) == "" {
55 // exit没捕获到
56 exit = 0
57 } else {
58 exit, err[3] = strconv.Atoi(string(match[5]))
59 }
60 if string(match[6]) == "" {
61 a0 = 0
62 } else {
63 // 系统调用的第一个参数
64 // exit和exit_group都是syscall_define1,只有一个参数
65 // fork没参数,clone几个参数不重要
66 // execve三个参数咱也不关心
67 // 所以看第一个就够了
68 a0, err[4] = strconv.ParseUint(string(match[6]), 16, 64)
69 }
70 event.ppid, err[4] = strconv.Atoi(string(match[7]))
71 event.pid, err[5] = strconv.Atoi(string(match[8]))
72 if syscallTable[event.syscall] == "clone" {
73 if exit == 0 || event.pid > exit {
74 // exit=0是给新进程的返回,没用
75 // pid>exit,证明有问题,抛弃
76 break
77 } else {
78 eventTable.Store(eventId, &Event{
79 timestamp: event.timestamp,
80 syscall: event.syscall,
81 exit_code: 0,
82 ppid: event.pid,
83 pid: exit,
84 argc: 0,
85 argv: make([]string, 0),
86 cwd: "",
87 })
88 }
89 } else {
90 eventTable.Store(eventId, &Event{
91 timestamp: event.timestamp,
92 syscall: event.syscall,
93 exit_code: a0,
94 ppid: event.ppid,
95 pid: event.pid,
96 argc: 0,
97 argv: make([]string, 0),
98 cwd: "",
99 })
100 }
101 }
102 case auparse.AUDIT_EXECVE: 54 case auparse.AUDIT_EXECVE:
103 if execveRegex.Match(rawEvent.Data) { 55 execve(rawEvent)
104 match := execveRegex.FindSubmatch(rawEvent.Data)
105 eventId, err[0] = strconv.Atoi(string(match[1]))
106 argc, err[1] = strconv.Atoi(string(match[2]))
107 tmp, ok = eventTable.Load(eventId)
108 if !ok {
109 break
110 }
111 pEvent = tmp.(*Event)
112 if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) {
113 match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
114 for i := 0; i < argc; i++ {
115 if len(match[i][2]) == 0 {
116 // 代表着匹配到的是十六进制数
117 str := hexToAscii(string(match[i][3]))
118 pEvent.argv = append(pEvent.argv, str)
119 } else {
120 pEvent.argv = append(pEvent.argv, string(match[i][2]))
121 }
122 }
123 pEvent.argc = argc
124 }
125 }
126 case auparse.AUDIT_CWD: 56 case auparse.AUDIT_CWD:
127 if cwdRegex.Match(rawEvent.Data) { 57 cwd(rawEvent)
128 match := cwdRegex.FindSubmatch(rawEvent.Data) 58 case auparse.AUDIT_PATH:
129 eventId, err[0] = strconv.Atoi(string(match[1])) 59 path(rawEvent)
130 tmp, ok = eventTable.Load(eventId)
131 if !ok {
132 break
133 }
134 tmp.(*Event).cwd = string(match[2])
135 }
136 case auparse.AUDIT_PROCTITLE: 60 case auparse.AUDIT_PROCTITLE:
137 if proctitleRegex.Match(rawEvent.Data) { 61 proctitle(rawEvent)
138 var cmdline string
139 match := proctitleRegex.FindSubmatch(rawEvent.Data)
140 eventId, err[0] = strconv.Atoi(string(match[1]))
141 tmp, ok = eventTable.Load(eventId)
142 if !ok {
143 break
144 }
145 pEvent = tmp.(*Event)
146 if pEvent.argc == 0 {
147 // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
148 if match[3] == nil {
149 // PROCTITLE写的是十六进制,转换为字符串
150 cmdline = hexToAscii(string(match[4]))
151 } else {
152 cmdline = string(match[3])
153 }
154 pEvent.argv = strings.Split(cmdline, " ")
155 pEvent.argc = len(pEvent.argv)
156 }
157 }
158 case auparse.AUDIT_EOE: 62 case auparse.AUDIT_EOE:
159 if eoeRegex.Match(rawEvent.Data) { 63 eoe(rawEvent)
160 match := eoeRegex.FindSubmatch(rawEvent.Data)
161 eventId, err[0] = strconv.Atoi(string(match[1]))
162 tmp, ok = eventTable.Load(eventId)
163 if !ok {
164 break
165 }
166 cooked := *(tmp.(*Event))
167 cookedChan <- cooked
168 eventTable.Delete(eventId) // 死人别占地
169 fmt.Printf("%d: %3d %6d %6d\n", eventId, cooked.syscall, cooked.ppid, cooked.pid)
170 }
171 default: 64 default:
172 // ATTENTION: 这里也需要做防护 65 // ATTENTION: 这里也需要做防护
173 } 66 }
174 } 67 }
175} 68}
69
70func syscallRaw(rawEvent libaudit.RawAuditMessage) {
71 if !syscallRegex.Match(rawEvent.Data) {
72 return
73 }
74
75 var exit int
76 var a [4]uint64
77 // 捕获基础信息
78 match := syscallRegex.FindSubmatch(rawEvent.Data)
79 event.timestamp, err[0] = getTimeFromStr(string(match[1]))
80 eventId, err[1] = strconv.Atoi(string(match[2]))
81 event.syscall, err[2] = strconv.Atoi(string(match[3]))
82 if string(match[5]) == "" {
83 // exit没捕获到
84 exit = 0
85 } else {
86 exit, err[3] = strconv.Atoi(string(match[5]))
87 }
88 event.ppid, err[4] = strconv.Atoi(string(match[6]))
89 event.pid, err[5] = strconv.Atoi(string(match[7]))
90
91 // 捕获参数
92 if !argsRegex.Match(rawEvent.Data) {
93 fmt.Printf("Error: don't get args in syscall event!\n")
94 return
95 }
96 argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
97 for i := 0; i < 4; i++ {
98 a[i], err[0] = strconv.ParseUint(string(argsMatch[i][2]), 16, 64)
99 }
100
101 switch syscallTable[event.syscall] {
102 case "clone":
103 if exit == 0 || event.pid > exit {
104 // exit=0是给新进程的返回,没用
105 // pid>exit,证明有问题,抛弃
106 break
107 } else {
108 eventTable.Store(eventId, &Event{
109 tag: NEWPID,
110 timestamp: event.timestamp,
111 syscall: event.syscall,
112 exit_code: 0,
113 ppid: event.pid,
114 pid: exit,
115 argc: 0,
116 argv: make([]string, 0),
117 cwd: "",
118 })
119 }
120 case "open":
121 // 检查打开的权限
122 if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 {
123 break
124 }
125 // TRUNC应该被直接标记为改变,而不是打开
126 eventTable.Store(eventId, &Event{
127 tag: FILEOPEN,
128 timestamp: event.timestamp,
129 syscall: event.syscall,
130 exit_code: uint64(exit),
131 ppid: event.ppid,
132 pid: event.pid,
133 argc: 0,
134 argv: make([]string, 0),
135 cwd: "",
136 syscallParam: a,
137 pathName: "",
138 })
139 case "exit", "exit_group":
140 eventTable.Store(eventId, &Event{
141 tag: PIDEXIT,
142 timestamp: event.timestamp,
143 syscall: event.syscall,
144 exit_code: a[0],
145 ppid: event.ppid,
146 pid: event.pid,
147 argc: 0,
148 argv: make([]string, 0),
149 cwd: "",
150 })
151 }
152}
153
154func execve(rawEvent libaudit.RawAuditMessage) {
155 if !execveRegex.Match(rawEvent.Data) {
156 return
157 }
158
159 match := execveRegex.FindSubmatch(rawEvent.Data)
160 eventId, err[0] = strconv.Atoi(string(match[1]))
161 argc, err[1] = strconv.Atoi(string(match[2]))
162 tmp, ok = eventTable.Load(eventId)
163 if !ok {
164 return
165 }
166 pEvent = tmp.(*Event)
167 if err[0] == nil && err[1] == nil && argsRegex.Match(rawEvent.Data) {
168 match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
169 for i := 0; i < argc; i++ {
170 if len(match[i][2]) == 0 {
171 // 代表着匹配到的是十六进制数
172 str := hexToAscii(string(match[i][3]))
173 pEvent.argv = append(pEvent.argv, str)
174 } else {
175 pEvent.argv = append(pEvent.argv, string(match[i][2]))
176 }
177 }
178 pEvent.argc = argc
179 }
180}
181
182func cwd(rawEvent libaudit.RawAuditMessage) {
183 if !cwdRegex.Match(rawEvent.Data) {
184 return
185 }
186
187 match := cwdRegex.FindSubmatch(rawEvent.Data)
188 eventId, err[0] = strconv.Atoi(string(match[1]))
189 tmp, ok = eventTable.Load(eventId)
190 if !ok {
191 return
192 }
193 tmp.(*Event).cwd = string(match[2])
194}
195
196func proctitle(rawEvent libaudit.RawAuditMessage) {
197 if !proctitleRegex.Match(rawEvent.Data) {
198 return
199 }
200
201 var cmdline string
202 match := proctitleRegex.FindSubmatch(rawEvent.Data)
203 eventId, err[0] = strconv.Atoi(string(match[1]))
204 tmp, ok = eventTable.Load(eventId)
205 if !ok {
206 return
207 }
208 pEvent = tmp.(*Event)
209 if pEvent.argc == 0 {
210 // 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
211 if match[3] == nil {
212 // PROCTITLE写的是十六进制,转换为字符串
213 cmdline = hexToAscii(string(match[4]))
214 } else {
215 cmdline = string(match[3])
216 }
217 pEvent.argv = strings.Split(cmdline, " ")
218 pEvent.argc = len(pEvent.argv)
219 }
220}
221
222func eoe(rawEvent libaudit.RawAuditMessage) {
223 if !eoeRegex.Match(rawEvent.Data) {
224 return
225 }
226
227 match := eoeRegex.FindSubmatch(rawEvent.Data)
228 eventId, err[0] = strconv.Atoi(string(match[1]))
229 tmp, ok = eventTable.Load(eventId)
230 if !ok {
231 return
232 }
233 cooked := *(tmp.(*Event))
234 cookedChan <- cooked
235 eventTable.Delete(eventId) // 死人别占地
236}
237
238func path(rawEvent libaudit.RawAuditMessage) {
239 if !pathRegex.Match(rawEvent.Data) {
240 return
241 }
242 match := pathRegex.FindSubmatch(rawEvent.Data)
243 eventId, err[0] = strconv.Atoi(string(match[1]))
244 name := string(match[2])
245
246 tmp, ok = eventTable.Load(eventId)
247 if !ok {
248 return
249 }
250 pEvent = tmp.(*Event)
251
252 // 先看看是不是文件操作
253 if pEvent.tag != FILEOPEN {
254 return
255 }
256
257 if pEvent.pathName == "" {
258 pEvent.pathName = name
259 } else {
260 pEvent.pathName += "/" + name
261 }
262}