Π Unix-ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΎΠ½Π½ΡΡ ΡΠΈΡΡΠ΅ΠΌΠ°Ρ ΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ Ρ Π²Π½Π΅ΡΠ½ΠΈΠΌ ΠΌΠΈΡΠΎΠΌ ΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠΉ ΡΠΈΡΡΠ΅ΠΌΠΎΠΉ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΡΠ΅ΡΠ΅Π· Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠΉ Π½Π°Π±ΠΎΡ ΡΡΠ½ΠΊΡΠΈΠΉ β ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ². Π Π·Π½Π°ΡΠΈΡ, Π² ΠΎΡΠ»Π°Π΄ΠΎΡΠ½ΡΡ ΡΠ΅Π»ΡΡ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ Π±ΡΠ²Π°Π΅Ρ ΠΏΠΎΠ΄ΡΠΌΠΎΡΡΠ΅ΡΡ Π·Π° Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΡΠΌΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠ°ΠΌΠΈ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΌΠΈ Π²ΡΠ·ΠΎΠ²Π°ΠΌΠΈ.
Π‘Π»Π΅Π΄ΠΈΡΡ Π·Π° Β«ΠΈΠ½ΡΠΈΠΌΠ½ΠΎΠΉ ΠΆΠΈΠ·Π½ΡΡΒ» ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌ Π½Π° Linux ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ ΡΡΠΈΠ»ΠΈΡΠ° strace
, ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΈ ΠΏΠΎΡΠ²ΡΡΠ΅Π½Π° ΡΡΠ° ΡΡΠ°ΡΡΡ. Π ΠΏΡΠΈΠΌΠ΅ΡΠ°ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Β«ΡΠΏΠΈΠΎΠ½ΡΠΊΠΎΠ³ΠΎΒ» ΠΎΠ±ΠΎΡΡΠ΄ΠΎΠ²Π°Π½ΠΈΡ ΠΏΡΠΈΠ»Π°Π³Π°ΡΡΡΡ ΠΊΡΠ°ΡΠΊΠ°Ρ ΠΈΡΡΠΎΡΠΈΡ strace
ΠΈ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΡΡΡΡΠΎΠΉΡΡΠ²Π° ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΡ
ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌ.
Π‘ΠΎΠ΄Π΅ΡΠΆΠ°Π½ΠΈΠ΅
ΠΡΠΎΠΈΡΡ ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΎΠ² Π£ΡΡΡΠΎΠΉΡΡΠ²ΠΎ strace Π² Π΄Π²ΡΡ ΡΠ»ΠΎΠ²Π°Ρ : Piglet Trace ΠΠ·Ρ: Π·Π°ΠΏΡΡΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΏΠΎΠ΄ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace ΠΠ·Ρ: ΠΏΡΠΈΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ ΠΊ ΠΏΡΠΎΡΠ΅ΡΡΡ Π½Π° Π»Π΅ΡΡ ΠΡΠΈΠΌΠ΅Ρ: ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΡ ΠΏΡΠΎΡΠ΅ΡΡΠΎΠ² ΠΡΠΈΠΌΠ΅Ρ: ΠΏΡΡΠΈ ΠΊ ΡΠ°ΠΉΠ»Π°ΠΌ Π²ΠΌΠ΅ΡΡΠΎ Π΄Π΅ΡΠΊΡΠΈΠΏΡΠΎΡΠΎΠ² ΠΡΠΈΠΌΠ΅Ρ: ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΎΠ±ΡΠ°ΡΠ΅Π½ΠΈΠΉ ΠΊ ΡΠ°ΠΉΠ»Π°ΠΌ ΠΡΠΈΠΌΠ΅Ρ: ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡΠΎΡΠ½ΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΠ°ΡΡΠ΅Ρ-ΠΊΠ»Π°ΡΡ: ΡΡΠ΅ΠΊ ΠΏΡΠΎΡΠ΅ΡΡΠ° Π² ΠΌΠΎΠΌΠ΅Π½Ρ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π° ΠΠ°ΡΡΠ΅Ρ-ΠΊΠ»Π°ΡΡ: ΠΈΠ½ΡΠ΅ΠΊΡΠΈΡ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΠΎΡΠ»Π΅ΡΠ»ΠΎΠ²ΠΈΠ΅
ΠΡΠΎΠΈΡΡ ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΎΠ²
ΠΠ»Π°Π²Π½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ°ΠΌΠΈ ΠΈ ΡΠ΄ΡΠΎΠΌ OC Π² Unix β ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ Π²ΡΠ·ΠΎΠ²Ρ (Π°Π½Π³Π». system calls, syscalls), Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌ Ρ Π²Π½Π΅ΡΠ½ΠΈΠΌ ΠΌΠΈΡΠΎΠΌ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΠΈΡΠΊΠ»ΡΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΡΠ΅ΡΠ΅Π· Π½ΠΈΡ .
ΠΠΎ Π² ΠΏΠ΅ΡΠ²ΠΎΠΉ ΠΏΡΠ±Π»ΠΈΡΠ½ΠΎΠΉ Π²Π΅ΡΡΠΈΠΈ Unix (ptrace
.
Π Π°Π·ΡΠ°Π±Π°ΡΡΠ²Π°Π»ΡΡ ptrace ΠΏΡΠ΅ΠΆΠ΄Π΅ Π²ΡΠ΅Π³ΠΎ Π΄Π»Ρ ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΡ
ΠΎΡΠ»Π°Π΄ΡΠΈΠΊΠΎΠ², Π½ΠΎ ΠΊ ΠΊΠΎΠ½ΡΡ 80-Ρ
(Π² ΡΠΏΠΎΡ
Ρ ΠΊΠΎΠΌΠΌΠ΅ΡΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΡΠΆΠ΅
trace
ΠΎΡ Sun. ΠΠ°ΠΊ ΠΊΠ»ΠΎΠ½, ΡΠ°ΠΊ ΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π» ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ°Π»ΠΈΡΡ Π΄Π»Ρ SunOS, Π½ΠΎ ΠΊ 1994 Π³ΠΎΠ΄Ρ strace
Π±ΡΠ»Π° ΠΏΠΎΡΡΠΈΡΠΎΠ²Π°Π½Π° Π½Π° System V, Solaris ΠΈ Π½Π°Π±ΠΈΡΠ°ΡΡΠΈΠΉ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΎΡΡΡ Linux.
Π‘Π΅Π³ΠΎΠ΄Π½Ρ strace ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ ΡΠΎΠ»ΡΠΊΠΎ Linux ΠΈ ΠΎΠΏΠΈΡΠ°Π΅ΡΡΡ Π½Π° Π²ΡΡ ΡΠΎΡ ΠΆΠ΅ ptrace
, ΠΎΠ±ΡΠΎΡΡΠΈΠΉ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎΠΌ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΠΉ.
Π‘ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΉ (ΠΈ Π²Π΅ΡΡΠΌΠ° Π°ΠΊΡΠΈΠ²Π½ΡΠΉ) ΠΌΠ΅ΠΉΠ½ΡΠ΅ΠΉΠ½Π΅Ρ strace
β
ΠΠ΅ΠΌΠ°Π»ΠΎΠ²Π°ΠΆΠ½ΠΎ ΠΈ ΡΠΎ, ΡΡΠΎ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² ptrace ΠΈ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊΠΈ ΡΠ°ΠΊ ΠΈ Π½Π΅ Π±ΡΠ»ΠΈ Π²ΠΊΠ»ΡΡΠ΅Π½Ρ Π² POSIX, Π½Π΅ΡΠΌΠΎΡΡΡ Π½Π° Π΄ΠΎΠ»Π³ΡΡ ΠΈΡΡΠΎΡΠΈΡ ΠΈ Π½Π°Π»ΠΈΡΠΈΠ΅ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ Π² Linux, FreeBSD, OpenBSD ΠΈ ΡΡΠ°Π΄ΠΈΡΠΈΠΎΠ½Π½ΡΡ Unix.
Π£ΡΡΡΠΎΠΉΡΡΠ²ΠΎ strace Π² Π΄Π²ΡΡ ΡΠ»ΠΎΠ²Π°Ρ : Piglet Trace
"You are not expected to understand this" (ΠΠ΅Π½Π½ΠΈΡ Π ΠΈΡΠΈ, ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΉ Π² ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠΌ ΠΊΠΎΠ΄Π΅ Version 6 Unix)
Π‘ ΡΠ°Π½Π½Π΅Π³ΠΎ Π΄Π΅ΡΡΡΠ²Π° Ρ ΡΠ΅ΡΠΏΠ΅ΡΡ Π½Π΅ ΠΌΠΎΠ³Ρ ΡΡΡΠ½ΡΠ΅ ΡΡΠΈΠΊΠΈ: Ρ ΠΈΠ³ΡΡΡΠΊΠ°ΠΌΠΈ Ρ Π½Π΅ ΠΈΠ³ΡΠ°Π», Π° ΠΏΡΡΠ°Π»ΡΡ ΡΠ°Π·ΠΎΠ±ΡΠ°ΡΡΡΡ Π² ΠΈΡ ΡΡΡΡΠΎΠΉΡΡΠ²Π΅ (Π²Π·ΡΠΎΡΠ»ΡΠ΅ ΡΠΏΠΎΡΡΠ΅Π±Π»ΡΠ»ΠΈ ΡΠ»ΠΎΠ²ΠΎ Β«Π»ΠΎΠΌΠ°Π»Β», Π½ΠΎ Π½Π΅ Π²Π΅ΡΡΡΠ΅ Π·Π»ΡΠΌ ΡΠ·ΡΠΊΠ°ΠΌ). ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΠΏΠΎΡΡΠΎΠΌΡ ΠΌΠ½Π΅ ΡΠ°ΠΊ Π±Π»ΠΈΠ·ΠΊΠΈ Π½Π΅ΡΠΎΡΠΌΠ°Π»ΡΠ½Π°Ρ ΠΊΡΠ»ΡΡΡΡΠ° ΠΏΠ΅ΡΠ²ΡΡ Unix ΠΈ ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠ³ΠΎ open-source-Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ.
Π ΡΠ°ΠΌΠΊΠ°Ρ
ΡΡΠΎΠΉ ΡΡΠ°ΡΡΠΈ ΡΠ°Π·Π±ΠΈΡΠ°ΡΡ ΠΈΡΡ
ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄ ΡΠ°Π·Π΄ΠΎΠ±ΡΠ΅Π²ΡΠ΅ΠΉ Π·Π° Π΄Π΅ΡΡΡΠΈΠ»Π΅ΡΠΈΡ strace Π½Π΅ΡΠ°Π·ΡΠΌΠ½ΠΎ. ΠΠΎ ΠΈ ΡΠ°ΠΉΠ½ Π΄Π»Ρ ΡΠΈΡΠ°ΡΠ΅Π»Π΅ΠΉ ΠΎΡΡΠ°Π²Π°ΡΡΡΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ. ΠΠΎΡΡΠΎΠΌΡ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠΊΠ°Π·Π°ΡΡ ΠΏΡΠΈΠ½ΡΠΈΠΏ ΡΠ°Π±ΠΎΡΡ ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΡ
strace ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌ, Ρ ΠΏΡΠΈΠ²Π΅Π΄Ρ ΠΊΠΎΠ΄ ΠΌΠΈΠ½ΠΈΠ°ΡΡΡΠ½ΠΎΠ³ΠΎ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊΠ° β
$ gcc examples/piglet-trace.c -o ptr
$ ptr echo test > /dev/null
BRK(12) -> 94744690540544
ACCESS(21) -> 18446744073709551614
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694657216512
CLOSE(3) -> 0
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
READ(0) -> 832
FSTAT(5) -> 0
MMAP(9) -> 140694657208320
MMAP(9) -> 140694650953728
MPROTECT(10) -> 0
MMAP(9) -> 140694655045632
MMAP(9) -> 140694655070208
CLOSE(3) -> 0
unknown(158) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MUNMAP(11) -> 0
BRK(12) -> 94744690540544
BRK(12) -> 94744690675712
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694646390784
CLOSE(3) -> 0
FSTAT(5) -> 0
IOCTL(16) -> 18446744073709551591
WRITE(1) -> 5
CLOSE(3) -> 0
CLOSE(3) -> 0
unknown(231)
Tracee terminated
Piglet Trace ΡΠ°ΡΠΏΠΎΠ·Π½Π°Π΅Ρ ΠΏΠΎΡΡΠ΄ΠΊΠ° ΡΠΎΡΠ½ΠΈ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ
Π²ΡΠ·ΠΎΠ²ΠΎΠ² Linux (ΡΠΌ.
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠ°Π·Π±Π΅ΡΡΠΌ ΡΠ°Π±ΠΎΡΡ Π½Π°ΡΠ΅Π³ΠΎ ΠΊΠ»ΠΎΠ½Π°. Π ΡΠ»ΡΡΠ°Π΅ Ρ Linux Π΄Π»Ρ ΠΎΡΠ»Π°Π΄ΡΠΈΠΊΠΎΠ² ΠΈ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ, ΠΊΠ°ΠΊ ΡΠΏΠΎΠΌΠΈΠ½Π°Π»ΠΎΡΡ Π²ΡΡΠ΅, ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² ptrace. ΠΠ½ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²ΠΎΠΌ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ Π² ΠΏΠ΅ΡΠ²ΠΎΠΌ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠ΅ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΎΠ² ΠΊΠΎΠΌΠ°Π½Π΄, ΠΈΠ· ΠΊΠΎΡΠΎΡΡΡ
Π½Π°ΠΌ Π½ΡΠΆΠ½Ρ ΡΠΎΠ»ΡΠΊΠΎ PTRACE_TRACEME
, PTRACE_SYSCALL
ΠΈ PTRACE_GETREGS
.
Π Π°Π±ΠΎΡΠ° ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊΠ° Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ Π² ΠΎΠ±ΡΡΠ½ΠΎΠΌ Unix-ΡΡΠΈΠ»Π΅: fork(2)
Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ, Π° ΡΠΎΡ Π² ΡΠ²ΠΎΡ ΠΎΡΠ΅ΡΠ΅Π΄Ρ Ρ ΠΏΠΎΠΌΠΎΡΡΡ exec(3)
Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ ΠΈΡΡΠ»Π΅Π΄ΡΠ΅ΠΌΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ. ΠΠ΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½Π°Ρ ΡΠΎΠ½ΠΊΠΎΡΡΡ Π·Π΄Π΅ΡΡ β Π²ΡΠ·ΠΎΠ² ptrace(PTRACE_TRACEME)
ΠΏΠ΅ΡΠ΅Π΄ exec
: ΠΏΡΠΎΡΠ΅ΡΡ-ΠΏΠΎΡΠΎΠΌΠΎΠΊ ΠΎΠΆΠΈΠ΄Π°Π΅Ρ, ΡΡΠΎ ΠΏΡΠΎΡΠ΅ΡΡ-ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ Π±ΡΠ΄Π΅Ρ Π΅Π³ΠΎ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΡΡ:
pid_t child_pid = fork();
switch (child_pid) {
case -1:
err(EXIT_FAILURE, "fork");
case 0:
/* Child here */
/* A traced mode has to be enabled. A parent will have to wait(2) for it
* to happen. */
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
/* Replace itself with a program to be run. */
execvp(argv[1], argv + 1);
err(EXIT_FAILURE, "exec");
}
ΠΡΠΎΡΠ΅ΡΡ-ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ ΡΠ΅ΠΏΠ΅ΡΡ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΡΠ·Π²Π°ΡΡ wait(2)
Π² Π΄ΠΎΡΠ΅ΡΠ½Π΅ΠΌ ΠΏΡΠΎΡΠ΅ΡΡΠ΅, ΡΠΎ Π΅ΡΡΡ ΡΠ±Π΅Π΄ΠΈΡΡΡΡ, ΡΡΠΎ ΠΏΠ΅ΡΠ΅ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π² ΡΠ΅ΠΆΠΈΠΌ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠΈ ΠΏΡΠΎΠΈΠ·ΠΎΡΠ»ΠΎ:
/* Parent */
/* First we wait for the child to set the traced mode (see
* ptrace(PTRACE_TRACEME) above) */
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "traceme -> waitpid");
ΠΠ° ΡΡΠΎΠΌ ΠΏΡΠΈΠ³ΠΎΡΠΎΠ²Π»Π΅Π½ΠΈΡ Π·Π°ΠΊΠΎΠ½ΡΠ΅Π½Ρ ΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠΈΡΡΡΠΏΠ°ΡΡ Π½Π΅ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²Π΅Π½Π½ΠΎ ΠΊ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ² Π² Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠΌ ΡΠΈΠΊΠ»Π΅.
ΠΡΠ·ΠΎΠ² ptrace(PTRACE_SYSCALL)
Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ, ΡΡΠΎ ΠΏΠΎΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ wait
ΡΠΎΠ΄ΠΈΡΠ΅Π»Ρ Π·Π°Π²Π΅ΡΡΠΈΡΡΡ Π»ΠΈΠ±ΠΎ ΠΏΠ΅ΡΠ΅Π΄ ΠΈΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ΠΌ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π°, Π»ΠΈΠ±ΠΎ ΡΡΠ°Π·Ρ ΠΏΠΎΡΠ»Π΅ Π΅Π³ΠΎ Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΡ. ΠΠ΅ΠΆΠ΄Ρ Π΄Π²ΡΠΌΡ Π²ΡΠ·ΠΎΠ²Π°ΠΌΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΡΡΠ΅ΡΡΠ²ΠΈΡΡ ΠΊΠ°ΠΊΠΈΠ΅-Π»ΠΈΠ±ΠΎ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ: Π·Π°ΠΌΠ΅Π½ΠΈΡΡ Π²ΡΠ·ΠΎΠ² Π½Π° Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π½ΡΠΉ, ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ ΠΈΠ»ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅.
ΠΠ°ΠΌ ΠΆΠ΅ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π΄Π²Π°ΠΆΠ΄Ρ Π²ΡΠ·Π²Π°ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ptrace(PTRACE_GETREGS)
, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠ΅Π³ΠΈΡΡΡΠ° rax
Π΄ΠΎ Π²ΡΠ·ΠΎΠ²Π° (Π½ΠΎΠΌΠ΅Ρ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π°) ΠΈ ΡΡΠ°Π·Ρ ΠΏΠΎΡΠ»Π΅ (Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅).
Π‘ΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎ, ΡΠΈΠΊΠ»:
/* A system call tracing loop, one interation per call. */
for (;;) {
/* A non-portable structure defined for ptrace/GDB/strace usage mostly.
* It allows to conveniently dump and access register state using
* ptrace. */
struct user_regs_struct registers;
/* Enter syscall: continue execution until the next system call
* beginning. Stop right before syscall.
*
* It's possible to change the system call number, system call
* arguments, return value or even avoid executing the system call
* completely. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "enter_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "enter_syscall -> waitpid");
/* According to the x86-64 system call convention on Linux (see man 2
* syscall) the number identifying a syscall should be put into the rax
* general purpose register, with the rest of the arguments residing in
* other general purpose registers (rdi,rsi, rdx, r10, r8, r9). */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1)
err(EXIT_FAILURE, "enter_syscall -> getregs");
/* Note how orig_rax is used here. That's because on x86-64 rax is used
* both for executing a syscall, and returning a value from it. To
* differentiate between the cases both rax and orig_rax are updated on
* syscall entry/exit, and only rax is updated on exit. */
print_syscall_enter(registers.orig_rax);
/* Exit syscall: execute of the syscall, and stop on system
* call exit.
*
* More system call tinkering possible: change the return value, record
* time it took to finish the system call, etc. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "exit_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "exit_syscall -> waitpid");
/* Retrieve register state again as we want to inspect system call
* return value. */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1) {
/* ESRCH is returned when a child terminates using a syscall and no
* return value is possible, e.g. as a result of exit(2). */
if (errno == ESRCH) {
fprintf(stderr, "nTracee terminatedn");
break;
}
err(EXIT_FAILURE, "exit_syscall -> getregs");
}
/* Done with this system call, let the next iteration handle the next
* one */
print_syscall_exit(registers.rax);
}
ΠΠΎΡ ΠΈ Π²Π΅ΡΡ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊ. Π’Π΅ΠΏΠ΅ΡΡ Π²Ρ Π·Π½Π°Π΅ΡΠ΅, Ρ ΡΠ΅Π³ΠΎ Π½Π°ΡΠΈΠ½Π°ΡΡ ΠΎΡΠ΅ΡΠ΅Π΄Π½ΠΎΠ΅ ΠΏΠΎΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
ΠΠ·Ρ: Π·Π°ΠΏΡΡΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΏΠΎΠ΄ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace
Π ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ strace
, ΠΏΠΎΠΆΠ°Π»ΡΠΉ, ΡΡΠΎΠΈΡ ΠΏΡΠΈΠ²Π΅ΡΡΠΈ ΡΠ°ΠΌΡΠΉ ΠΏΡΠΎΡΡΠΎΠΉ ΡΠΏΠΎΡΠΎΠ± β Π·Π°ΠΏΡΡΠΊ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΏΠΎΠ΄ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace
.
Π§ΡΠΎΠ±Ρ Π½Π΅ ΠΊΠΎΠΏΠ°ΡΡΡΡ Π² Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠΌ ΡΠΏΠΈΡΠΊΠ΅ Π²ΡΠ·ΠΎΠ²ΠΎΠ² ΡΠΈΠΏΠΈΡΠ½ΠΎΠΉ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ, Π½Π°ΠΏΠΈΡΠ΅ΠΌ write
:
int main(int argc, char *argv[])
{
char str[] = "write me to stdoutn";
/* write(2) is a simple wrapper around a syscall so it should be easy to
* find in the syscall trace. */
if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
perror("write");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Π‘ΠΎΠ±Π΅ΡΠ΅ΠΌ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΈ ΡΠ±Π΅Π΄ΠΈΠΌΡΡ, ΡΡΠΎ ΠΎΠ½Π° ΡΠ°Π±ΠΎΡΠ°Π΅Ρ:
$ gcc examples/write-simple.c -o write-simple
$ ./write-simple
write me to stdout
Π Π½Π°ΠΊΠΎΠ½Π΅Ρ Π·Π°ΠΏΡΡΡΠΈΠΌ Π΅Π΅ ΠΏΠΎΠ΄ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace:
$ strace ./write-simple
pexecve("./write", ["./write"], 0x7ffebd6145b0 /* 71 vars */) = 0
brk(NULL) = 0x55ff5489e000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=197410, ...}) = 0
mmap(NULL, 197410, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7a2a633000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 >