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

Download the raw C source

/*
 * 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); }