Saat menerapkan ulang QInst pada LLVM, saya menemukan masalah berikut: QEMU dalam mode emulasi dari satu proses secara alami memotong semua panggilan sistem "tamu". Akibatnya, plugin instrumentasi memiliki titik masuk tunggal untuk preprocessing mereka, di mana keputusan dapat dibuat oleh angka SYS_*
dan nilai argumen. Cukup nyaman. Masalahnya adalah bahwa semua panggilan sistem dibuat terutama oleh libc
dan, menulis ulang kode secara statis, kita tidak akan sampai ke bagian ini dalam banyak kasus. Tentu saja, orang dapat menggunakan ptrace
, yang hanya dirancang untuk ini juga. Tetapi kemudian saya tidak yakin apakah itu akan mungkin dilakukan tanpa proses terpisah, dan semantik QInst menyiratkan intersepsi "sinkron" yang sepele - Anda harus entah bagaimana menyuntikkan panggilan ke handler, dan ini jauh lebih rumit daripada LD_PRELOAD
biasa. Anda dapat membungkus setiap panggilan sistem - tetapi ini setidaknya tidak nyaman (selain itu, kami dapat melewatkan sesuatu, karena dalam hal ini kami tidak mencegat panggilan sistem, tetapi bungkus khusus mereka).
Di bawah cut - solusi yang tidak terikat dengan LLVM, tetapi dipertajam untuk Linux pada x86_64 (tetapi diadaptasi untuk Linux pada arsitektur lain).
Catatan: artikel ini tidak menyajikan solusi siap pakai universal - hanya cocok untuk daftar kasus yang cukup luas . Tetapi artikel ini dapat dianggap ulasan-Jumat: menarik (mudah-mudahan) baru (untuk sebagian besar?) Informasi, sejumput pemrograman brute force dan contoh-contoh kerja. Dan jangan biarkan hari Jumat kita bahkan dikaburkan oleh kenyataan bahwa hari ini adalah hari Kamis!
Untuk memulainya, mari kita putuskan: dalam artikel ini, tujuannya bukan untuk mem-bypass mekanisme anti-debugging, dan secara umum diasumsikan bahwa program tidak menolak intersepsi panggilan sistem. Di sisi lain, menulis ulang kode sumber secara manual sehingga program ini membantu kami juga merupakan prospek yang begitu-begitu saja dalam hal intensitas tenaga kerja. Karena itu LD_PRELOAD
. Namun, jika Anda ingin mengkompilasi mekanisme semacam itu secara statis ke dalam program Anda sendiri, maka LD_PRELOAD
sama sekali tidak penting di sini.
Penyimpangan liris: bagaimana panggilan sistem dilakukan di Linux? Ada beberapa cara untuk melakukan ini. Sekali, suatu proses (pada x86) cukup masukkan nomor panggilan sistem dalam eax
, argumen pertama dalam ebx
dan seterusnya, setelah itu menarik int 0x80
. Pada titik tertentu, mereka memutuskan bahwa itu tidak terlalu cepat, atau tidak ramah pada cache, atau sesuatu yang lain, dan mengubahnya. Kemudian lagi. Saat ini, vDSO digunakan - ini adalah objek yang dibagikan secara jujur ββyang disuntikkan ke dalam setiap proses, yang dapat Anda tarik fungsinya untuk membuat panggilan sistem yang sering yang tidak memerlukan pengalihan konteks (misalnya, time
). Tampaknya Anda dapat mengakhiri artikel ini, tetapi tidak: sekitar empat panggilan sistem terpisah dijelaskan di sana (angka pasti kemungkinan besar tergantung pada versi kernel), dan ada sesuatu yang tidak diamati pada titik masuk global ...
Mungkin untuk mencari titik umum lebih lanjut, memilih melalui instruksi mana pada panggilan sistem mesin ini ( int 0x80
/ sysenter
/ ...) - atau mungkin tidak ada sama sekali - mari kita serahkan ini kepada pembaca. Lebih baik kita memperhatikan mekanisme seccomp
, yang menyediakan antarmuka standar lain untuk menyaring panggilan sistem.
Seccomp adalah singkatan dari komputasi aman dan dimaksudkan untuk menangani kode yang tidak terlalu tepercaya. Kutipan dari man seccomp (2)
:
Strict secure computing mode is useful for number-crunching applications that may need to execute untrusted byte code, perhaps obtained by reading from a pipe or socket.
Nah, setelah Meltdown & Co, orang-orang sudah takut untuk menjalankan JavaScript di beberapa utas - saya tidak tahu bagaimana dengan kode biner yang tidak terpercaya. Namun, keamanan di lingkungan yang sebagian tepercaya adalah topik yang terpisah, dan saya hanya bisa mengatakan bahwa meskipun seccomp dapat berfungsi, misalnya, pada level arus individu, saya secara alami tidak akan berani duduk di ruang alamat yang sama dengan menjalankan kode mesin yang tidak dipercaya. Secara umum, "persiapan" malware yang aman juga bukan topik artikel ini.
Dalam hal ini, saya tertarik untuk mencegat panggilan sistem bukan dari sudut pandang instrumentasi. Untungnya, selain mode SECCOMP_SET_MODE_STRICT
, memaksa proses untuk mengakhiri ketika membuat panggilan apa pun kecuali read
, write
, _exit
dan sigreturn
, ada SECCOMP_SET_MODE_FILTER
- ini memungkinkan Anda untuk mengatur program BPF yang akan memfilter hanya panggilan sistem yang diminati, dan untuk apa yang diputuskan untuk menentukan apa yang menjadi keputusan yang disaring. Di sini, pilihan sudah jauh lebih besar, tetapi SECCOMP_RET_TRAP
sekarang menarik bagi SECCOMP_RET_TRAP
: ketika putusan ini dikeluarkan, aliran tertentu dikirim SIGSYS
, yaitu, persis apa yang dibutuhkan: Anda dapat secara sinkron memproses permintaan dan mengembalikan kontrol seolah-olah pemanggilan sistem telah terjadi secara normal.
Persiapan
Untuk penangan kami, buat file syscall-handler.c :
#include <linux/seccomp.h> #include <linux/filter.h> #include <linux/audit.h> #include <linux/signal.h> #include <sys/ptrace.h> uint64_t handle_syscall(uint32_t num, uint32_t *drop_syscall, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6); void initialize_handler(void) { } static void __attribute__((constructor))constr(void) { initialize_handler(); }
Mari kita jelaskan fungsi yang memproses panggilan sistem - ini cukup jelas: fungsi mengambil jumlah panggilan sistem, (maksimum) enam argumennya, dan tanda (dikembalikan) bahwa panggilan sistem tidak hanya diperhitungkan, tetapi ditiru, dan itu benar-benar tidak perlu dilakukan. Sisanya bahkan lebih sederhana: fungsi initialize_handler
akan mengonfigurasi pengiriman semua ini.
Dan apa fungsi constr
misterius ini? Ini hanya memanggil initialize_handler
, tetapi karena ditandai dengan atribut constructor
, itu akan dipanggil selama inisialisasi perpustakaan (atau file yang dapat dieksekusi utama) yang berisi fungsi ini. Jika kita ingin mengaktifkan intersepsi setelah memulai program, kita dapat mengatur constr
dan memanggil initialize_handler
secara manual.
Menulis Program BPF
Sekarang Anda perlu menulis bytecode BPF yang hanya akan menanggapi nomor panggilan sistem yang diinginkan dengan mengeluarkan SECCOMP_RET_TRAP
. Dokumentasi untuk format program terletak, khususnya, pada sumber kernel di Documentation / networking / filter.txt , sedangkan untuk seccomp Anda perlu melihat versi yang lama, bukan eBPF. File linux / seccomp.h , linux / filter.h sendiri, serta linux / bpf_common.h yang dirujuk , juga akan sangat berguna. Jika Anda mengambil sumber-sumber ini di tumpukan, contoh dari man seccomp
dan kocok, Anda mendapatkan sesuatu seperti ini:
struct sock_filter filt[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, args[5]))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K , MARKER, 0, 1), BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_ALLOW), BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_TRAP), };
Karena saya menulis ini beberapa minggu yang lalu, dan bahkan kemudian itu adalah kode dari contoh, maka menurut tradisi lama katakanlah sudah jelas tinggalkan pembaca sebagai latihan. Pada prinsipnya, opcodes ( LD
/ JMP
dengan offset 0 dan 1 / RET
), kondisi ( JEQ
), instruksi untuk menggunakan operan langsung ( BPF_K
) terlihat jelas di sini - lihat filter.txt untuk detail lebih lanjut . Dan makro memungkinkan Anda untuk tidak memikirkan mengemas ini semua dalam kata-kata tim.
Mengapa kita membutuhkan konstanta MARKER
? Ini adalah penopang yang masih memungkinkan Anda untuk melewatkan panggilan sistem jika userspace-filter kami memutuskan demikian. Ceroboh, kruk - tapi saya tidak berjanji untuk menunjukkan solusi universal. Namun, solusi yang ditunjukkan juga dalam kebanyakan kasus berfungsi dengan cukup sukses, sementara diatur secara sepele.
Kami menempatkan MARKER
sama ini dalam argumen keenam dari panggilan sistem dengan harapan bahwa ini adalah "detail berlebihan" dan bahwa keenam argumen jarang digunakan. Kami kurangi dengan menggunakan instruksi pertama dari program BPF kami: dari sini ini adalah ekspresi misterius - offsetof(struct seccomp_data, args[5])
- kernel meneruskan struktur seccomp_data
kepada seccomp_data
(lihat man seccomp
):
struct seccomp_data { int nr; __u32 arch; __u64 instruction_pointer; __u64 args[6]; };
... dan kita kurangi satu variabel darinya pada offset: offsetof(struct seccomp_data, args[5])
. Paragraf ini disponsori oleh aol-nnov
Ini digunakan entah bagaimana seperti ini:
uint64_t handle_syscall(uint32_t num, uint32_t *drop_syscall, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6) { return 0; } #define MARKER 0x12345678 static int in_handler; static void handle_sigsys(int num, siginfo_t *si, void *arg) { ucontext_t *ctx = arg; greg_t *gregs = ctx->uc_mcontext.gregs; uint32_t drop = 0; uint64_t res; if (!in_handler) { in_handler = 1; res = handle_syscall(gregs[REG_RAX], &drop, gregs[REG_RDI], gregs[REG_RSI], gregs[REG_RDX], gregs[REG_R10], gregs[REG_R8], gregs[REG_R9]); in_handler = 0; } if (!drop) { res = syscall(gregs[REG_RAX], gregs[REG_RDI], gregs[REG_RSI], gregs[REG_RDX], gregs[REG_R10], gregs[REG_R8], MARKER); } gregs[REG_RAX] = res; } void initialize_handler(void) {
handle_sigsys
adalah penangan sinyal gaya baru, sehingga dapat dikatakan, digunakan bersama dengan fungsi sigaction
. Tentang parameter ketiganya, man sigaction
mengatakan:
ucontext This is a pointer to a ucontext_t structure, cast to void *. The structure pointed to by this field contains signal context information that was saved on the user-space stack by the kernel; for details, see sigreturn(2). Further information about the ucontext_t structure can be found in getcontext(3). Commonly, the handler function doesn't make any use of the third argument.
Kami tidak punya kasus yang sangat umum untuk suatu perubahan kami tidak akan menggunakan argumen kedua - ada juga banyak hal menarik, tetapi tidak ada argumen untuk panggilan sistem. Di sini tarian dengan rebana dimulai, dan rebana dibutuhkan dari arsitektur yang benar. Sangat mungkin bahwa libseccomp berhasil meningkatkan detail arsitektur ini, dan hanya menghasilkan berbagai argumen - dan jika tidak melakukannya sekarang, maka mungkin akan lebih baru - tetapi karena kami memiliki artikel pengantar, kami akan menganggap bahwa level rendah seperti itu bukan bug. , dan sebuah fitur ... Oleh karena itu, cukup periksa tabel kedua dari man syscall untuk arsitektur yang diperlukan, dalam kasus saya x86_64.
Dalam penangan yang sama, kita melihat fungsi lucu lainnya: syscall
- sebenarnya, dari dia kita baru saja membaca man. Dia hanya mendapatkan nomor panggilan sistem dan argumen, dan dia mengeksekusinya. Pada beberapa arsitektur, ada segala macam ABI yang rumit dengan "alignment" nilai 64-bit dalam register 32-bit (semuanya dibaca di halaman manual yang sama) - mari kita berharap ini tidak relevan pada x86_64, kalau tidak kita harus menguraikannya tergantung pada nomor panggil dengan mengurangi register, karena syscall()
sudah menyembunyikan logika ini dengan sendirinya. Selain kasus aneh seperti di sini, ketika nomor panggilan ditentukan secara dinamis, itu dapat digunakan jika karena alasan tertentu kita tidak memiliki libc-wrapper untuk panggilan sistem. Sebagai contoh, inilah tepatnya cara saya memanggil seccomp
dalam fungsi berikut:
syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);
prctl
aneh prctl
diperlukan untuk alasan keamanan: dalam man seccomp
contoh di mana, tanpa persyaratan ini, Anda dapat memasukkan biner ID pengguna-set sial ke dalam realitas virtual, di mana setuid
akan mengembalikan nol, tetapi tidak akan melakukan apa-apa, yang akan sangat berbahaya dari sudut pandang tampilan elevasi hak istimewa.
Kami mengkompilasi dan menggunakan perpustakaan kami:
$ gcc -fPIC --shared syscall-handler.c -o syscall-handler.so $ LD_PRELOAD=syscall-handler.so ls ERROR: ld.so: object 'syscall-handler.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored. article.md example.c syscall-handler.c syscall-handler.so $ LD_PRELOAD=./syscall-handler.so ls article.md example.c syscall-handler.c syscall-handler.so
Seperti yang Anda lihat, setidaknya pada sistem saya, Anda perlu menentukan path ke perpustakaan. Meski relatif, tapi jalannya. Baik bahwa bootloader memperingatkan tentang kesalahan tersebut. Dalam kasus kedua, semuanya tampak berhasil.
Debugging
Bagaimana cara kita men-debug panggilan sistem? Anda dapat, tentu saja, melalui catch syscall
di gdb, tetapi dalam kasus ini (seperti pada kebanyakan orang lain, jujur) alat strace
luar biasa akan membantu kami:
strace -E LD_PRELOAD=./syscall-handler.so ls
Parameter -E
menunjukkan bahwa Anda perlu mengatur variabel yang ditentukan untuk proses yang sedang dipelajari. Kalau tidak, akan perlu memuat pustaka yang di-debug ke dalam debugger itu sendiri - perspektif yang sangat-begitu.
Output strace $ strace -E LD_PRELOAD=./syscall-handler.so ls execve("/bin/ls", ["ls"], 0x5652b17024d0 /* 61 vars */) = 0 brk(NULL) = 0x55a2f556d000 arch_prctl(0x3001 /* ARCH_??? */, 0x7fffe8e949b0) = -1 EINVAL ( ) openat(AT_FDCWD, "./syscall-handler.so", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \21\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0775, st_size=16504, ...}) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29f6baf000 getcwd("/home/trosinenko/some/path", 128) = 63 mmap(NULL, 16480, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f6baa000 mmap(0x7f29f6bab000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f29f6bab000 mmap(0x7f29f6bac000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f29f6bac000 mmap(0x7f29f6bad000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f29f6bad000 close(3) = 0 access("/etc/ld.so.preload", R_OK) = -1 ENOENT ( ) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=254851, ...}) = 0 mmap(NULL, 254851, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f29f6b6b000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300p\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=163240, ...}) = 0 mmap(NULL, 174640, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f6b40000 mprotect(0x7f29f6b46000, 135168, PROT_NONE) = 0 mmap(0x7f29f6b46000, 102400, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f29f6b46000 mmap(0x7f29f6b5f000, 28672, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f29f6b5f000 mmap(0x7f29f6b67000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7f29f6b67000 mmap(0x7f29f6b69000, 6704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f29f6b69000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360r\2\0\0\0\0\0"..., 832) = 832 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0!U\364U\255V\275\207\34\202%\274\312\205\356%"..., 68) = 68 fstat(3, {st_mode=S_IFREG|0755, st_size=2025032, ...}) = 0 lseek(3, 64, SEEK_SET) = 64 read(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784) = 784 lseek(3, 848, SEEK_SET) = 848 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 lseek(3, 880, SEEK_SET) = 880 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0!U\364U\255V\275\207\34\202%\274\312\205\356%"..., 68) = 68 mmap(NULL, 2032984, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f694f000 mmap(0x7f29f6974000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f29f6974000 mmap(0x7f29f6aec000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f29f6aec000 mmap(0x7f29f6b36000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e6000) = 0x7f29f6b36000 mmap(0x7f29f6b3c000, 13656, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f29f6b3c000 close(3) = 0 openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libpcre2-8.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200!\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=539176, ...}) = 0 mmap(NULL, 541448, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f68ca000 mmap(0x7f29f68cc000, 376832, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f29f68cc000 mmap(0x7f29f6928000, 151552, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x5e000) = 0x7f29f6928000 mmap(0x7f29f694d000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x82000) = 0x7f29f694d000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \22\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=18816, ...}) = 0 mmap(NULL, 20752, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f68c4000 mmap(0x7f29f68c5000, 8192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f29f68c5000 mmap(0x7f29f68c7000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f29f68c7000 mmap(0x7f29f68c8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f29f68c8000 close(3) = 0 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\201\0\0\0\0\0\0"..., 832) = 832 lseek(3, 824, SEEK_SET) = 824 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\09V\4W\221\35\226\215\236\6\10\215\240\25\227\v"..., 68) = 68 fstat(3, {st_mode=S_IFREG|0755, st_size=158288, ...}) = 0 lseek(3, 824, SEEK_SET) = 824 read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\09V\4W\221\35\226\215\236\6\10\215\240\25\227\v"..., 68) = 68 mmap(NULL, 140448, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f29f68a1000 mmap(0x7f29f68a8000, 69632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7f29f68a8000 mmap(0x7f29f68b9000, 20480, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f29f68b9000 mmap(0x7f29f68be000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c000) = 0x7f29f68be000 mmap(0x7f29f68c0000, 13472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f29f68c0000 close(3) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29f689f000 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29f689c000 arch_prctl(ARCH_SET_FS, 0x7f29f689c800) = 0 mprotect(0x7f29f6b36000, 12288, PROT_READ) = 0 mprotect(0x7f29f68be000, 4096, PROT_READ) = 0 mprotect(0x7f29f68c8000, 4096, PROT_READ) = 0 mprotect(0x7f29f694d000, 4096, PROT_READ) = 0 mprotect(0x7f29f6b67000, 4096, PROT_READ) = 0 mprotect(0x7f29f6bad000, 4096, PROT_READ) = 0 mprotect(0x55a2f4670000, 4096, PROT_READ) = 0 mprotect(0x7f29f6bdd000, 4096, PROT_READ) = 0 munmap(0x7f29f6b6b000, 254851) = 0 set_tid_address(0x7f29f689cad0) = 31949 set_robust_list(0x7f29f689cae0, 24) = 0 rt_sigaction(SIGRTMIN, {sa_handler=0x7f29f68a8c50, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f29f68b6540}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {sa_handler=0x7f29f68a8cf0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f29f68b6540}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 statfs("/sys/fs/selinux", 0x7fffe8e94900) = -1 ENOENT ( ) statfs("/selinux", 0x7fffe8e94900) = -1 ENOENT ( ) brk(NULL) = 0x55a2f556d000 brk(0x55a2f558e000) = 0x55a2f558e000 openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 read(3, "nodev\tsysfs\nnodev\ttmpfs\nnodev\tbd"..., 1024) = 425 read(3, "", 1024) = 0 close(3) = 0 access("/etc/selinux/config", F_OK) = -1 ENOENT ( ) rt_sigaction(SIGSYS, {sa_handler=0x7f29f6bab1ff, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f29f6995470}, NULL, 8) = 0 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0 seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=4, filter=0x7fffe8e94930}) = 0 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 rt_sigreturn({mask=[]}) = 3 fstat(3, {st_mode=000, st_size=0, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(3, {st_mode=S_IFREG|0644, st_size=8994080, ...}) = 0 rt_sigreturn({mask=[]}) = 0 mmap(NULL, 8994080, PROT_READ, MAP_PRIVATE, 3, 0) = 0x9 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6aaf6, si_syscall=__NR_mmap, si_arch=AUDIT_ARCH_X86_64} --- mmap(NULL, 8994080, PROT_READ, MAP_PRIVATE, 3, 0x12345678) = -1 EINVAL ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) close(3) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(3) = 0 rt_sigreturn({mask=[]}) = 0 openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3 rt_sigreturn({mask=[]}) = 3 fstat(3, {st_mode=037, st_size=0, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(3, {st_mode=S_IFREG|0644, st_size=2995, ...}) = 0 rt_sigreturn({mask=[]}) = 0 read(3, "", 4096) = 0 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a658d8, si_syscall=__NR_read, si_arch=AUDIT_ARCH_X86_64} --- read(3, "# Locale name alias data base.\n#"..., 4096) = 2995 rt_sigreturn({mask=[]}) = 2995 read(3, "", 4096) = 0 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a658d8, si_syscall=__NR_read, si_arch=AUDIT_ARCH_X86_64} --- read(3, "", 4096) = 0 rt_sigreturn({mask=[]}) = 0 close(3) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(3) = 0 rt_sigreturn({mask=[]}) = 0 openat(AT_FDCWD, "/usr/lib/locale/ru_RU.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru_RU.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) openat(AT_FDCWD, "/usr/lib/locale/ru_RU.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru_RU.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) openat(AT_FDCWD, "/usr/lib/locale/ru_RU/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru_RU/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) openat(AT_FDCWD, "/usr/lib/locale/ru.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) openat(AT_FDCWD, "/usr/lib/locale/ru.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) openat(AT_FDCWD, "/usr/lib/locale/ru/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/ru/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) ioctl(1, TCGETS, {B4000000 opost -isig icanon -echo ...}) = 16 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a65cea, si_syscall=__NR_ioctl, si_arch=AUDIT_ARCH_X86_64} --- ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 rt_sigreturn({mask=[]}) = 0 ioctl(1, TIOCGWINSZ, {ws_row=45567, ws_col=63162, ws_xpixel=32553, ws_ypixel=0}) = 16 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6667b, si_syscall=__NR_ioctl, si_arch=AUDIT_ARCH_X86_64} --- ioctl(1, TIOCGWINSZ, {ws_row=58, ws_col=271, ws_xpixel=0, ws_ypixel=0}) = 0 rt_sigreturn({mask=[]}) = 0 openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3 rt_sigreturn({mask=[]}) = 3 fstat(3, {st_mode=037777777777, st_size=139818209035479, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0 rt_sigreturn({mask=[]}) = 0 getdents64(3, /* d_reclen < offsetof(struct dirent64, d_name) */ /* 0 entries */, 32768) = 217 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a3007b, si_syscall=__NR_getdents64, si_arch=AUDIT_ARCH_X86_64} --- getdents64(3, /* 7 entries */, 32768) = 232 rt_sigreturn({mask=[]}) = 232 getdents64(3, /* 7 entries */, 32768) = 217 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a3007b, si_syscall=__NR_getdents64, si_arch=AUDIT_ARCH_X86_64} --- getdents64(3, /* 0 entries */, 32768) = 0 rt_sigreturn({mask=[]}) = 0 close(3) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(3) = 0 rt_sigreturn({mask=[]}) = 0 fstat(1, {st_mode=000, st_size=0, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x7), ...}) = 0 rt_sigreturn({mask=[]}) = 0 write(1, "article.md example.c syscall-h"..., 61) = 1 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a60317, si_syscall=__NR_write, si_arch=AUDIT_ARCH_X86_64} --- write(1, "article.md example.c syscall-h"..., 61article.md example.c syscall-handler.c syscall-handler.so ) = 61 rt_sigreturn({mask=[]}) = 61 close(1) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(1) = 0 rt_sigreturn({mask=[]}) = 0 close(2) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(2) = 0 rt_sigreturn({mask=[]}) = 0 exit_group(0) = 231 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a34fe6, si_syscall=__NR_exit_group, si_arch=AUDIT_ARCH_X86_64} --- exit_group(0) = ? +++ exited with 0 +++
(-, userspace), - , ...
... rt_sigaction(SIGSYS, {sa_handler=0x7f29f6bab1ff, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f29f6995470}, NULL, 8) = 0 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0 seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=4, filter=0x7fffe8e94930}) = 0 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 rt_sigreturn({mask=[]}) = 3 fstat(3, {st_mode=000, st_size=0, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(3, {st_mode=S_IFREG|0644, st_size=8994080, ...}) = 0 rt_sigreturn({mask=[]}) = 0 mmap(NULL, 8994080, PROT_READ, MAP_PRIVATE, 3, 0) = 0x9 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6aaf6, si_syscall=__NR_mmap, si_arch=AUDIT_ARCH_X86_64} --- mmap(NULL, 8994080, PROT_READ, MAP_PRIVATE, 3, 0x12345678) = -1 EINVAL ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) close(3) = 3 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a655bb, si_syscall=__NR_close, si_arch=AUDIT_ARCH_X86_64} --- close(3) = 0 rt_sigreturn({mask=[]}) = 0 openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a6579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3 rt_sigreturn({mask=[]}) = 3 fstat(3, {st_mode=037, st_size=0, ...}) = 5 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a5f7b9, si_syscall=__NR_fstat, si_arch=AUDIT_ARCH_X86_64} --- fstat(3, {st_mode=S_IFREG|0644, st_size=2995, ...}) = 0 rt_sigreturn({mask=[]}) = 0 read(3, "", 4096) = 0 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f29f6a658d8, si_syscall=__NR_read, si_arch=AUDIT_ARCH_X86_64} --- read(3, "# Locale name alias data base.\n#"..., 4096) = 2995 ...
, , β SIGSYS
, . , , fstat
: β !
...
, - . - : openat
, libc
: handle_syscall
if (num == SYS_openat) { fprintf(stderr, "openat: %s\n", (const char *)arg2); }
:
$ gcc -fPIC --shared syscall-handler.c -o syscall-handler.so $ LD_PRELOAD=./syscall-handler.so ls $ strace -E LD_PRELOAD=./syscall-handler.so ls ... rt_sigaction(SIGSYS, {sa_handler=0x7f55ef2cb24e, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f55ef0b5470}, NULL, 8) = 0 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0 seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=4, filter=0x7ffca47b36e0}) = 0 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 257 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f55ef18579c, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- write(2, "openat: /usr/lib/locale/locale-a"..., 39) = 1 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f55ef180317, si_syscall=__NR_write, si_arch=AUDIT_ARCH_X86_64} --- +++ killed by SIGSYS +++
: SA_NODEFER
, SIGSYS
, deadlock. initialize_handler
:
sig.sa_flags = SA_SIGINFO | SA_NODEFER;
$ LD_PRELOAD=./syscall-handler.so ls openat: /usr/lib/locale/locale-archive openat: /usr/share/locale/locale.alias openat: /usr/lib/locale/ru_RU.UTF-8/LC_IDENTIFICATION openat: /usr/lib/locale/ru_RU.utf8/LC_IDENTIFICATION openat: /usr/lib/locale/ru_RU/LC_IDENTIFICATION openat: /usr/lib/locale/ru.UTF-8/LC_IDENTIFICATION openat: /usr/lib/locale/ru.utf8/LC_IDENTIFICATION openat: /usr/lib/locale/ru/LC_IDENTIFICATION openat: . article.md example.c syscall-handler.c syscall-handler.so
...
, . mmap
:) , , - .
example.c:
#include <stdio.h> #include <stdlib.h> int main() { fprintf(stderr, "Allocating 1MB of memory\n"); void *ptr = malloc(1 << 20); fprintf(stderr, "Allocated: %p\n", ptr); }
$ LD_PRELOAD=./syscall-handler.so ./example Allocating 1MB of memory Allocated: 0x564fc07012a0
β¦ ?!?
$ strace -E LD_PRELOAD=./syscall-handler.so ./example ... rt_sigaction(SIGSYS, {sa_handler=0x7fd7065cd24e, sa_mask=[], sa_flags=SA_RESTORER|SA_NODEFER|SA_SIGINFO, sa_restorer=0x7fd7063e2470}, NULL, 8) = 0 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0 seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=4, filter=0x7fff8ea97810}) = 0 write(2, "Allocating 1MB of memory\n", 25) = 1 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064ad317, si_syscall=__NR_write, si_arch=AUDIT_ARCH_X86_64} --- write(2, "Allocating 1MB of memory\n", 25Allocating 1MB of memory ) = 25 rt_sigreturn({mask=[]}) = 25 brk(NULL) = 0xc --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064b355b, si_syscall=__NR_brk, si_arch=AUDIT_ARCH_X86_64} --- brk(NULL) = 0x55e31f74f000 rt_sigreturn({mask=[]}) = 94433973694464 brk(0x55e31f770000) = 0xc --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064b355b, si_syscall=__NR_brk, si_arch=AUDIT_ARCH_X86_64} --- brk(0x55e31f770000) = 0x55e31f770000 rt_sigreturn({mask=[]}) = 94433973829632 mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x9 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064b7af6, si_syscall=__NR_mmap, si_arch=AUDIT_ARCH_X86_64} --- mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0x12345678) = -1 EINVAL ( ) rt_sigreturn({mask=[]}) = -1 EPERM ( ) brk(0x55e31f870000) = 0xc --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064b355b, si_syscall=__NR_brk, si_arch=AUDIT_ARCH_X86_64} --- brk(0x55e31f870000) = 0x55e31f870000 rt_sigreturn({mask=[]}) = 94433974878208 write(2, "Allocated: 0x55e31f74f2a0\n", 26) = 1 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd7064ad317, si_syscall=__NR_write, si_arch=AUDIT_ARCH_X86_64} --- write(2, "Allocated: 0x55e31f74f2a0\n", 26Allocated: 0x55e31f74f2a0 ) = 26 rt_sigreturn({mask=[]}) = 26 exit_group(0) = 231 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7fd706481fe6, si_syscall=__NR_exit_group, si_arch=AUDIT_ARCH_X86_64} --- exit_group(0) = ? +++ exited with 0 +++
: mmap
, libc
brk
. ...
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> int main() { fprintf(stderr, "Allocating 1MB of memory\n"); void *ptr = malloc(1 << 20); fprintf(stderr, "Allocated: %p\n", ptr); const char *fname = "test.bin"; int fd = open(fname, O_RDONLY); fprintf(stderr, "Mapping first 1MB of %s\n", fname); void *ptr2 = mmap(NULL, 1 << 20, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, fd, 0); fprintf(stderr, "Mapped: %p\n", ptr2); return 0; }
$ gcc example.c -o example $ ./example Allocating 1MB of memory Allocated: 0x7f1aa7d09010 Mapping first 1MB of test.bin Mapped: 0x7f1aa7c09000 $ LD_PRELOAD=./syscall-handler.so ./example Allocating 1MB of memory Allocated: 0x556a51df52a0 openat: test.bin Mapping first 1MB of test.bin Mapped: 0xffffffffffffffff
Β«! β ...Β» 0xffffffffffffffff
, -1
, MAP_FAILED
.
, mmap
, :
#define ALLOW(sys) \ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), \ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K , sys, 0, 1), \ BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_ALLOW), struct sock_filter filt[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, args[5]))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K , MARKER, 0, 1), BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_ALLOW), ALLOW(SYS_mmap) BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_TRAP), };
:
$ LD_PRELOAD=./syscall-handler.so ./example Allocating 1MB of memory Allocated: 0x7fad45a6f010 openat: test.bin Mapping first 1MB of test.bin Mapped: 0x7fad4596f000
#define _GNU_SOURCE #include <errno.h> #include <stddef.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> #include <linux/audit.h> #include <linux/filter.h> #include <linux/seccomp.h> #include <sys/prctl.h> #include <unistd.h> #include <syscall.h> #include <signal.h> #include <string.h> uint64_t handle_syscall(uint32_t num, uint32_t *drop_syscall, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6) { if (num == SYS_openat) { fprintf(stderr, "openat: %s\n", (const char *)arg2); } return 0; } #define MARKER 0x12345678 static int in_handler; static void handle_sigsys(int num, siginfo_t *si, void *arg) { ucontext_t *ctx = arg; greg_t *gregs = ctx->uc_mcontext.gregs; uint32_t drop = 0; uint64_t res; if (!in_handler) { in_handler = 1; res = handle_syscall(gregs[REG_RAX], &drop, gregs[REG_RDI], gregs[REG_RSI], gregs[REG_RDX], gregs[REG_R10], gregs[REG_R8], gregs[REG_R9]); in_handler = 0; } if (!drop) { res = syscall(gregs[REG_RAX], gregs[REG_RDI], gregs[REG_RSI], gregs[REG_RDX], gregs[REG_R10], gregs[REG_R8], MARKER); } gregs[REG_RAX] = res; } void initialize_handler(void) { #define ALLOW(sys) \ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), \ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K , sys, 0, 1), \ BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_ALLOW), struct sock_filter filt[] = { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, args[5]))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K , MARKER, 0, 1), BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_ALLOW), ALLOW(SYS_mmap) BPF_STMT(BPF_RET | BPF_K , SECCOMP_RET_TRAP), }; struct sock_fprog prog = { sizeof(filt) / sizeof(filt[0]), filt }; struct sigaction sig; memset(&sig, 0, sizeof(sig)); sig.sa_sigaction = handle_sigsys; sig.sa_flags = SA_SIGINFO | SA_NODEFER; sigaction(SIGSYS, &sig, NULL); prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog); } static void __attribute__((constructor))constr(void) { initialize_handler(); }