Programs and patches
ptrace demo
A compact demonstration of the ptrace system call.
Last updated · 7 April 2006
Ukázkový program k článku Napište si debugger (vyšlo též na root.cz dne 29.10.2003).
Program je funkční v linuxu na platrofmách i386 a amd64, na jiných architekturách by bylo potřeba upravit nastavování a obsluhu breakpointů.
Source code
/* * ptrace-demo.c * - Doprovodny program k clanku "Napiste si debugger" * * Michal Ludvig <michal@logix.cz> (c) 2003 * Homepage: /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); }