Gestion des appels système à l'aide de LD_PRELOAD avec un seul point d'entrée

Lors de la réimplémentation de QInst sur LLVM, je suis tombé sur le problème suivant: QEMU en mode d'émulation d'un processus intercepte naturellement tous les appels système "invités". Par conséquent, le plug-in d'instrumentation a un point d'entrée unique pour leur prétraitement, où vous pouvez prendre des décisions par les nombres SYS_* et les valeurs d'argument. C'est assez pratique. Le problème est que tous les appels système sont effectués principalement par libc et, en réécrivant le code statiquement, nous n'atteindrons tout simplement pas cette partie dans la plupart des cas. Bien sûr, on pourrait utiliser ptrace , qui est également conçu pour cela. Mais alors je ne suis pas sûr qu'il serait possible de se passer d'un processus séparé, et la sémantique QInst impliquait une interception «synchrone» triviale - il faudrait en quelque sorte injecter l'appel au gestionnaire, et c'est beaucoup plus compliqué que le LD_PRELOAD habituel. Vous pouvez encapsuler chaque appel système - mais cela est au moins gênant (en outre, nous pouvons ignorer quelque chose, car dans ce cas, nous n'interceptons pas les appels système eux-mêmes, mais leurs wrappers spécifiques).


Under the cut - une solution qui n'est pas liée à LLVM, mais affinée pour Linux sur x86_64 (mais adaptée pour Linux sur d'autres architectures).


Remarque: cet article ne présente pas de solution prête à l'emploi universelle - il ne convient que pour une liste assez large de cas. Mais cet article peut être considéré comme une revue vendredi: des informations intéressantes (espérons-le) nouvelles (pour la plupart?), Une pincée de programmation par force brute et des exemples de travail. Et ne laissez même pas notre vendredi être obscurci par le fait qu'aujourd'hui est jeudi!


Pour commencer, décidons: dans cet article, le but n'est pas de contourner les mécanismes anti-débogage, et en général, on suppose que le programme ne résiste pas à l'interception des appels système. D'un autre côté, la réécriture manuelle du code source afin que le programme nous aide est également une perspective médiocre uniquement en termes d'intensité de travail. Par conséquent LD_PRELOAD . Cependant, si vous souhaitez compiler statiquement un tel mécanisme dans votre propre programme, alors LD_PRELOAD n'est absolument pas essentiel ici.


Digression lyrique: comment sont effectués les appels système sous Linux? Il existe plusieurs façons de procéder. Une fois, un processus (sur x86) a simplement mis le numéro d'appel système dans eax , le premier argument dans ebx et ainsi de suite, après quoi il a tiré int 0x80 . À un moment donné, ils ont décidé que ce n'était pas très rapide, ou pas convivial pour le cache, ou quelque chose d'autre, et l'ont refait. Là encore. À l'heure actuelle, vDSO est utilisé - il s'agit d'un objet partagé honnête injecté dans chaque processus, dont vous pouvez extraire des fonctions pour effectuer des appels système fréquents qui ne nécessitent pas de changement de contexte (par exemple, l' time ). Il semblerait que vous puissiez terminer l'article à ce sujet, mais non: environ quatre appels système distincts y sont décrits (le nombre exact dépend probablement de la version du noyau), et quelque chose n'est pas observé au point d'entrée global ...


Nous pourrions continuer à rechercher un point commun qui sélectionne à travers quelles instructions les appels système font sur cette machine ( int 0x80 / sysenter / ...) - ou peut-être qu'il n'existe pas du tout - laissons cela au lecteur. Faisons mieux attention au mécanisme seccomp , qui fournit une autre interface standard pour filtrer les appels système.


Seccomp est synonyme d'informatique sécurisée et est destiné à gérer du code peu fiable. Citation de l' 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. 

