From f1da8108620a8560436a815f52a82439b16b6ee4 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Mon, 7 Nov 2022 15:28:03 +0100 Subject: add "Fun with ptrace" posts --- _posts/2022-11-07-ptrace-sigtraps.md | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 _posts/2022-11-07-ptrace-sigtraps.md (limited to '_posts/2022-11-07-ptrace-sigtraps.md') diff --git a/_posts/2022-11-07-ptrace-sigtraps.md b/_posts/2022-11-07-ptrace-sigtraps.md new file mode 100644 index 0000000..d3a7e5e --- /dev/null +++ b/_posts/2022-11-07-ptrace-sigtraps.md @@ -0,0 +1,72 @@ +--- +title: 'Fun with ptrace: SIGTRAPs galore' +date: 2022-11-07 13:00 +0100 +--- +When using `PTRACE_ATTACH` the `ptrace` mechanism reuses SIGTRAP for a number +of things by default. +This makes it unnecessarily hard to distinguish regular traps possibly caused +by breakpoints we might place from other events. + +1. After `ptrace(PTRACE_SYSCALL)`, syscall-stops will be reported as SIGTRAPs. + + ```c + int status; + + ptrace(PTRACE_SYSCALL, pid, 0, 0); + waitpid(pid, &status, 0); + + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { + /* We don't know if the tracee has just entered/exited a syscall or + * received a regular SIGTRAP (could be caused by a breakpoint we + * placed). */ + } + ``` + + This is fixed by using the `PTRACE_O_TRACESYSGOOD` option. + + ```c + int status; + + ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD); + ptrace(PTRACE_SYSCALL, pid, 0, 0); + waitpid(pid, &status, 0); + + if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) { + /* We know for sure that the tracee has just entered/exited a + * syscall. */ + } + ``` + +2. Every `execve` call will be reported as a SIGTRAP. + + ```c + int status; + + ptrace(PTRACE_CONT, pid, 0, 0); + waitpid(pid, &status, 0); + + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { + /* We don't know if the tracee just called execve() or received a + * regular SIGTRAP (could be caused by a breakpoint we placed). */ + } + ``` + + This is fixed by using the `PTRACE_O_TRACEEXEC` option. + + ```c + int status; + + ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC); + ptrace(PTRACE_CONT, pid, 0, 0); + waitpid(pid, &status, 0); + + if (WIFSTOPPED(status) && status >> 8 == (SIGTRAP | PTRACE_EVENT_EXEC << 8)) { + /* We know for sure that the tracee has just called execve(). */ + } + ``` + + This point doesn't apply to tracees attached using `PTRACE_SEIZE`. + {: .alert .alert-info } + +As you can see, you should always use at least the `PTRACE_O_TRACESYSGOOD` and +`PTRACE_O_TRACEEXEC` options to be able to distinguish between SIGTRAPs. -- cgit v1.2.3