strace est un utilitaire permettant de tracer/suivre les appels systèmes. Comment utiliser strace pour debugguer des applications ou des exécutables ?

strace est un utilitaire permettant de tracer/suivre les appels systèmes. Les appels systèmes sont les interfaces fondamentales entre les applications et le noyau. Généralement, ils ne sont pas appelés directement, mais via des wrappers de la glibc. Par exemples: fstat, mmap, open, close

open("/usr/lib/locale/fr_FR.utf8/LC_MONETARY", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=290, ...}) = 0
mmap(NULL, 290, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f841bde9000
close(3) 

En utilisant strace, on peut intercepter ces appels systèmes pour un processus ou une commande donnée. strace est donc un outil puissant de dépannage pour tous les administrateurs et utilisateurs unix/linux.

Strace utilisation basique

strace commande

root@kali:~# strace ifconfig eth0
execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
brk(0) = 0x1b0f000
...
write(1, " inet adr:192.168.1.14 "..., 75
inet adr:192.168.1.14 Bcast:192.168.1.255 Masque:255.255.255.0
) = 75
open("/proc/net/if_inet6", O_RDONLY) = 6
...
close(5) = 0
exit_group(0)

Appels systèmes dans un fichier

Si vous voulez enregistrer la sortie strace dans un fichier, utilisez: strace -o filename commande

Par exemple:

root@kali:~# strace -o strace.txt ifconfig eth0
eth0
Link encap:Ethernet  HWaddr 00:0c:29:b9:0e:c0  
inet adr:192.168.1.14  Bcast:192.168.1.255  Masque:255.255.255.0
adr inet6: 2a01:e35:2f20:ed90:20c:29ff:feb9:ec0/64 Scope:Global
adr inet6: fe80::20c:29ff:feb9:ec0/64 Scope:Lien
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:17876 errors:0 dropped:0 overruns:0 frame:0
TX packets:10953 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 lg file transmission:1000 
RX bytes:18635399 (17.7 MiB)  TX bytes:980986 (957.9 KiB)

root@kali:~# head -n 2 strace.txt 
execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
brk(0)                                  = 0x1437000

Rapport/sommaire d’appels systèmes

strace -c command permet de faire un rapport des appels systèmes. Par exemple:

root@kali:~# strace -c ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0c:29:b9:0e:c0  
          inet adr:192.168.1.14  Bcast:192.168.1.255  Masque:255.255.255.0
          adr inet6: 2a01:e35:2f20:ed90:20c:29ff:feb9:ec0/64 Scope:Global
          adr inet6: fe80::20c:29ff:feb9:ec0/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17294 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10524 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          RX bytes:18290334 (17.4 MiB)  TX bytes:924666 (902.9 KiB)

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.040000        2857        14        11 access
  0.00    0.000000           0         8           read
  0.00    0.000000           0        10           write
  0.00    0.000000           0        35        14 open
  0.00    0.000000           0        22           close
  0.00    0.000000           0        22           fstat
  0.00    0.000000           0        28           mmap
  0.00    0.000000           0         6           mprotect
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0        11           ioctl
  0.00    0.000000           0         3           socket
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.040000                   169        25 total

Ici, l’appel système open génère 14 erreurs. Comment tracer spécifiquement ces appels systèmes ?

Tracer un appel système spécifique

On utilise l’option -e de strace:

root@kali:~# strace -e open ifconfig eth0
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY) = -1 ENOENT (No such file or directory)

...

open("/usr/lib/x86_64-linux-gnu/gconv/ISO8859-1.so", O_RDONLY) = 6
eth0      Link encap:Ethernet  HWaddr 00:0c:29:b9:0e:c0  
          inet adr:192.168.1.14  Bcast:192.168.1.255  Masque:255.255.255.0
open("/proc/net/if_inet6", O_RDONLY)    = 6
          adr inet6: 2a01:e35:2f20:ed90:20c:29ff:feb9:ec0/64 Scope:Global
          adr inet6: fe80::20c:29ff:feb9:ec0/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17783 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10915 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          RX bytes:18626514 (17.7 MiB)  TX bytes:978630 (955.6 KiB)

On voit clairement que ifconfig essaye d’ouvrir des fichiers absents. Combien ?

root@kali:~# strace -e open ifconfig eth0 2>&1 | grep "No such file or directory" | wc -l
14

Exactement 14 !!! comme l’indiquait le rapport d’appels systèmes.

Tracer plusieurs appels systèmes spécifiques

On peut utiliser l’option -e trace=function1,function2, … Par exemple pour tracer les appels systèmes mprotect ou brk:

root@kali:~# strace -e trace=mprotect,brk ifconfig eth0
brk(0)                                  = 0x1c1f000
mprotect(0x7f400429c000, 2097152, PROT_NONE) = 0
mprotect(0x7f400449c000, 16384, PROT_READ) = 0
mprotect(0x60f000, 4096, PROT_READ)     = 0
mprotect(0x7f40046c5000, 4096, PROT_READ) = 0
brk(0)                                  = 0x1c1f000
brk(0x1c40000)                          = 0x1c40000
mprotect(0x7f4003f1b000, 2093056, PROT_NONE) = 0
mprotect(0x7f400411a000, 4096, PROT_READ) = 0
eth0      Link encap:Ethernet  HWaddr 00:0c:29:b9:0e:c0  
          inet adr:192.168.1.14  Bcast:192.168.1.255  Masque:255.255.255.0
          adr inet6: 2a01:e35:2f20:ed90:20c:29ff:feb9:ec0/64 Scope:Global
          adr inet6: fe80::20c:29ff:feb9:ec0/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17822 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10932 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          RX bytes:18630337 (17.7 MiB)  TX bytes:979684 (956.7 KiB)