Eh bien, après que Meltdown & Co ait déjà peur de lancer JavaScript dans plusieurs threads - je ne sais pas comment c'est avec du code binaire non fiable. Cependant, la sécurité dans un environnement partiellement sécurisé est un problème distinct, et je peux seulement dire que bien que seccomp puisse fonctionner, par exemple, au niveau des flux individuels, je n'oserais naturellement pas rester dans le même espace d'adressage avec un code machine non fiable en cours d'exécution. En général, la "préparation" sécurisée des logiciels malveillants n'est pas non plus le sujet de cet article.


Dans ce cas, je suis intéressé à intercepter les appels système plutôt du point de vue de l'instrumentation. Heureusement, en plus du mode SECCOMP_SET_MODE_STRICT , forçant le processus à se terminer lors de tout appel sauf read , write , _exit et sigreturn , il existe SECCOMP_SET_MODE_FILTER - il vous permet de spécifier un programme BPF qui filtrera uniquement les appels système d'intérêt, et pour les filtrés de décider quoi faire. Ici, il y a déjà beaucoup plus de choix, mais je suis maintenant intéressé par SECCOMP_RET_TRAP : lorsque ce verdict est SIGSYS , SIGSYS envoyé à un thread spécifique, c'est exactement ce dont vous avez besoin: vous pouvez traiter de manière synchrone la demande et retourner le contrôle comme si l'appel système s'était produit normalement.


La préparation


Pour notre gestionnaire, créez le fichier 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(); } 

Décrivons la fonction qui traite les appels système - c'est assez clair: la fonction prend le numéro de l'appel système, (maximum) six de ses arguments, et le signe (retourné) que l'appel système a été non seulement pris en compte, mais émulé, et qu'il n'a vraiment pas besoin d'être exécuté. Le reste est encore plus simple: la fonction initialize_handler configurera la répartition de tout cela.


Et quelle est cette mystérieuse fonction de constr ? Il appelle uniquement initialize_handler , mais comme il est marqué avec l'attribut constructor , il sera appelé lors de l'initialisation de la bibliothèque (ou du fichier exécutable principal) contenant cette fonction. Si nous voulons activer l'interception après le démarrage du programme, nous pouvons définir constr et appeler manuellement initialize_handler .


Écrire un programme BPF


Vous devez maintenant écrire un bytecode BPF qui répondra simplement aux numéros d'appel système souhaités en émettant SECCOMP_RET_TRAP . La documentation pour le format du programme se trouve, en particulier, dans les sources du noyau dans Documentation / networking / filter.txt , tandis que pour seccomp vous devez regarder l'ancienne version, pas eBPF. Les fichiers linux / seccomp.h , linux / filter.h eux-mêmes, ainsi que le fichier linux / bpf_common.h auquel il fait référence, seront également très utiles. Si vous récupérez ces sources dans un tas, un exemple de man seccomp et shake, vous obtenez quelque chose comme ceci:


 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), }; 

Depuis que j'ai écrit cela il y a quelques semaines, et même alors, c'était un code d'un exemple, puis selon l'ancienne tradition disons que c'est évident laisser le lecteur comme exercice. En principe, les opcodes ( LD / JMP avec décalages 0 et 1 / RET ), les conditions ( JEQ ), les instructions d'utilisation de l'opérande immédiat ( BPF_K ) sont clairement visibles ici - voir filter.txt pour plus de détails . Et les macros vous permettent de ne pas penser à tout emballer dans les mots des équipes.


Pourquoi avons-nous besoin de la constante MARKER ? C'est une telle béquille qui vous permettra toujours d'ignorer l'appel système si notre filtre d'espace utilisateur le décide. Sloppy, béquillé - mais je n'ai pas promis de montrer une solution universelle. Cependant, la solution présentée également dans la plupart des cas fonctionne assez bien, tout en étant banale.


Nous plaçons ce même MARKER dans le sixième argument de l'appel système dans l'espoir qu'il s'agit d'un «détail superflu» et que les six arguments sont rarement utilisés. Nous le soustrayons en utilisant la toute première instruction de notre programme BPF: d'ici, c'est une expression mystérieuse - offsetof(struct seccomp_data, args[5]) - le noyau nous transmet la structure seccomp_data (voir man seccomp ):


 struct seccomp_data { int nr; /* System call number */ __u32 arch; /* AUDIT_ARCH_* value (see <linux/audit.h>) */ __u64 instruction_pointer; /* CPU instruction pointer */ __u64 args[6]; /* Up to 6 system call arguments */ }; 

