Ilk eBPF Programi ve Tracepoint'ler
Tracing a system call
Önceki alıştırmalarda scheduler’a (sched_process_exec) hook yaptık. Bu event, kernel binary’yi yükleyip dahili yapılarını doldurduktan sonra tetiklenir.
Peki bundan önce ne olur? bash gibi bir program ls çalıştırmak istediğinde, execve system call aracılığıyla kernel’a bir istek gönderir.
System call’lar şimdiye kadar kullandığımız özel tracepoint’lerden farklıdır. Tüm syscall’lar için geçerli olan genel entry ve exit event’lerini kullanırlar.
Genel syscall structure’ı
Yüzlerce system call (open, write, execve…) olduğu ve her birinin kendine özgü argument’leri bulunduğu için, kernel tracepoint’ler için genel bir structure kullanır.
Entry event’lerinde, tipi struct trace_event_raw_sys_enter olan bir context alırız:
struct trace_event_raw_sys_enter {
long int id; /* Syscall number */
long unsigned int args[6]; /* Arguments */
/* ... */
};
name veya pid gibi kullanışlı alanlar almıyoruz, sadece syscall number (id) ve system call’ın argument’leri olan bir integer dizisi, args[6], alıyoruz.
Neden 6? Çünkü syscall’ların alabilceği maksimum argument sayısı budur.
Tip bilgisi de almıyoruz! Tüm argument’ler long unsigned int (u64) tipindedir ve bunları doğru şekilde yorumlamak (pointer, struct, işaretli sayı veya u64 olarak) bize kalmıştır.
Her argument’in tipini ve anlamını öğrenmek için manual sayfasına bakabilirsiniz, burada fonksiyon imzası gösterilir:
int execve(const char *filename, char *const argv[], char *const envp[]);
filename ilk argument olduğu için ctx->args[0] konumundadır. args dizisi bunu unsigned long olarak saklar, bu yüzden const char * tipine cast etmemiz gerekir.
Şuna benzer bir şey:
Ancak işlenecek bir detay daha var.
Adres alanları hakkında
ctx->args[0], çağıran process’e (örneğin bash) ait belleğe işaret eder, bu da user space bellektir.
Kernel, güvenlik için user ve kernel bellek arasında katı bir ayrım uygular. Bu, daha önce kullandığımız kernel-space helper’larını (örneğin bpf_probe_read_kernel_str) bu veriyi okumak için kullanamayacağımız anlamına gelir. Bunun yerine bpf_probe_read_user_str(dst, size, src) kullanmamız gerekir.
Göreviniz
ctx->args[0]’dan filename pointer’ını oluşturun.- String’i user space’den yerel bir buffer’a okuyun.
Buffer üzerinde DEBUG_STR kullanırsınız, çalıştırılan birden fazla programı göreceksiniz.
SUBMIT_STR_LEN ile yalnızca bir cevap gönderebileceğinizi unutmayın, bu yüzden önceki alıştırmalarda yaptığımız gibi filtrelemeniz gerekecek.
len değerini bpf_probe_read_user_str’nin dönüş değerinden alabilirsiniz, kopyalanan byte sayısını döndürür.
Compare two strings for equality
- Args:
char* bufdynamic buffer to compareu32 buf_szlength of dynamic bufferconst char* buf2literal string to compare against