这里给你提供10个使用eBPF和uprobes的Golang代码案例:
- 监控函数调用次数:
package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
BPF_HASH(counts, u64);
int count_calls(struct pt_regs *ctx) {
u64 key = PT_REGS_IP(ctx);
u64 *val = counts.lookup(&key);
if (val) {
(*val)++;
} else {
counts.update(&key, &(u64){1});
}
return 0;
}
`
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
uprobeFD, err := m.LoadUprobe("count_calls")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load uprobe: %v\n", err)
os.Exit(1)
}
err = m.AttachUprobe("/path/to/binary", "function_name", uprobeFD)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach uprobe: %v\n", err)
os.Exit(1)
}
table := bpf.NewTable(m.TableId("counts"), m)
it := table.Iter()
for it.Next() {
key := binary.LittleEndian.Uint64(it.Key())
val := binary.LittleEndian.Uint64(it.Leaf())
fmt.Printf("Function at address 0x%x was called %d times\n", key, val)
}
}
- 统计函数执行时间:
package main
import (
"fmt"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
BPF_HASH(start, u32);
BPF_HISTOGRAM(dist);
int trace_entry(struct pt_regs *ctx) {
u32 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
start.update(&pid, &ts);
return 0;
}
int trace_return(struct pt_regs *ctx) {
u32 pid = bpf_get_current_pid_tgid();
u64 *tsp, delta;
tsp = start.lookup(&pid);
if (tsp != 0) {
delta = bpf_ktime_get_ns() - *tsp;
dist.increment(bpf_log2l(delta));
start.delete(&pid);
}
return 0;
}
`
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
entryFD, err := m.LoadUprobe("trace_entry")
if err != nil {
fmt.Println(err)
return
}
returnFD, err := m.LoadUprobe("trace_return")
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUprobe("/path/to/binary", "function_name", entryFD)
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUretprobe("/path/to/binary", "function_name", returnFD)
if err != nil {
fmt.Println(err)
return
}
table := bpf.NewTable(m.TableId("dist"), m)
buckets := table.HistogramRead()
for i, count := range buckets {
if count == 0 {
continue
}
lower := 1 << uint32(i)
upper := lower << 1
fmt.Printf("[%d-%d] ns: %d\n", lower, upper, count)
}
}
- 监控文件IO调用:
package main
import (
"fmt"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
struct data_t {
u64 ts;
char name[TASK_COMM_LEN];
};
BPF_HASH(start, u32);
BPF_PERF_OUTPUT(events);
int trace_entry(struct pt_regs *ctx) {
struct data_t data = {};
u32 pid = bpf_get_current_pid_tgid();
if (bpf_get_current_comm(&data.name, sizeof(data.name)) == 0) {
data.ts = bpf_ktime_get_ns();
start.update(&pid, &data);
}
return 0;
}
int trace_return(struct pt_regs *ctx) {
struct data_t *datap;
u32 pid = bpf_get_current_pid_tgid();
datap = start.lookup(&pid);
if (datap != 0) {
u64 delta;
delta = bpf_ktime_get_ns() - datap->ts;
events.perf_submit(ctx, datap, sizeof(*datap));
start.delete(&pid);
}
return 0;
}
`
type eventData struct {
Timestamp uint64
Name [16]byte
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
entryFD, err := m.LoadUprobe("trace_entry")
if err != nil {
fmt.Println(err)
return
}
returnFD, err := m.LoadUprobe("trace_return")
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUprobe("/path/to/binary", "function_name", entryFD)
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUretprobe("/path/to/binary", "function_name", returnFD)
if err != nil {
fmt.Println(err)
return
}
table := bpf.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bpf.InitPerfMap(table, channel)
if err != nil {
fmt.Println(err)
return
}
go func() {
for data := range channel {
var event eventData
err = binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event)
if err != nil {
fmt.Println(err)
continue
}
fmt.Printf("Process: %s, Timestamp: %d\n", string(event.Name[:]), event.Timestamp)
}
}()
select {}
}
- 监控系统调用:
package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
BPF_HASH(syscall_counts, u32);
int count_syscalls(struct pt_regs *ctx) {
u32 pid = bpf_get_current_pid_tgid();
u64 *count = syscall_counts.lookup(&pid);
if (count) {
(*count)++;
} else {
syscall_counts.update(&pid, &(u64){1});
}
return 0;
}
`
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
syscallFD, err := m.LoadUprobe("count_syscalls")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load uprobe: %v\n", err)
os.Exit(1)
}
err = m.AttachUprobe("", "__x64_sys_read", syscallFD)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach uprobe: %v\n", err)
os.Exit(1)
}
table := bpf.NewTable(m.TableId("syscall_counts"), m)
it := table.Iter()
for it.Next() {
pid := binary.LittleEndian.Uint32(it.Key())
count := binary.LittleEndian.Uint64(it.Leaf())
fmt.Printf("Process with PID %d made %d syscalls\n", pid, count)
}
}
- 拦截网络数据包:
package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <bcc/proto.h>
BPF_PERF_OUTPUT(events);
int trace_packet(struct __sk_buff *skb) {
u8 *cursor = 0;
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
if (ethernet->type == ETH_P_IP) {
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
if (ip->protocol == IPPROTO_TCP) {
struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp));
if (tcp->dest == bpf_htons(80)) { // HTTP traffic
events.perf_submit(skb, skb->len);
}
}
}
return 0;
}
`
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
uprobeFD, err := m.LoadUprobe("trace_packet")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load uprobe: %v\n", err)
os.Exit(1)
}
err = m.AttachUprobe("tcp_sendmsg", uprobeFD)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach uprobe: %v\n", err)
os.Exit(1)
}
perfMap, err := bpf.InitPerfMap(m.TableId("events"), printPacketSize)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize perf map: %v\n", err)
os.Exit(1)
}
perfMap.Start()
select {}
}
func printPacketSize(data []byte) {
fmt.Printf("Received packet of size %d bytes\n", len(data))
}
- 拦截文件读写操作:
package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct key_t {
int id;
u64 inode;
char name[TASK_COMM_LEN];
};
BPF_HASH(open_files, struct key_t);
int trace_open(struct pt_regs *ctx) {
struct key_t key = {};
u64 id = bpf_get_current_pid_tgid();
key.id = id >> 32;
key.inode = PT_REGS_RC(ctx);
bpf_get_current_comm(&key.name, sizeof(key.name));
open_files.update(&key, &id);
return 0;
}
int trace_close(struct pt_regs *ctx) {
struct key_t key = {};
u64 id = bpf_get_current_pid_tgid();
key.id = id >> 32;
key.inode = PT_REGS_RC(ctx);
bpf_get_current_comm(&key.name, sizeof(key.name));
open_files.delete(&key);
return 0;
}
`
type fileKey struct {
ID int32
Inode uint64
Name [16]byte
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
openFD, err := m.LoadUprobe("trace_open")
if err != nil {
fmt.Println(err)
return
}
closeFD, err := m.LoadUprobe("trace_close")
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUprobe("/path/to/binary", "open", openFD)
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUretprobe("/path/to/binary", "close", closeFD)
if err != nil {
fmt.Println(err)
return
}
table := bpf.NewTable(m.TableId("open_files"), m)
channel := make(chan []byte)
perfMap, err := bpf.InitPerfMap(table, channel)
if err != nil {
fmt.Println(err)
return
}
go func() {
for data := range channel {
var key fileKey
err = binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &key)
if err != nil {
fmt.Println(err)
continue
}
fmt.Printf("Process: %s opened file with inode %d\n", string(key.Name[:]), key.Inode)
}
}()
select {}
}
- 监控TCP连接:
package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct key_t {
u64 ip;
u64 pid;
};
BPF_HASH(tcp_connections, struct key_t);
int trace_connect(struct pt_regs *ctx) {
struct key_t key = {};
u64 ip = PT_REGS_RC(ctx);
u64 pid = bpf_get_current_pid_tgid();
key.ip = ip;
key.pid = pid;
tcp_connections.update(&key, &ip);
return 0;
}
int trace_accept(struct pt_regs *ctx) {
struct key_t key = {};
u64 ip = PT_REGS_RC(ctx);
u64 pid = bpf_get_current_pid_tgid();
key.ip = ip;
key.pid = pid;
tcp_connections.update(&key, &ip);
return 0;
}
`
type connectionKey struct {
IP uint64
PID uint64
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
connectFD, err := m.LoadUprobe("trace_connect")
if err != nil {
fmt.Println(err)
return
}
acceptFD, err := m.LoadUprobe("trace_accept")
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUprobe("/path/to/binary", "connect", connectFD)
if err != nil {
fmt.Println(err)
return
}
err = m.AttachUprobe("/path/to/binary", "accept", acceptFD)
if err != nil {
fmt.Println(err)
return
}
table := bpf.NewTable(m.TableId("tcp_connections"), m)
it := table.Iter()
for it.Next() {
var key connectionKey
var ip uint64
if binary.Read(bytes.NewBuffer(it.Key()), binary.LittleEndian, &key) == nil &&
binary.Read(bytes.NewBuffer(it.Leaf()), binary.LittleEndian, &ip) == nil {
fmt.Printf("Process with PID %d connected to IP %d\n", key.PID, ip)
}
}
}
- 监控内存分配:
”`go package main
import (
"fmt"
"os"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include
BPF_HASH(allocations, u64);
int trace
内容由零声教学AI助手提供,问题来源于学员提问