... et on en soustrait une variable à l'offset requis: offsetof(struct seccomp_data, args[5]) . Ce paragraphe est parrainé par aol-nnov


Il est utilisé en quelque sorte comme ceci:


 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) { //  BPF-  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), }; struct sock_fprog prog = { sizeof(filt) / sizeof(filt[0]), filt }; //  userspace- struct sigaction sig; memset(&sig, 0, sizeof(sig)); sig.sa_sigaction = handle_sigsys; sig.sa_flags = SA_SIGINFO; sigaction(SIGSYS, &sig, NULL); // ,   prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog); } 

handle_sigsys est un gestionnaire de signaux de nouveau style, pour ainsi dire, utilisé en conjonction avec la fonction sigaction . À propos de son troisième paramètre, l' man sigaction dit:


  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. 

Eh bien, nous n'avons pas de cas très courant, donc pour un changement nous n'utiliserons pas le deuxième argument - il y a aussi beaucoup de choses intéressantes, mais il n'y a pas d'arguments pour l'appel système. Ici les danses avec un tambourin commencent, et le tambourin est nécessaire à partir de la bonne architecture. Il est tout à fait possible que libseccomp incostue avec succès ces détails architecturaux et produise simplement un tableau d'arguments - et si ce n'est pas le cas maintenant, ce sera probablement plus tard - mais puisque nous avons un article d'introduction, nous supposerons qu'un niveau aussi bas n'est pas un bug , et une fonctionnalité ... Par conséquent, vérifiez simplement la deuxième table de man syscall pour l'architecture requise, dans mon cas x86_64.


Dans le même gestionnaire, nous voyons une autre fonction amusante: syscall - en fait, c'est d'elle que nous venons de lire l'homme. Elle obtient simplement le numéro d'appel système et les arguments, et elle l'exécute. Sur certaines architectures, il existe toutes sortes d'ABI délicats avec un "alignement" de valeurs 64 bits dans les registres 32 bits (tout est lu dans la même page de manuel) - espérons que cela ne soit pas pertinent sur x86_64, sinon nous aurions à l'analyser en fonction du nombre appel en soustrayant les registres, car syscall() cache déjà cette logique en soi. En plus des cas étranges comme ici, lorsque le numéro d'appel est déterminé dynamiquement, il peut être utilisé si, pour une raison quelconque, nous n'avons pas de libc-wrapper pour un appel système. Par exemple, c'est exactement comment j'appelle seccomp dans la fonction suivante:


  syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog); 

Cet étrange prctl nécessaire pour des raisons de sécurité: dans man seccomp y a un exemple où, sans cette exigence, vous pourriez mettre le malheureux binaire set-user-ID en réalité virtuelle, dans lequel setuid retournera zéro, mais ne fera rien, ce qui sera très dangereux du point de vue vue d'élévation de privilège.


Nous compilons et utilisons notre bibliothèque:


 $ 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 

Comme vous pouvez le voir, au moins sur mon système, vous devez spécifier le chemin d'accès à la bibliothèque. Bien que relatif, mais le chemin. Il est bon que le chargeur de démarrage vous avertisse de l'erreur. Dans le second cas, tout semble fonctionner.


DĂ©bogage


Comment déboguer les appels système? Vous pouvez, bien sûr, catch syscall dans gdb, mais dans ce cas (comme dans la plupart des autres, pour être honnête), le merveilleux outil strace nous aidera:


 strace -E LD_PRELOAD=./syscall-handler.so ls 

Le paramètre -E indique que vous devez définir la variable spécifiée pour le processus à l'étude. Sinon, il serait nécessaire de charger la bibliothèque déboguée dans le débogueur lui-même - une perspective moyenne.


Strace de sortie
 $ 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(); } 

Source: https://habr.com/ru/post/fr469267/


All Articles