Tracer une catégorie spécifique d’appels systèmes

-e trace=file On peut penser qu’il s’agit là  d’une abréviation de -e trace=open,stat,chmod,unlink,…En outre, utiliser cette abréviation permet de s’assurer que l’on n’omet pas d’inclure les appels lstat

-e trace=process Trace tous les appels systèmes liés à  la gestion des processus. C’est particulièrement utile pour suivre les étapes fork, wait et exec d’un process.

-e trace=network Trace tous les appels systèmes de type réseau.

-e trace=signal Trace tous les appels systèmes de type signal.

-e trace=ipc Trace tous les appels systèmes IPC.

-e trace=desc Trace tous les appels systèmes liés aux descripteurs.

Par exemple pour tracer les appels spécifiques au réseau (network):

root@kali:~# strace -e trace=network ifconfig eth0
socket(PF_FILE, SOCK_DGRAM, 0)          = 3
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 5
eth0      Link encap:Ethernet  HWaddr 00:0c:29:b9:0e:c0  
          inet adr:192.168.1.14  Bcast:192.168.1.255  Masque:255.255.255.0
          adr inet6: 2a01:e35:2f20:ed90:20c:29ff:feb9:ec0/64 Scope:Global
          adr inet6: fe80::20c:29ff:feb9:ec0/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17836 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10940 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000 
          RX bytes:18631824 (17.7 MiB)  TX bytes:980182 (957.2 KiB)

Récupérer les horodatages/timestamps et les temps d’appels sytèmes

Il peut àªtre utile de récupérer les temps d’appels systèmes et leur timestamp:

strace -r Timestamp relatif - temps passé pour un appel système strace -t Préfixer chaque ligne par la date du jour strace -tt Préfixer chaque ligne par la date du jour à  la microseconde près strace -ttt Préfixer chaque ligne par la date du jour à  la microseconde près depuis le 1er janvier 1970 (The Epoch)

root@kali:~# strace -r -o strace_r.txt ifconfig eth0 > /dev/null
root@kali:~# strace -t -o strace_t.txt ifconfig eth0 > /dev/null
root@kali:~# strace -tt -o strace_tt.txt ifconfig eth0 > /dev/null
root@kali:~# strace -ttt -o strace_ttt.txt ifconfig eth0 > /dev/null
root@kali:~# ls -rt strace_*
strace_r.txt  strace_t.txt  strace_tt.txt  strace_ttt.txt
root@kali:~# head -n 2 strace_*
==> strace_r.txt <==
     0.000000 execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
     0.000192 brk(0)                    = 0x75a000

==> strace_ttt.txt <==
1399201553.405215 execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
1399201553.405410 brk(0)                = 0x159d000

==> strace_tt.txt <==
07:05:50.173343 execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
07:05:50.173648 brk(0)                  = 0x1398000

==> strace_t.txt <==
07:05:41 execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 34 vars */]) = 0
07:05:41 brk(0) 

Que faire lorsqu’un processus fork

Vous pouvez suivre les appels systèmes, si un processus fork en utilisant l’option -f

root@kali:~# strace -f -o strace_acroread.txt acroread > /dev/null

Attacher à  un processus existant

Pour attacher strace à  un processus existant: strace -p PID

Par exemple:

root@kali:~# pidof sshd
6177
root@kali:~# strace -p 6177
Process 6177 attached - interrupt to quit
select(7, [3 4], NULL, NULL, NULL

Essayons maintenant de nous connecter sur le serveur kali (IP:192.168.1.14):

root@station:~# ssh kali

Maintenant on a quelque chose du type:

root@kali:~# strace -p 6177
Process 6177 attached - interrupt to quit
select(7, [3 4], NULL, NULL, NULL)      = 1 (in [3])
accept(3, {sa_family=AF_INET, sin_port=htons(45468), sin_addr=inet_addr("192.168.1.23")}, [16]) = 5
fcntl(5, F_GETFL)                       = 0x2 (flags O_RDWR)
pipe([6, 7])                            = 0
socketpair(PF_FILE, SOCK_STREAM, 0, [8, 9]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f22b8b91a90) = 12420
close(7)                                = 0
write(8, "\0\0\2\263\0", 5)             = 5
write(8, "\0\0\2\252\n\n\n\nPort 22\n\n\n\nProtocol 2\n\nH"..., 690) = 690
close(8)                                = 0
close(9)                                = 0
close(5)                                = 0
select(7, [3 4 6], NULL, NULL, NULL)    = 1 (in [6])
close(6)                                = 0
select(7, [3 4], NULL, NULL, NULL

Une pair de sockets connectés est créé socketpair(…) avec pour addresse distante 192.168.1.23:45468

En outre, on voit clairement que la connection est établie :

root@kali:~# netstat -at | grep -F '192.168.1.23'
tcp        0      0 192.168.1.14:ssh        192.168.1.23:45468      ESTABLISHED 

J’espère avoir pu vous aider !!! Strace est un outil très puissant, ne pas oublier man strace !!!