Skip to content

Instantly share code, notes, and snippets.

@zigelboim-misha
Last active March 10, 2024 12:40
Show Gist options
  • Save zigelboim-misha/c497b674c08dd15e231a84658472c4f2 to your computer and use it in GitHub Desktop.
Save zigelboim-misha/c497b674c08dd15e231a84658472c4f2 to your computer and use it in GitHub Desktop.
eBPF Kernel Side - Print all System Write Events
FROM ubuntu:22.04
RUN apt update && apt upgrade && apt install -y wget clang llvm curl
RUN wget https://aka.pw/bpf-ecli -O ecli && chmod +x ./ecli
RUN wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc
ENTRYPOINT [ "tail", "-f", "/dev/null" ]
// This code is taken from eunomia eBPF tutorial
// https://eunomia.dev/tutorials/1-helloworld/#hello-world-minimal-ebpf-program
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#define BPF_NO_GLOBAL_DATA
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
typedef unsigned int u32;
typedef int pid_t;
const pid_t pid_filter = 0;
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("tp/syscalls/sys_enter_write")
int handle_tp(void *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;
if (pid_filter && pid != pid_filter)
return 0;
bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid);
return 0;
}
@zigelboim-misha
Copy link
Author

zigelboim-misha commented Mar 6, 2024

This gist is for creating a basic eBPF function that will be injected into the kernel.

Everything Explained

eBPF file

Header Files

  • #define BPF_NO_GLOBAL_DATA - This macro is defined to indicate that the BPF program does not use any global data.
  • #include <linux/bpf.h> - This includes the main BPF header file.
  • #include <bpf/bpf_helpers.h> - This includes BPF helper functions.
  • #include <bpf/bpf_tracing.h> - This includes BPF tracing related functions.

Type Definitions

  • typedef unsigned int u32; - This defines an unsigned 32-bit integer type u32.
  • typedef int pid_t; - This defines the pid_t type, which represents a process ID.

Variables

  • const pid_t pid_filter = 0; - This defines a constant pid_filter with a default value of 0. This variable will be used to filter which process IDs to trace. If set to 0, it will trace all process IDs.

  • char LICENSE[] SEC("license") = "Dual BSD/GPL"; - This specifies the license of the BPF program. In this case, it's dual licensed under the BSD and GPL licenses.

BPF Program Section

  • SEC("tp/syscalls/sys_enter_write") - This is a section specifier indicating that this BPF program should be attached to the syscalls/sys_enter_write tracepoint. It means the program will run when the sys_enter_write tracepoint is hit.
    BPF Program:

  • int handle_tp(void *ctx) - This is the main function of the BPF program. It takes a context pointer as an argument.

  • pid_t pid = bpf_get_current_pid_tgid() >> 32; - This line extracts the process ID from the context. The bpf_get_current_pid_tgid() function returns a 64-bit value where the high 32 bits represent the process ID. Shifting by 32 bits gets the process ID alone.

  • if (pid_filter && pid != pid_filter) return 0; - This checks if pid_filter is set and if the current process ID does not match the filtered PID. If it doesn't match, the program returns early, effectively skipping the tracing for this process.

  • bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid); - This line prints a message to the kernel log using bpf_printk(). It will log a message every time the write syscall is called, showing the process ID that triggered it. This will be written to /sys/kernel/debug/tracing/trace_pipe.

  • return 0; - The function ends by returning 0.

Output Example

root@52b8eb8a0394:/ebpf# cat /sys/kernel/debug/tracing/trace_pipe             
 ld-linux-x86-64-67595   [000] d...1 16709.167314: bpf_trace_printk: BPF triggered sys_enter_write from PID 67573.
            sudo-67571   [004] d...1 16709.167379: bpf_trace_printk: BPF triggered sys_enter_write from PID 67571.
            sudo-63906   [015] d...1 16709.167422: bpf_trace_printk: BPF triggered sys_enter_write from PID 63906.
 containerd-shim-63479   [014] d...1 16709.167479: bpf_trace_printk: BPF triggered sys_enter_write from PID 63445.
         dockerd-37655   [011] d...1 16709.167531: bpf_trace_printk: BPF triggered sys_enter_write from PID 602.
   lifecycle-serve-529   [006] d...1 16709.167569: bpf_trace_printk: BPF triggered sys_enter_write from PID 506.

Dockerfile

The Dockerfile contains everything that is needed to compile the .c code:

  1. Build the image using docker build -t ebpf .
  2. Run the Dockerfile using docker run --privileged -v C:\Path\To\Files:/ebpf ebpf:latest

Compile the code

To compile the code, run the ./ecc minimal.bpf.c command from inside the container.

Run the code on the kernel

To run the code itself, you can inject the code using 3rd party tools or run it using ./ecli run package.json.

Enabling Tracing

If your Linux distribution (e.g. Ubuntu) does not have the tracing subsystem enabled by default, you may not see any output. Use the following command to enable this feature:

  • sudo mount -t debugfs none /sys/kernel/debug
  • sudo echo 1 > /sys/kernel/debug/tracing/tracing_on

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment