BPF Map'ler ve State Yonetimi

Reading syscall buffers

Önceki alıştırmada, birden fazla eBPF programı arasında veri paylaşmak için map’leri nasıl kullanacağımızı öğrendik.

Şimdi bunu syscall buffer’larından veri yakalamak için uygulayalım. Syscall’ların iki tracepoint event’i vardır: enter (başlamadan önce) ve exit (tamamlandıktan sonra). Her ikisi arasındaki veriyi ilişkilendirmemiz gerekecek.

read syscall

read syscall’a bakalım:

ssize_t read(int fd, void *buf, size_t count);

sys_enter_read ve sys_exit_read’e hook olursak:

  • Entry noktasında: argument’lere erişimimiz var, ancak buffer hala boş.
  • Exit noktasında: buffer veriyle dolmuş durumda, ancak yalnızca return value’ya erişimimiz var.

Her ikisini ilişkilendirmek için, entry noktasında buffer pointer’ı bir map kullanarak yakalayıp, exit noktasında doldurulmuş buffer’ı okuyoruz.

Entry noktasında buffer adresini saklama

Öncelikle, syscall başladığında buffer pointer’ı kaydetmemiz gerekiyor. Buffer adresi ctx->args[1] içindedir.

Bu adresi, daha sonra arayabilmemiz için PID’yi key olarak kullanarak bir map’te saklayacağız:

u64 buf_addr = ctx->args[1];
bpf_map_update_elem(&read_buffers, &pid, &buf_addr, BPF_ANY);

Buffer adresini u64 value olarak sakladığımıza dikkat edin.

Exit noktasında buffer’ı okuma

Şimdi exit programında, syscall tarafından doldurulan buffer’ı alıp okumamız gerekiyor.

Önce read işleminin başarılı olup olmadığını kontrol edin:

size_t count = ctx->ret;
if (count <= 0) return 0;

Return value ctx->ret, kaç byte okunduğunu söyler.

Ardından, PID’yi key olarak kullanarak kayıtlı buffer adresini bulun:

u64 *buf_ptr = bpf_map_lookup_elem(&read_buffers, &pid);
if (!buf_ptr) return 0;

Bu, saklanan value’ya (u64*) bir pointer döndürür.

Buffer’ı okumak için yerel bir buffer oluşturun ve bpf_probe_read_user kullanın. Gerçek buffer adresini elde etmek için buf_ptr’ı dereference etmeniz gerekecek:

char local_buf[32];
if (count > 32) count = 32;
bpf_probe_read_user(local_buf, count, (void *)*buf_ptr);

*buf_ptr ifadesine dikkat edin, daha önce sakladığımız buffer adresini elde etmek için pointer’ı dereference ediyoruz.

Görev

Bir program, parola içeren bir dosyayı okuyor. Dosyanın tüm içeriğini SUBMIT_STR_LEN ile gönderin.

Yapmanız gereken

Buffer içeriğini yakalamak ve göndermek için hem entry hem de exit programlarını tamamlayın.

Quick reference
Compare two strings for equality
Function bpf_strncmp Full documentation
Args:
char* bufdynamic buffer to compare
u32 buf_szlength of dynamic buffer
const char* buf2literal string to compare against
Returns 0 if strings match, non-zero if they differ
Read string from user space into kernel buffer
Function bpf_probe_read_user_str Full documentation
Args:
void* dstkernel buffer to read into
u32 sizemaximum bytes to read
const void* srcuser space pointer to string
On success, returns number of bytes read (including null terminator)
On error, returns negative error code
Read bytes from user space into kernel buffer
Function bpf_probe_read_user Full documentation
Args:
void* dstkernel buffer to read into
u32 sizebytes to read
const void* srcuser space pointer
On success, returns 0
On error, returns negative error code
Run your code to see execution events here