package main
import (
"fmt"
"regexp"
"strconv"
"strings"
"sync"
"syscall"
"github.com/elastic/go-libaudit/v2"
"github.com/elastic/go-libaudit/v2/auparse"
)
// 为每个事务id存储其信息,事务id在操作系统运行期间是唯一的
var eventTable sync.Map
// 事件信息
var tmp any
var ok bool
var event Event
var pEvent *Event
var eventId, argc int
var errs [6]error
// 要用的正则匹配列表
var (
syscallRegex = regexp.MustCompile(`audit\((\d+\.\d+):(\d+)\).*?syscall=(\d+)(?:.*?exit=([-+]?\d+))?.*?ppid=(\d+) pid=(\d+).*?$`)
execveRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): argc=(\d+)`)
argsRegex = regexp.MustCompile(`a\d+=("(.*?)"|([0-9a-fA-F]+))`)
pathRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\):.*?name="(.*?)"`)
cwdRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): cwd="(.*?)"`)
proctitleRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\): proctitle=("(.*?)"|([0-9a-fA-F]+))$`)
eoeRegex = regexp.MustCompile(`audit\(\d+\.\d+:(\d+)\)`)
)
func orgnaze() {
defer wg.Done()
defer close(cookedChan)
// 接收信息
var raw interface{}
var rawEvent libaudit.RawAuditMessage
for {
raw, ok = <-rawChan
if !ok {
break
}
rawEvent = raw.(libaudit.RawAuditMessage)
// fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data)
switch rawEvent.Type {
case auparse.AUDIT_SYSCALL:
syscallRaw(rawEvent)
case auparse.AUDIT_EXECVE:
execve(rawEvent)
case auparse.AUDIT_CWD:
cwd(rawEvent)
case auparse.AUDIT_PATH:
path(rawEvent)
case auparse.AUDIT_PROCTITLE:
proctitle(rawEvent)
case auparse.AUDIT_EOE:
eoe(rawEvent)
default:
// ATTENTION: 这里也需要做防护
}
}
}
func syscallRaw(rawEvent libaudit.RawAuditMessage) {
if !syscallRegex.Match(rawEvent.Data) {
return
}
var exit int
var a [4]uint64
// 捕获基础信息
match := syscallRegex.FindSubmatch(rawEvent.Data)
event.timestamp, errs[0] = getTimeFromStr(string(match[1]))
eventId, errs[1] = strconv.Atoi(string(match[2]))
event.syscall, errs[2] = strconv.Atoi(string(match[3]))
if string(match[4]) == "" {
// exit没捕获到
exit = 0
} else {
exit, errs[3] = strconv.Atoi(string(match[4]))
}
event.ppid, errs[4] = strconv.Atoi(string(match[5]))
event.pid, errs[5] = strconv.Atoi(string(match[6]))
// 捕获参数
if !argsRegex.Match(rawEvent.Data) {
fmt.Printf("Error: don't get args in syscall event!\n")
return
}
argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
for i := 0; i < 4; i++ {
a[i], errs[0] = strconv.ParseUint(string(argsMatch[i][3]), 16, 64)
}
switch syscallTable[event.syscall] {
case "execve":
eventTable.Store(eventId, &Event{
tag: EXECVE,
timestamp: event.timestamp,
syscall: event.syscall,
exit_code: a[0],
ppid: event.ppid,
pid: event.pid,
argc: 0,
argv: make([]string, 0),
cwd: "",
})
case "open":
// 检查打开的权限
if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 {
break
}
// TRUNC应该被直接标记为改变,而不是打开
eventTable.Store(eventId, &Event{
tag: FILEOPEN,
timestamp: event.timestamp,
syscall: event.syscall,
exit_code: uint64(exit),
ppid: event.ppid,
pid: event.pid,
argc: 0,
argv: make([]string, 0),
cwd: "",
syscallParam: a,
pathName: "",
})
case "write":
eventTable.Store(eventId, &Event{
tag: FILEWRITE,
timestamp: event.timestamp,
syscall: event.syscall,
exit_code: uint64(exit),
ppid: event.ppid,
pid: event.pid,
argc: 0,
argv: make([]string, 0),
cwd: "",
syscallParam: a,
// pathName: "",
})
case "close":
// 文件关闭
eventTable.Store(eventId, &Event{
tag: FILECLOSE,
timestamp: event.timestamp,
syscall: event.syscall,
exit_code: uint64(exit),
ppid: event.ppid,
pid: event.pid,
argc: 0,
argv: make([]string, 0),
cwd: "",
syscallParam: a,
// pathName: "",
})
}
}
func execve(rawEvent libaudit.RawAuditMessage) {
if !execveRegex.Match(rawEvent.Data) {
return
}
match := execveRegex.FindSubmatch(rawEvent.Data)
eventId, errs[0] = strconv.Atoi(string(match[1]))
argc, errs[1] = strconv.Atoi(string(match[2]))
tmp, ok = eventTable.Load(eventId)
if !ok {
return
}
pEvent = tmp.(*Event)
if errs[0] == nil && errs[1] == nil && argsRegex.Match(rawEvent.Data) {
match := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
for i := 0; i < argc; i++ {
if len(match[i][2]) == 0 {
// 代表着匹配到的是十六进制数
str := hexToAscii(string(match[i][3]))
pEvent.argv = append(pEvent.argv, str)
} else {
pEvent.argv = append(pEvent.argv, string(match[i][2]))
}
}
pEvent.argc = argc
}
}
func cwd(rawEvent libaudit.RawAuditMessage) {
if !cwdRegex.Match(rawEvent.Data) {
return
}
match := cwdRegex.FindSubmatch(rawEvent.Data)
eventId, errs[0] = strconv.Atoi(string(match[1]))
tmp, ok = eventTable.Load(eventId)
if !ok {
return
}
tmp.(*Event).cwd = string(match[2])
}
func proctitle(rawEvent libaudit.RawAuditMessage) {
if !proctitleRegex.Match(rawEvent.Data) {
return
}
var cmdline string
match := proctitleRegex.FindSubmatch(rawEvent.Data)
eventId, errs[0] = strconv.Atoi(string(match[1]))
tmp, ok = eventTable.Load(eventId)
if !ok {
return
}
pEvent = tmp.(*Event)
if pEvent.argc == 0 {
// 只有等于0,才证明没经过EXECVE提取参数,才允许使用PROCTITLE提取参数
if match[3] == nil {
// PROCTITLE写的是十六进制,转换为字符串
cmdline = hexToAscii(string(match[4]))
} else {
cmdline = string(match[3])
}
pEvent.argv = strings.Split(cmdline, " ")
pEvent.argc = len(pEvent.argv)
}
}
func eoe(rawEvent libaudit.RawAuditMessage) {
if !eoeRegex.Match(rawEvent.Data) {
return
}
match := eoeRegex.FindSubmatch(rawEvent.Data)
eventId, errs[0] = strconv.Atoi(string(match[1]))
tmp, ok = eventTable.Load(eventId)
if !ok {
return
}
cooked := *(tmp.(*Event))
cookedChan <- cooked
// fmt.Printf("Send: %10d\t%v\t%7d\t%7d\n", eventId, cooked.tag, cooked.ppid, cooked.pid)
eventTable.Delete(eventId) // 死人别占地
}
func path(rawEvent libaudit.RawAuditMessage) {
if !pathRegex.Match(rawEvent.Data) {
return
}
match := pathRegex.FindSubmatch(rawEvent.Data)
eventId, errs[0] = strconv.Atoi(string(match[1]))
name := string(match[2])
tmp, ok = eventTable.Load(eventId)
if !ok {
return
}
pEvent = tmp.(*Event)
// 先看看是不是文件操作
if pEvent.tag != FILEOPEN {
return
}
if name[0] == '/' {
pEvent.pathName = name
} else {
pEvent.pathName += "/" + name
}
}
|