package main
import (
"bufio"
"flag"
"fmt"
"log"
"netlink"
"os"
"os/exec"
"strings"
"syscall"
"time"
"github.com/elastic/go-libaudit/v2"
)
var (
fs = flag.NewFlagSet("audit", flag.ExitOnError)
diag = fs.String("diag", "", "dump raw information from kernel to file")
rate = fs.Uint("rate", 0, "rate limit in kernel (default 0, no rate limit)")
backlog = fs.Uint("backlog", 8192, "backlog limit")
immutable = fs.Bool("immutable", false, "make kernel audit settings immutable (requires reboot to undo)")
receiveOnly = fs.Bool("ro", false, "receive only using multicast, requires kernel 3.16+")
)
func main() {
// 检查用户身份,并添加auditd规则,监听所有syscall
if os.Geteuid() != 0 {
fmt.Printf("Err: Please run me as root, %d!\n", os.Getegid())
return
}
// 所有的系统调用号与名称的关系
err := figureOutSyscalls()
if err != nil {
fmt.Printf("Error figuring out syscall numbers: %v\n", err)
}
exec.Command("auditctl", "-D").Run()
exec.Command("auditctl", "-b", "1000000000").Run()
exec.Command("auditctl", "--reset-lost").Run()
var auditCmd *exec.Cmd
pidSyscall := []string{"execve"}
// 设置监听规则
for i := 0; i < len(pidSyscall); i++ {
auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i])
auditCmd.Run()
}
// 监听文件的消息
fileSyscall := []string{"open", "write", "close"}
// fileSyscall := []string{"open", "write", "creat", "unlink", "opendir", "mkdir", "rmdir", "chmod", "fchmod", "chown", "fchown", "lchown", "flock"}
for i := 0; i < len(fileSyscall); i++ {
auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", fileSyscall[i])
auditCmd.Run()
}
// 查找pid
containerdPid, err = getPid()
if err != nil {
fmt.Printf("Error finding containerd: %v\n", err)
return
}
// 创世之神,1号进程
// 1号进程还是不要在进程树上直接出现了,不然它的小儿子们都会出现
// /usr/bin/containerd,也就是我们最关注的进程
// 开始运行,解析命令行参数后监听
if err := fs.Parse(os.Args[1:]); err != nil {
log.Fatal(err)
}
if err := read(); err != nil {
log.Fatalf("error: %v", err)
}
}
func coroutine(client *libaudit.AuditClient) {
// 各协程至此开始
rawChan = make(chan interface{})
cookedChan = make(chan Event)
wg.Add(1)
go procWatch()
wg.Add(1)
go receive(client)
wg.Add(1)
go orgnaze()
wg.Add(1)
go deal()
wg.Wait()
time.Sleep(2 * time.Second)
}
func procWatch() error {
ns, err := netlink.NewNetlinkSocket(syscall.NETLINK_CONNECTOR, 12345)
if err != nil {
fmt.Printf("Error creating socket: %v\n", err)
return err
}
defer ns.Close()
for {
res, err := ns.Receive()
if err != nil {
fmt.Printf("Error recv: %v\n", err)
continue
}
for i := 0; i < len(res); i++ {
procEvent := netlink.ParseProcEvent(res[i].Data)
switch procEvent.What {
case netlink.PROC_EVENT_FORK:
data := procEvent.Data.(netlink.ProcEventFork)
cooked := Event{
tag: NEWPID,
timestamp: time.Now(),
pid: int(data.ChildPid),
tgid: int(data.ChildTgid),
ppid: int(data.ParentPid),
parentTgid: int(data.ParentTgid),
}
checkProc(&cooked)
cookedChan <- cooked
case netlink.PROC_EVENT_EXIT:
data := procEvent.Data.(netlink.ProcEventExit)
cooked := Event{
tag: PIDEXIT,
timestamp: time.Now(),
pid: int(data.ProcessPid),
exit_code: uint64(data.ExitCode),
exit_signal: int(data.ExitSignal),
}
cookedChan <- cooked
default:
}
}
}
}
func checkProc(pCooked *Event) {
fileName := fmt.Sprintf("/proc/%d/task/%d/cmdline", pCooked.tgid, pCooked.pid)
fd, err := os.Open(fileName)
if err != nil {
fmt.Printf("Err: %v\n", err)
return
}
scanner := bufio.NewScanner(fd)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
pCooked.argv = append(pCooked.argv, strings.Split(line, "\x00")...)
}
pCooked.argc = len(pCooked.argv)
fd.Close()
fileName = fmt.Sprintf("/proc/%d/task/%d/cwd", pCooked.tgid, pCooked.pid)
pCooked.cwd, err = os.Readlink(fileName)
if err != nil {
fmt.Printf("Err readlink %s: %v\n", fileName, err)
pCooked.cwd = ""
}
}
|