ebpf.party Solutions
This is my solutions for exercises in ebpf.party.
Source code for the website is here https://github.com/DavidVentura/ebpf.party.
This is my solutions for exercises in ebpf.party.
Source code for the website is here https://github.com/DavidVentura/ebpf.party.
| #include "ep_platform.h" | |
| #include "sched.h" | |
| SEC("tp/sched/sched_process_exec") | |
| int handle_exec(struct trace_event_raw_sched_process_exec *ctx) { | |
| // 1. Try running the example as-is to see the output | |
| // 2. Declare a buffer for the process name | |
| // 3. Copy the process name into the buffer with a helper | |
| // 4. Call DEBUG_STR on your buffer to see the output | |
| // DEBUG_STR("Example", "Hi"); | |
| char buffer[16]; | |
| bpf_get_current_comm(&buffer, sizeof(buffer)); | |
| DEBUG_STR("Command", buffer); | |
| if (bpf_strncmp(buffer, sizeof(buffer), "ls") == 0) return 0; | |
| if (bpf_strncmp(buffer, sizeof(buffer), "sudo") == 0) return 0; | |
| SUBMIT_STR(buffer); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "sched.h" | |
| SEC("tp/sched/sched_process_exec") | |
| int handle_exec(struct trace_event_raw_sched_process_exec *ctx) { | |
| // 1. Get and print the PID | |
| // 2. Calculate the offset | |
| // 3. Create the `fname` buffer | |
| // 4. Populate `fname` with a helper | |
| // DEBUG_STR("Hi", "Welcome back"); | |
| int pid = ctx->pid; | |
| DEBUG_NUM("PID", pid); | |
| int off = ctx->__data_loc_filename & 0xFFFF; | |
| int len = ctx->__data_loc_filename >> 16; | |
| char fname[32]; | |
| bpf_probe_read_kernel_str(fname, sizeof(fname), (void *)ctx + off); | |
| DEBUG_STR_LEN("fname", fname, len); | |
| if (bpf_strncmp(fname, sizeof(fname), "/bin/ls") == 0) return 0; | |
| if (bpf_strncmp(fname, sizeof(fname), "/bin/sudo") == 0) return 0; | |
| SUBMIT_STR_LEN(fname, len); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| SEC("tp/syscalls/sys_enter_execve") | |
| int trace_execve(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| char filename[32]; | |
| // ctx->args[0] is the first syscall argument (filename pointer) | |
| // const char* ptr = ...; | |
| // copy it to the buffer | |
| // bpf_probe_read_user_str(dst, size, src); | |
| const char* ptr = (const char*) ctx->args[0]; | |
| int len = bpf_probe_read_user_str(filename, sizeof(filename), ptr); | |
| DEBUG_STR("filename", filename); | |
| DEBUG_NUM("len", len); | |
| if (bpf_strncmp(filename, sizeof(filename), "/bin/ls") == 0) return 0; | |
| if (bpf_strncmp(filename, sizeof(filename), "/bin/sudo") == 0) return 0; | |
| SUBMIT_STR_LEN(filename, len); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| SEC("tp/syscalls/sys_enter_execve") | |
| int trace_execve(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| // Get the argv pointer | |
| // While we haven't made it to the terminator (or 10 args): | |
| // Get the pointer to the argument | |
| // If it's NULL, stop | |
| // Read the pointed value | |
| // Get the argv pointer | |
| char **argv = (char **)ctx->args[1]; | |
| // In a loop, read the next pointer from the array | |
| int i; | |
| for (i=0; i<=10; i++) | |
| { | |
| char *arg_ptr; | |
| bpf_probe_read_user(&arg_ptr, sizeof(arg_ptr), &argv[i]); | |
| // Remember to check for the NULL terminator! | |
| if (!arg_ptr) | |
| return 0; | |
| // Read the string that pointer points to | |
| char arg_buf[64]; | |
| bpf_probe_read_user_str(arg_buf, sizeof(arg_buf), arg_ptr); | |
| DEBUG_STR("buf", arg_buf); | |
| if (bpf_strncmp(arg_buf, sizeof(arg_buf), "--password") == 0) | |
| { | |
| char *arg_ptr_pass; | |
| bpf_probe_read_user(&arg_ptr_pass, sizeof(arg_ptr_pass), &argv[i+1]); | |
| char arg_buf_pass[64]; | |
| int len = bpf_probe_read_user_str(arg_buf_pass, sizeof(arg_buf_pass), arg_ptr_pass); | |
| DEBUG_STR("password", arg_buf_pass); | |
| SUBMIT_STR_LEN(arg_buf_pass, len); | |
| return 0; | |
| } | |
| } | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "sched.h" | |
| #include "syscalls.h" | |
| // Map to store process names, keyed by PID | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, u64); // What you use to look up | |
| __type(value, char[16]); // What you store | |
| } process_names SEC(".maps"); | |
| // When a process starts: store its name | |
| SEC("tracepoint/sched/sched_process_exec") | |
| int on_process_start(struct trace_event_raw_sched_process_exec *ctx) { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| char comm[16]; | |
| bpf_get_current_comm(comm, sizeof(comm)); | |
| bpf_map_update_elem(&process_names, &pid, comm, BPF_ANY); | |
| return 0; | |
| } | |
| // When a process exits: check if we had stored it in the map | |
| SEC("tracepoint/syscalls/sys_enter_exit") | |
| int on_process_exit(struct trace_event_raw_sys_enter *ctx) { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| // Look up the name we stored | |
| char *comm = bpf_map_lookup_elem(&process_names, &pid); | |
| if (!comm) return 0; | |
| // Debug: see all processes and their exit codes | |
| int exit_code = ctx->args[0]; // exit's first argument | |
| DEBUG_NUM("Exit code", exit_code); | |
| DEBUG_STR_LEN("Process", comm, 16); | |
| // TODO: If process name is "exit_with_code", submit the exit code | |
| if (bpf_strncmp(comm, sizeof(comm), "exit_with_code") == 0) | |
| { | |
| DEBUG_NUM("Submitted exit code", exit_code); | |
| SUBMIT_NUM(exit_code); | |
| } | |
| // Clean up | |
| bpf_map_delete_elem(&process_names, &pid); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, u64); // PID | |
| __type(value, u64); // Buffer address | |
| } read_buffers SEC(".maps"); | |
| SEC("tracepoint/syscalls/sys_enter_read") | |
| int trace_read_entry(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| u64 buf_addr = ctx->args[1]; | |
| bpf_map_update_elem(&read_buffers, &pid, &buf_addr, BPF_ANY); | |
| return 0; | |
| } | |
| SEC("tracepoint/syscalls/sys_exit_read") | |
| int trace_read_exit(struct trace_event_raw_sys_exit *ctx) | |
| { | |
| // Fill this in | |
| size_t count = ctx->ret; | |
| if (count <= 0) return 0; | |
| DEBUG_NUM("count", count); | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| u64 *buf_ptr = bpf_map_lookup_elem(&read_buffers, &pid); | |
| if (!buf_ptr) return 0; | |
| char local_buf[46]; | |
| if (count > 46) count = 46; | |
| bpf_probe_read_user(local_buf, count, (void *)*buf_ptr); | |
| DEBUG_STR("buf", local_buf); | |
| SUBMIT_STR_LEN(local_buf, count); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| // Compound key for tracking (pid, fd) pairs | |
| struct pid_fd_key { | |
| u64 pid; | |
| u32 fd; | |
| }; | |
| // Current open in progress (PID → marker) | |
| // Lifetime: open entry → exit only | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, u64); | |
| __type(value, u8); | |
| } open_curr_fd_interesting SEC(".maps"); | |
| // Tracked interesting FDs ((pid, fd) → marker) | |
| // Lifetime: open exit → until we're done tracking | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, struct pid_fd_key); | |
| __type(value, u8); | |
| } open_interesting_fds SEC(".maps"); | |
| // Current read buffer (PID → buffer pointer) | |
| // Lifetime: read entry → exit only | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, u64); | |
| __type(value, u64); | |
| } read_curr_fd_buf SEC(".maps"); | |
| SEC("tracepoint/syscalls/sys_enter_open") | |
| int trace_open_entry(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| u8 mark = 1; | |
| // Get pathname pointer from ctx->args[0] | |
| // Read pathname string into local buffer with `bpf_probe_read_user_str` | |
| // Check if pathname == "/tmp/password" with bpf_strncmp | |
| // If yes, mark PID in open_curr_fd_interesting with `bpf_map_update_elem` | |
| char pathname[20]; | |
| const char* ptr = (const char*) ctx->args[0]; | |
| bpf_probe_read_user_str(pathname, sizeof(pathname), ptr); | |
| if (bpf_strncmp(pathname, sizeof(pathname), "/tmp/password") == 0) | |
| { | |
| DEBUG_STR("pathname", pathname); | |
| bpf_map_update_elem(&open_curr_fd_interesting, &pid, &mark, BPF_ANY); | |
| } | |
| return 0; | |
| } | |
| SEC("tracepoint/syscalls/sys_exit_open") | |
| int trace_open_exit(struct trace_event_raw_sys_exit *ctx) | |
| { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| // Check ctx->ret >= 0 (success) | |
| // Check if PID exists in open_curr_fd_interesting with `bpf_map_lookup_elem` | |
| // If yes: | |
| // - Get fd from ctx->ret | |
| // - Delete PID from open_curr_fd_interesting with `bpf_map_delete_elem` | |
| // - Store (pid, fd) in open_interesting_fds | |
| int fd = ctx->ret; | |
| if (fd < 0) return 0; | |
| u64 *buf_ptr = bpf_map_lookup_elem(&open_curr_fd_interesting, &pid); | |
| if (!buf_ptr) return 0; | |
| DEBUG_NUM("fd", fd); | |
| bpf_map_delete_elem(&open_curr_fd_interesting, &pid); | |
| struct pid_fd_key s = {pid: pid, fd: fd}; | |
| bpf_map_update_elem(&open_interesting_fds, &s, &fd, BPF_ANY); | |
| return 0; | |
| } | |
| SEC("tracepoint/syscalls/sys_enter_read") | |
| int trace_read_entry(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| u64 fd = ctx->args[0]; | |
| u64 buf_addr = ctx->args[1]; | |
| // Check if (pid, fd) exists in open_interesting_fds | |
| // If yes, store buffer address, keyed by pid, in read_curr_fd_buf | |
| struct pid_fd_key s = {pid: pid, fd: fd}; | |
| u64 *buf_ptr = bpf_map_lookup_elem(&open_interesting_fds, &s); | |
| if (!buf_ptr) return 0; | |
| bpf_map_update_elem(&read_curr_fd_buf, &pid, &buf_addr, BPF_ANY); | |
| return 0; | |
| } | |
| SEC("tracepoint/syscalls/sys_exit_read") | |
| int trace_read_exit(struct trace_event_raw_sys_exit *ctx) | |
| { | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| // Check ctx->ret > 0 | |
| // Lookup buffer pointer from read_curr_fd_buf | |
| // If found: | |
| // - Create local buffer | |
| // - Read from user space with bpf_probe_read_user | |
| // - Submit with SUBMIT_STR_LEN | |
| // - Cleanup: delete from read_curr_fd_buf | |
| size_t count = ctx->ret; | |
| if (count <= 0) return 0; | |
| u64 *buf_ptr = bpf_map_lookup_elem(&read_curr_fd_buf, &pid); | |
| if (!buf_ptr) return 0; | |
| char local_buf[13]; | |
| if (count > 13) count = 13; | |
| bpf_probe_read_user(local_buf, count, (void *)*buf_ptr); | |
| DEBUG_STR("buf", local_buf); | |
| SUBMIT_STR_LEN(local_buf, count); | |
| bpf_map_delete_elem(&read_curr_fd_buf, &pid); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| #include "ep_sock.h" | |
| #include <bpf/bpf_endian.h> | |
| // Map 1: Track port during connect (PID → port) | |
| // Lifetime: connect entry → exit only | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 1024); | |
| __type(key, u64); // pid | |
| __type(value, u16); // port | |
| } connect_curr_port SEC(".maps"); | |
| SEC("tracepoint/syscalls/sys_enter_connect") | |
| int trace_connect_entry(struct trace_event_raw_sys_enter *ctx) | |
| { | |
| struct sockaddr_in addr; | |
| // Read sockaddr from ctx->args[1] with bpf_probe_read_user() | |
| // Check if addr.sin_family == 2 (IPv4) | |
| // Convert port with bpf_ntohs(addr.sin_port) | |
| // Get PID with bpf_get_current_pid_tgid() | |
| // Store port in connect_curr_port map | |
| bpf_probe_read_user(&addr, sizeof(addr), (void *)ctx->args[1]); | |
| if (addr.sin_family != 2) return 0; | |
| u16 port = bpf_ntohs(addr.sin_port); | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| DEBUG_NUM("port", port); | |
| bpf_map_update_elem(&connect_curr_port, &pid, &port, BPF_ANY); | |
| return 0; | |
| } | |
| SEC("tracepoint/syscalls/sys_exit_connect") | |
| int trace_connect_exit(struct trace_event_raw_sys_exit *ctx) | |
| { | |
| // Check if ctx->ret == 0 (successful connection) | |
| // Get PID with bpf_get_current_pid_tgid() | |
| // Look up port from connect_curr_port map | |
| // Check if lookup returned NULL | |
| // Submit the port with SUBMIT_NUM(*port) | |
| // Clean up map entry with bpf_map_delete_elem() | |
| int conn = ctx->ret; | |
| DEBUG_NUM("pid", conn); | |
| if (conn != 0) return 0; | |
| u64 pid = bpf_get_current_pid_tgid(); | |
| u16 *port = bpf_map_lookup_elem(&connect_curr_port, &pid); | |
| if (!port) return 0; | |
| DEBUG_NUM("returned port", *port); | |
| SUBMIT_NUM(*port); | |
| bpf_map_delete_elem(&connect_curr_port, &pid); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "ep_sock.h" | |
| #include "syscalls.h" | |
| #include <bpf/bpf_tracing.h> | |
| #include <bpf/bpf_endian.h> | |
| SEC("tracepoint/syscalls/sys_exit_connect") | |
| int trace_connect_exit(struct trace_event_raw_sys_exit *ctx) | |
| { | |
| // Uncomment to see -115 (EINPROGRESS) | |
| DEBUG_NUM("connect ret", ctx->ret); | |
| return 0; | |
| } | |
| SEC("kprobe/tcp_finish_connect") | |
| int trace_tcp_connected(struct pt_regs *ctx) | |
| { | |
| // Get struct sock* from 1st parameter | |
| // Read destination port from sock | |
| // Convert port to little endian and submit | |
| struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx); | |
| struct sock_common sc; | |
| bpf_probe_read_kernel(&sc, sizeof(sc), (void *)&sk->__sk_common); | |
| u16 port = bpf_ntohs(sc.skc_dport); | |
| DEBUG_NUM("port", port); | |
| SUBMIT_NUM(port); | |
| return 0; | |
| } |
| #include "ep_platform.h" | |
| #include "syscalls.h" | |
| #include "kfuncs.h" | |
| #include <bpf/bpf_tracing.h> | |
| SEC("kprobe/tcp_sendmsg") | |
| int trace_tcp_send(struct pt_regs *ctx) | |
| { | |
| // Get msghdr from 2nd parameter | |
| // Read iov_iter from msg->msg_iter | |
| // Read iovec from iter | |
| // Read data from the usersspace buf at iov.iov_base | |
| // Find the token in the buf, submit it | |
| struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx); | |
| struct iov_iter iter; | |
| bpf_probe_read_kernel(&iter, sizeof(iter), (void *)&msg->msg_iter); | |
| struct iovec iovec = iter.__ubuf_iovec; | |
| DEBUG_STRUCT("iovec", iovec); | |
| char buf[125]; | |
| bpf_probe_read_user_str(buf, sizeof(buf), iovec.iov_base); | |
| DEBUG_STR("buf", buf); | |
| int token_start = bpf_strstr(buf, "Bearer ") + 7; | |
| DEBUG_NUM("token_start", token_start); | |
| int token_pos = bpf_strchr(&buf[66], '\r'); | |
| DEBUG_NUM("token_pos", token_pos); | |
| DEBUG_STR_LEN("token", &buf[66], token_pos); | |
| SUBMIT_STR_LEN(&buf[66], token_pos); | |
| return 0; | |
| } |