summaryrefslogtreecommitdiffstats
path: root/src/organize.go
blob: 293371bc1aaee94840eee0b48a15c15667fb4083 (plain) (blame)
package main

import (
	"fmt"
	"os"
	"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+).*?subj=(.*?):(.*?):(.*?):(.*?) .*?$`)
	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+)\): item=(\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:
		}
	}
}

func syscallRaw(rawEvent libaudit.RawAuditMessage) {
	if !syscallRegex.Match(rawEvent.Data) {
		return
	}

	var exit int
	var a [4]uint64
	var subj [4]string
	// 捕获基础信息
	match := syscallRegex.FindSubmatch(rawEvent.Data)
	event.timestamp, _ = getTimeFromStr(string(match[1]))
	eventId, _ = strconv.Atoi(string(match[2]))
	event.syscall, _ = strconv.Atoi(string(match[3]))
	if string(match[4]) == "" {
		// exit没捕获到
		exit = 0
	} else {
		exit, _ = strconv.Atoi(string(match[4]))
	}
	event.ppid, _ = strconv.Atoi(string(match[5]))
	event.pid, _ = strconv.Atoi(string(match[6]))

	// 几个subj,说不定会有用
	for i := 0; i < 4; i++ {
		subj[i] = string(match[7+i])
	}

	// 捕获参数
	if !argsRegex.Match(rawEvent.Data) {
		fmt.Fprintf(os.Stderr, "Error: don't get args in syscall event!\n")
		return
	}
	argsMatch := argsRegex.FindAllSubmatch(rawEvent.Data, -1)
	for i := 0; i < 4; i++ {
		a[i], _ = 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,
			srcPath:      "",
		})
	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,
		})
	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,
		})
	case "pivot_root":
		if subj[2] == "container_runtime_t" {
			eventTable.Store(eventId, &Event{
				tag:          PIVOTROOT,
				timestamp:    event.timestamp,
				syscall:      event.syscall,
				ppid:         event.ppid,
				pid:          event.pid,
				syscallParam: a,
			})
		}
	}
}

func execve(rawEvent libaudit.RawAuditMessage) {
	if !execveRegex.Match(rawEvent.Data) {
		return
	}

	match := execveRegex.FindSubmatch(rawEvent.Data)
	eventId, _ = strconv.Atoi(string(match[1]))
	argc, _ = strconv.Atoi(string(match[2]))
	tmp, ok = eventTable.Load(eventId)
	if !ok {
		return
	}
	pEvent = tmp.(*Event)
	if 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, _ = 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, _ = 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, _ = strconv.Atoi(string(match[1]))
	tmp, ok = eventTable.Load(eventId)
	if !ok {
		return
	}
	cooked := *(tmp.(*Event))
	cookedChan <- cooked
	eventTable.Delete(eventId) // 死人别占地
}

func path(rawEvent libaudit.RawAuditMessage) {
	if !pathRegex.Match(rawEvent.Data) {
		return
	}
	match := pathRegex.FindSubmatch(rawEvent.Data)
	eventId, _ = strconv.Atoi(string(match[1]))
	// item, _ := strconv.Atoi(string(match[2]))
	name := string(match[3])

	tmp, ok = eventTable.Load(eventId)
	if !ok {
		return
	}
	pEvent = tmp.(*Event)

	// 先看看是不是文件操作
	if pEvent.tag != FILEOPEN {
		return
	}

	if name[0] == '/' {
		pEvent.srcPath = name
	} else {
		pEvent.srcPath += "/" + name
	}
}