Są to narzędzia do śledzenia wywołań funkcji wewnątrz śledzonego programu oraz sygnałów do niego wysyłanych. W ogólności będziemy badać wykonanie polecenia z linii komend, poprzedzając owe polecenie komendą strace lub ltrace.
Uwaga: W przypadku tych narzędzi do odpluskwiania nie trzeba posiadać źródeł śledzonego programu, ani nie jest wymagana wcześniejsza specjalna kompilacja owych źródeł (np. opcja -g przed użyciem gdb).
Najprościej rzecz ujmując strace śledzi wywołania funkcji systemowych (stąd s- od system), a ltrace funkcji bibliotecznych (l- library).
Po wywołaniu s(l)trace polecenie dostajemy na ekranie (standardowo stderr) lub (przy użyciu opcji -o) wypisane do pliku informacje o nazwie każdej wywołanej funkcji, jej parametrach przy wywołaniu oraz wartości przez nią zwróconą, a w przypadku błędy nazwę symboliczną błędu i jej opis, np.
open(,,/foo/bar'', O_RDONLY) = -1 ENOENT (No such file or directory)Uwaga: Największym mankamentem tego narzędzia jest właśnie ilość informacji, które dostajemy, a problemem odnalezienie tych istotnych.
W przypadku funkcji systemowych najczęściej będziemy mieli do czynienia z funkcjami do czytania, pisanie, otwierania i zamykania plików, sprawdzanie uprawnień, ale także wszelkim innymi funkcjami udostępnionymi przez system (jak choćby obsługa sygnałów sigprocmask) i działającymi w trybie jądra.
Jeśli chodzi o funkcje biblioteczne to mamy to do czynieniu na przykład z funkcjami bibliotecznymi C.
-p pid | dołącz się do procesu (attach) i śledź go aż do przerwania przez CTRL-C |
-e trace=set | śledzenie tylko wyspecyfikowanych w zbiorze set wywołań |
-e signal=set | podobnie jak wyżej, ale śledzimy tylko wybrane sygnały |
-f | śledź także dzieci (powstałe przez fork) procesu (dopiero od momentu kiedy pid dziecka jest znany przez powrót z fork-a) |
Przykład 1:
login@Kubuntu:~$ strace -o plik1 find /etc -name "passwd" /etc/pam.d/passwd find: /etc/ssl/private: Brak dostępu /etc/passwdJak zatem wygląda "plik1"?
execve("/usr/bin/find", ["find", "/etc", "-name", "passwd"], [/* 36 vars */]) = 0 uname({sys="Linux", node="Kubuntu", ...}) = 0 brk(0) = 0x8067000 ... open("etc", O_RDONLY|O_LARGEFILE|O_NOFOLLOW) = 4 fchdir(4) = 0 close(4) = 0 stat64(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("fstab", {st_mode=S_IFREG|0644, st_size=398, ...}) = 0 lstat64("X11", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 open("X11", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 4 fstat64(4, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 fcntl64(4, F_SETFD, FD_CLOEXEC) = 0 getdents64(4, /* 17 entries */, 4096) = 544 getdents64(4, /* 0 entries */, 4096) = 0 close(4) = 0 open("X11", O_RDONLY|O_LARGEFILE|O_NOFOLLOW) = 4 fchdir(4) = 0 close(4) = 0 stat64(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("Xresources", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 open("Xresources", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 4 fstat64(4, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 fcntl64(4, F_SETFD, FD_CLOEXEC) = 0 getdents64(4, /* 2 entries */, 4096) = 48 getdents64(4, /* 0 entries */, 4096) = 0 close(4) = 0 ... itd.Przykład 2:
login@Kubuntu:~$ strace -o plik2 cat /etc/shadow cat: /etc/shadow: Brak dostępu
Jak znaleźć w "plik2" to co naistotniejsze?
(grep -C num -> wypisanie szukanych linii wraz z kontekstem wynoszącym num linii poprzednich i następnych, gdzie ,,--'' rozdziela konteksty)
Przykład 3 (dwa warianty programów liczących kwadraty liczb; odpowiednio test1.c i test2.c):
#include < stdio.h> #include "kwadrat.h" int main() { int i, x, n; scanf("%d",&n); for (i = 0; i < n; i++) { x = kwadrat(i); (* test1 *) x = kwadrat(&i); (* test2 *) printf("%d\n",x); } return 0; }Bardzo nieciekawa funkcja kwadrat:
#include "kwadrat.h" int kwadrat(int *x) { (*x) *= (*x); return *x; }Wykonanie tych prawie poprawnych programów da nam odpowiednio:
login@Kubuntu:~$ ./test1 5 Naruszenie ochrony pamięcioraz...
login@Kubuntu:~$ ./test1 5 0 1 4Strace użyty do prorgamu test1:
login@Kubuntu:~$ strace ./test1 execve("./test1", ["./test1"], [/* 36 vars */]) = 0 uname({sys="Linux", node="Kubuntu", ...}) = 0 ... read(0, 5 "5\n", 1024) = 2 --- SIGSEGV (Segmentation fault) @ 0 (0) --- +++ killed by SIGSEGV +++Gdy ltrace w tym samym przypadku zwróci:
... scanf(0x8048508, 0xbfecce50, 0xbfecce58, 0x8048433, 15 ) = 1 --- SIGSEGV (Segmentation fault) --- +++ killed by SIGSEGV +++Równie mało informacji uzyskamy w przypadku test2:
... read(0, 5 "5\n", 1024) = 2 fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 1), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fc7000 write(1, "0\n", 20 ) = 2 write(1, "1\n", 21 ) = 2 write(1, "4\n", 24 ) = 2 munmap(0xb7fc7000, 4096) = 0 exit_group(0) = ?Oraz ltrace (tym razem w całości):
__libc_start_main(0x8048394, 1, 0xbfa40bd4, 0x8048420, 0x8048471