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