Tracer les syscalls macOS avec dtrace sur Apple Silicon
Guide pratique pour tracer syscalls BSD et Mach traps avec DTrace sur macOS moderne — prérequis SIP, scripts qui marchent, et que faire quand les probes Apple disparaissent.
DTrace reste l'outil le plus puissant pour tracer les syscalls sur macOS en 2026 — mais c'est aussi un outil qui se défend de plus en plus. SIP verrouille la plupart des probes utiles ; Apple Silicon a cassé des scripts qui supposaient des registres Intel ; et la séparation BSD/Mach impose deux providers différents pour couvrir l'ensemble. Ce guide montre ce qui marche encore et comment l'utiliser.
Prérequis
DTrace livre dans /usr/sbin/dtrace sur tout Mac, mais les probes nécessaires sont verrouillés par System Integrity Protection (SIP) :
- Sans modifier SIP : vous pouvez tracer vos propres processus en lançant
dtraceen root, et utiliser le providersyscallsur tout processus que vous lancez vous-même. - Avec dtrace_unrestrict (recommandé pour la recherche) : démarrez en recovery,
csrutil enable --without dtrace, redémarrez. Vous pouvez désormais tracer n'importe quel processus — y compris les binaires Apple signés.
Sur un Mac de labo où réduire SIP est acceptable, c'est le second mode qu'il vous faut. Sinon, le provider syscall fonctionne encore sur les processus lancés en root.
Tracer tous les syscalls BSD d'un processus
Le script utile le plus simple : chaque syscall BSD, trié par fréquence, pour un PID donné.
#pragma D option quiet
syscall:::entry
/pid == $target/
{
@[probefunc] = count();
}
END {
printa("%-30s %@d\n", @);
}
Sauvegardez en syscall-count.d puis :
sudo dtrace -s syscall-count.d -c "/usr/bin/curl -s https://example.com -o /dev/null"
Vous verrez quelque chose comme :
mmap 23
read 31
stat64 47
write 12
close 19
...
C'est le chemin BSD. Le provider syscall est un fin enrobage de unix_syscall64() dans le noyau — chaque entrée correspond 1:1 à une entrée du catalogue BSD.
Tracer les Mach traps
Les Mach traps ne passent pas par le provider syscall. Pour les voir, il faut le provider mach_trap.
#pragma D option quiet
mach_trap:::entry
/pid == $target/
{
@[probefunc] = count();
}
END {
printa("%-30s %@d\n", @);
}
Lancement identique. Vous verrez typiquement :
mach_msg2_trap 1432
mach_reply_port 7
task_self_trap 3
thread_self_trap 18
mk_timer_arm_leeway_trap 24
C'est le côté Mach — chaque entrée mappe vers une page de référence Mach trap. mach_msg2_trap domine le compte car presque toute forme de communication inter-processus sur macOS — XPC, distributed objects, IOKit, même la livraison d'événements AppKit — finit dans un message Mach.
Tracer un syscall précis avec ses arguments
C'est là que DTrace mérite sa place face à strace/fs_usage. Les probes ont des arguments typés inspectables inline :
#pragma D option quiet
syscall::open*:entry
{
printf("[%d] %s\n", pid, copyinstr(arg0));
}
sudo dtrace -s open-trace.d
Chaque open() et openat() system-wide, avec le chemin résolu au moment du probe. Utile pour trouver des chemins en dur dans des binaires opaques, voir les chargements de configuration, ou construire une règle EDR sensible au contenu.
Les registres arg0–arg7 sont les arguments du syscall dans l'ordre du prototype C. Pour open c'est (path, oflag, mode), donc arg0 est le pointeur sur le chemin.
Surveiller les appels à task_for_pid
Pour le travail de sécurité, le Mach trap le plus intéressant est task_for_pid. DTrace montre chaque appel réussi et raté system-wide :
#pragma D option quiet
mach_trap::task_for_pid:entry
{
self->target_pid = arg1;
self->caller = execname;
}
mach_trap::task_for_pid:return
/self->target_pid/
{
printf("[%-15s pid=%-5d] task_for_pid(%d) -> %d\n",
self->caller, pid, self->target_pid, arg1);
self->target_pid = 0;
self->caller = 0;
}
Vous verrez des usages légitimes (débogueur Xcode, Activity Monitor) et tout ce qui sort du normal (un processus utilisateur qui essaie de chopper launchd ou WindowServer). Sur un système propre, ce script est très silencieux.
Pourquoi les probes Apple disparaissent parfois
Trois pièges récurrents :
-
SIP bloque les probes sur les binaires Apple signés.
dtrace -p $(pgrep Safari)ne retournera rien — Safari est signé et AMFI refuse l'attache. Le contournementcsrutil enable --without dtraceest la seule issue pour les binaires à entitlements restreints. -
syscall::entryne voit pas les retoursnosys. Quand un slot syscall a été retiré (ou qu'un numéro non implémenté est appelé), le providersyscallenregistre l'entrée maisarg0du retour estENOSYSet l'appel est dispatché vers un no-op. Vérifiez la valeur de retour si un compte semble étrangement haut. -
fbt (Function Boundary Tracing) est désactivé sur Apple Silicon. Sur macOS Intel,
fbt:::entrypouvait sonder n'importe quelle fonction noyau. Sur arm64 c'est désactivé. Vous êtes limité aux providers bien connus (syscall,mach_trap,proc,sched,io).
Alternatives quand DTrace ne suffit pas
Quand DTrace refuse — binaires restreints, événements de plus haut niveau — Endpoint Security (ES) est le chemin moderne. ES n'est pas un traceur de syscalls, mais il expose ~80 événements curatés : ES_EVENT_TYPE_NOTIFY_OPEN, ES_EVENT_TYPE_NOTIFY_EXEC, ES_EVENT_TYPE_NOTIFY_GET_TASK, etc. La plupart correspondent à un syscall précis.
Le post suivant de cette série, Détecter l'abus de syscalls avec macOS Endpoint Security en 2026, parcourt la cartographie pratique entre syscalls et événements ES pour le travail de sécurité.
Pour aller plus loin
- Le catalogue de référence donne l'historique par version et les prototypes qui se marient avec votre sortie DTrace.
- Comment fonctionnent les syscalls macOS en 2026 explique le côté noyau de ce que
syscall:::entryobserve réellement. - Le plongeon
task_for_pidest le compagnon long format de l'exemple DTrace ci-dessus.