#include #include #include #include #include #include #include #include void setbp(pid_t pid, void *addr, long* orig) { union { long word; unsigned char bytes[4]; }u; *orig = u.word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL); u.bytes[0] = 0xcc; // 0xcc is INT3 ptrace(PTRACE_POKETEXT, pid, addr, u.word); printf("set breakpoint (0x%lx) at 0x%lx.\n", u.word, (unsigned long)addr); } void unsetbp(pid_t pid, void *addr, long orig) { printf("remove breakpoint at 0x%lx.\n", (unsigned long)addr); ptrace(PTRACE_POKETEXT, pid, addr, orig); struct user_regs_struct regs; ptrace(PTRACE_GETREGS, pid, 0, ®s); #if __WORDSIZE == 64 regs.rip = (long)addr; #else regs.eip = (long)addr; #endif ptrace(PTRACE_SETREGS, pid, 0, ®s); } void breakpoint(pid_t pid, void* addr) { long orig; while(1) { setbp(pid, addr, &orig); printf("executing...\n"); ptrace(PTRACE_CONT, pid, 0, 0); wait(NULL); printf("breakpoint hit. press return to continue\n"); getchar(); unsetbp(pid, addr, orig); // single step to next instruction, so we can set // breakpoint again ptrace(PTRACE_SINGLESTEP, pid, 0, 0); wait(NULL); } } int main(int ac, char *av[]) { if(ac == 2) { // test loop, executed by child process. int i = 0; while(1) { printf ("debugee: %d\n", i++); bp_addr: sleep(2); } return 0; } int pid; switch(pid=fork()) { case -1: perror("fork"); break; case 0: ptrace(PTRACE_TRACEME, 0, 0, 0); // exec myself, but with one argument execlp(av[0], av[0], "loop", NULL); break; default: wait(NULL); ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC); breakpoint(pid, &&bp_addr); break; } return 0; }