ebpf入门---监听所有新进程

什么是ebpf

eBPF 全称 extended Berkeley Packet Filter,中文意思是 扩展的伯克利包过滤器。一般来说,要向内核添加新功能,需要修改内核源代码或者编写 内核模块 来实现。而 eBPF 允许程序在不修改内核源代码,或添加额外的内核模块情况下运行

用途

ebpf由于直接与内核交互,所以更为底层,可以用来绕过一些安全组件的监控功能。同时可以做到如你看不见的后门(通过hook read的syscall,篡改返回结果)等等更有意思的场景。个人看来,ebpf在未来会得到更广泛的应用,同时也是以后rootkit的首选

简单使用

今天这个demo是个入门,原理就是hook syscall,判断是否是execve,然后获取对应的pid和执行文件,然后返回并输出

python

整体代码还是很简单的,注释里面也比较详细了

from bcc import BPF

bpf_text = """
#include <linux/binfmts.h>
struct data_t {
    int pid;
    char comm[TASK_COMM_LEN];
    char filename[256]; 
};

BPF_PERF_OUTPUT(myevents);//注册events

TRACEPOINT_PROBE(raw_syscalls, sys_enter) {
    if (args->id != __NR_execve)
        return 0;
    struct data_t data = {};
    data.pid = bpf_get_current_pid_tgid() >> 32;//获取pid
    bpf_get_current_comm(&data.comm, sizeof(data.comm));//获取当前进程的命令名称
    bpf_probe_read_user(&data.filename, sizeof(data.filename), (void *)args->args[0]);//用户空间读取execve的第一个参数,通常是要执行的文件路径
    myevents.perf_submit(args, &data, sizeof(data));// 将data结构体的内容发送到用户空间的events性能事件输出
    return 0;
}
"""

b = BPF(text=bpf_text)#创建了一个BPF对象b,它用于编译和加载前文中定义的eBPF程序


def print_event(cpu, data, size):#它是一个回调函数,用于处理从eBPF程序发送到用户空间的事件。函数的参数包括:cpu:捕获事件的CPU编号。,data:一个包含eBPF事件数据的字节流。,size:数据的大小。
    event = b["myevents"].event(data)#将data字节流转换成一个Python对象,这个对象包含了与eBPF程序中定义的数据结构匹配的字段
    print("New process created, PID: %d, Command: %s, Path: %s" % (event.pid, event.comm.decode('utf-8', 'replace'), event.filename.decode('utf-8', 'replace')))


b["myevents"].open_perf_buffer(print_event)#这行代码将print_event回调函数附加到名为myevents的eBPF性能事件输出上。当myevents输出中有新的事件时,print_event函数将被调用。

print("Listening for sys_execve calls... Press Ctrl+C to exit.")
try:
    while True:
        b.perf_buffer_poll()#用于轮询性能事件缓冲区,检查是否有任何新的事件到达。如果有,它将调用open_perf_buffer登记的回调函数来处理事件。
except KeyboardInterrupt:
    pass

运行效果
在这里插入图片描述

go

go代码更为复杂一点,但大体逻辑上类似

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "os"
    "os/signal"

    "github.com/iovisor/gobpf/bcc"
)

const bpfProgram = `
#include <linux/sched.h>

struct data_t {
    u32 pid;
    char comm[TASK_COMM_LEN];
    char filename[256];
};

BPF_PERF_OUTPUT(myevents);

TRACEPOINT_PROBE(raw_syscalls, sys_enter) {


    if (args->id != __NR_execve) return 0;
    struct data_t data = {};
    data.pid = bpf_get_current_pid_tgid() >> 32;
    bpf_get_current_comm(&data.comm, sizeof(data.comm));
    bpf_probe_read_user(&data.filename, sizeof(data.filename), (void *)args->args[0]);


    myevents.perf_submit(args, &data, sizeof(data));

    return 0;
}

`

type execveEvent struct {
    Pid      uint32
    Comm     [16]byte
    Filename [256]byte
}

func main() {

    //创建了一个新的 eBPF 模块,并加载了 eBPF 程序。defer module.Close() 确保在程序结束时清理和关闭 eBPF 模块
    module := bcc.NewModule(bpfProgram, []string{})
    defer module.Close()
    //加载 eBPF 程序中定义的跟踪点
        tracepoint, err := module.LoadTracepoint("tracepoint__raw_syscalls__sys_enter")
    if err != nil {
    fmt.Fprintf(os.Stderr, "Failed to load tracepoint: %s\n", err)
    os.Exit(1)
    }

    //将跟踪点附加到系统调用的入口点
    err = module.AttachTracepoint("raw_syscalls:sys_enter", tracepoint)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Failed to attach tracepoint: %s\n", err)
    os.Exit(1)
    }

    //创建一个新的性能事件表来接收 eBPF 程序发送的事件,并创建一个 Go 通道来接收这些事件
    table := bcc.NewTable(module.TableId("myevents"), module)
    channel := make(chan []byte)
    perfMap, err := bcc.InitPerfMap(table, channel, nil)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err)
    os.Exit(1)
    }

    go func() {
    for {
    data := <-channel
    var event execveEvent
    err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Failed to decode received data: %s\n", err)
    continue
    }
    filename := string(bytes.Split(event.Filename[:], []byte("\x00"))[0])
    fmt.Printf("execve called by PID %d (%s): %s\n", event.Pid, event.Comm, filename)
    }
    }()

    //启动性能事件表来接收事件,并确保在程序结束时停止它
    perfMap.Start()
    defer perfMap.Stop()

    fmt.Println("Waiting for execve events... Press Ctrl-C to exit.")
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, os.Interrupt)
    <-signals
}

效果
在这里插入图片描述

但是这两个都有一个共性的问题,python虽然可以是pyinstaller去编译成可执行文件,但和go编译的结果都是动态链接的,而往往目标机器上可能没有对应的库,所以我们要转化为静态链接,目前这两个demo我尝试了一下是无法变成静态链接的,如果有思路也可以进一步交流

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值