/* * ptrace-demo.c * - Doprovodny program k clanku "Napiste si debugger" * * Michal Ludvig <michal@logix.cz> (c) 2003 * Homepage: http://www.logix.cz/michal/devel/ptrace-demo * * This code is public domain. Use it as you want to, * but don't blame me if something doesn't work as you * expect. */ #define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/user.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/ptrace.h> #define offsetof(STRUCT,MEMBER) ((long)&((STRUCT *)0)->MEMBER) #if defined(__amd64__) #define PC rip #elif defined(__i386__) #define PC eip #else #error "Kompilujte pouze na platforme i386 nebo amd64!" #endif /**** Zde je potomek ****/ long var; void set_var (long value) { var = value; printf ("Hodnota nastavena na %ld\n", var); } void print_var () { printf ("Hodnota proměnné var je %ld\n", var); } void child () { printf ("(child) Zastavuji potomka\n"); ptrace (PTRACE_TRACEME, 0, NULL, NULL); kill (getpid (), SIGSTOP); printf ("(child) Potomek pokracuje\n"); set_var (10); printf ("(child) Hodnota nastavena\n"); print_var (); printf ("(child) Konec\n"); } /**** Funkce rodice volajici ptrace(2) na potomka ****/ int set_text_byte (pid_t child_pid, void *address, char *insn_byte) { char saved_byte; union { long insn_long; char insn_char[sizeof (long)]; } insn; insn.insn_long = ptrace (PTRACE_PEEKTEXT, child_pid, address, NULL); if (insn.insn_long == -1 && errno) { perror ("ptrace(PTRACE_PEEKTEXT)"); return -1; } saved_byte = insn.insn_char[0]; insn.insn_char[0] = *insn_byte; *insn_byte = saved_byte; if (ptrace (PTRACE_POKETEXT, child_pid, address, insn.insn_long) < 0) { perror ("ptrace(PTRACE_POKETEXT)"); return -1; } return 0; } int set_data_long (pid_t child_pid, void *address, long *new_data) { long saved_long; saved_long = ptrace (PTRACE_PEEKDATA, child_pid, address, NULL); if (saved_long == -1 && errno) { perror ("ptrace(PTRACE_PEEKDATA)"); return -1; } if (ptrace (PTRACE_POKEDATA, child_pid, address, *new_data) < 0) { perror ("ptrace(PTRACE_POKEDATA)"); return -1; } *new_data = saved_long; return 0; } int read_register (pid_t child_pid, long offset, long *data) { long ret; ret = ptrace (PTRACE_PEEKUSER, child_pid, offset, NULL); if (ret == -1 && errno) { perror ("perror(PTRACE_PEEKUSER)"); return -1; } *data = ret; return 0; } int write_register (pid_t child_pid, long offset, long data) { return ptrace (PTRACE_POKEUSER, child_pid, offset, data); } int decrement_pc (pid_t child_pid) { long pc; if (read_register (child_pid, offsetof (struct user, regs.PC), &pc) < 0) return -1; if (write_register (child_pid, offsetof (struct user, regs.PC), pc - 1) < 0) return -1; return 0; } /**** Hlani funkce rodice, ktera ovlada chovani potomka ****/ void parent (pid_t child_pid) { int status; char breakpoint_insn; long var_value; wait4 (child_pid, &status, WUNTRACED, NULL); if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP) { printf ("Neco je spatne...\n"); exit (1); } printf ("(parent) Potomek zastaven - nastavuji breakpoint\n"); breakpoint_insn = 0xcc; /* int3 == 0xcc */ if (set_text_byte (child_pid, print_var, &breakpoint_insn) < 0) exit (1); ptrace (PTRACE_CONT, child_pid, NULL, NULL); wait4 (child_pid, &status, WUNTRACED, NULL); if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGTRAP) { printf ("Neco je spatne...\n"); exit (1); } printf ("(parent) Potomek zastaven - vracim puvodni instrukci\n"); if (set_text_byte (child_pid, print_var, &breakpoint_insn) < 0) exit (1); printf ("(parent) - dekrementuji instruction pointer\n"); if (decrement_pc (child_pid)) exit (1); var_value = 123; printf ("(parent) - menim hodnoru 'var' na %ld\n", var_value); if (set_data_long (child_pid, &var, &var_value) < 0) exit (1); printf ("(parent) (puvodni hodnota byla %ld)\n", var_value); printf ("(parent) A jedeme dal...\n"); ptrace (PTRACE_CONT, child_pid, NULL, NULL); while (wait4 (child_pid, &status, WUNTRACED, NULL) >= 0) { printf ("(parent) Udalost od potomka: "); if (WIFEXITED (status)) printf ("WEXITSTATUS: %d\n", WEXITSTATUS (status)); else if (WIFSIGNALED (status)) printf ("WTERMSIG: %d\n", WTERMSIG (status)); else if (WIFSTOPPED (status)) { printf ("WSTOPSIG: %d (%s)\n", WSTOPSIG (status), strsignal (WSTOPSIG (status))); } } } /**** Trivialni main - pouze vytvori dva totozne procesy ****/ int main () { pid_t child_pid; if ((child_pid = fork ())) parent (child_pid); else child (); exit (0); }