Ok j'ai dû chercher, alors si je peux faire gagner un peu de temps à
d'autres âmes perdues comme la mienne! J'avais envie de pouvoir sniffer les i/o
sur n'importe quel process avec des filtres genre sur les file descriptors (ou
FD), sur le type de FD ou sur le nom du process, etc.. histoire de voir dans
les protocoles de communication locaux (genre via socket unix de pute de merde
$@#!$!@#) comment un daemon communique avec son environnement ou comment
(exemple d'OS/X) un programme peut communiquer avec le kernel, oui oui sous
OS/X ça "peut" se passer via une socket() et pas via un /dev/bla, ioctl() etc..
ca existe aussi mais dans mon cas c'était dans mon cul... bref...
Donc j'ai commencé par ecrire un LD_PRELOAD, mais ce n'est pas le sujet, je
me suis amusé avec dtrace(1) pour voir; pas mal d'outils sous OS/X utilisent
dtrace(1), comme "dtruss(1m)" (fourni par default sous OS/X), je vous laisse
chercher pour tous les trouver, mais voila un premier pas:
J'en reviens a mon process, je sais pas ce qu'il fait, j'aimerais voir ses
read()/write(), je passe un coup d'"lsof" (aka
LiSt Open Files) pour voir les FDs (File Descriptors, je me répète et j'en vois
plein... alors je me dis merde, j'aimerais bien voir où il écrit, juste voir ce
qui se passe sur ce FD, etc... je cherche un peu et je tombe sur:
Content, je m'en vais essayer, mais deception...:
# rwsnoop -p 377
UID PID CMD D BYTES FILE
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
501 377 iTerm R 42 ptmx
Beuh il me print pas le contenu du read()/write(), genre un bel hexdump
aurait été bien agréable, ensuite je ne peux pas filtrer par file descriptor
(genre je veux voir juste le #6, je sais ce que c'est $#@!$@#!) et j'aimerais
aussi pouvoir voir (quand ça n'est pas un fichier) le type de socket (vu que le
type n'est pas fourni par la variable interne dtrace "fds"),
histoire de savoir ce qui se passe vraiment et pas croire que c'est une vnode
alors qu'en fait c'est une socket à la noix... bref...
Donc la première étape, c'est de dumper le contenu du read()/write() et le
return code, c'est parti... je vais expliquer rapidement:
syscall::read:entry
/<predicate>/
{
self->fd = arg0;
self->rarg1 = arg1;
self->rarg2 = arg2;
[...]
}
En gros à l'entrée de read() je sauve les arguments, oui je veux afficher
mon code de retour et le read() n'a pas encore été exécuté donc le buffer est
toujours vide... du coup :
syscall::read:return
/<predicate>/
{
buffer = copyin(self->rarg1, arg0);
tracemem(buffer, $opt_dumpsz);
printf(\"\n\");
}
donc la à la fin de l'exec, je prends le code de retour (arg0), je copie
tout ca dans un buffer local (oui oui on est dans l'addressage du noyau là...)
et j'affiche le contenu de la mémoire avec tracemem() tout joli en fonction du type, mais si comme moi vous
préférez TOUJOURS avoir un hexdump, magie, simplicité,
élasticité... on rajoute ça au debut de son script:
#pragma D option rawbytes
voilà là on voit le contenu avec ce format:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
0: 03 4f 42 7e 3b 39 35 3b 63 a8 11 16 01 00 00 00 .OB~;95;c.......
10: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................
20: 36 00 18 00 00 00 00 00 f0 f0 52 00 01 00 00 00 6.........R.....
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
autre galère, retrouver le type de FD, apres avoir
parcouru, diverses choses, je conseille donc aux gens qui se cassent la tête,
de farfouiller dans ces liens:
je finis par aller voir si dans les struct du noyau je peux accéder à la
struct proc (oui elle contient les FDs du process!!), je
resume vite fait et simplement il faut passer par la struct task puis récupérer la struct proc (via bsd_info de la structure task), struct filedesc, blablabla jusqu'à la struct fileglob et on finit par voir que la struct proc est accessible via
curproc dans dtrace(1), apres du déréferencement à la main on arrive à
ça:
curproc->p_fd->fd_ofiles[<FD_num_as_index_of_this_array>]->f_fglob->fg_type
c'est un int avec pour valeurs possibles:
/* file types */
145#define DTYPE_VNODE 1 /* file */
146#define DTYPE_SOCKET 2 /* communications endpoint */
147#define DTYPE_PSXSHM 3 /* POSIX Shared memory */
148#define DTYPE_PSXSEM 4 /* POSIX Semaphores */
149#define DTYPE_KQUEUE 5 /* kqueue */
150#define DTYPE_PIPE 6 /* pipe */
151#define DTYPE_FSEVENTS 7 /* fsevents */
152
And voilà....!! maintenant vous reste plus qu'à faire l'amas de ces
informations et hop vous avez vos filtres dans votre gentil script dtrace(1)! :)
Maintenant répétez les operations pour avoir filename, socket endpoint, en
fonction du type, etc... vous aurez tout ce que vous voulez.
Bon je suis fatigué d'écrire ce truc... j'espère que ça servira à quelqu'un,
je retourne lire mes trucs de glands...