Rizin
unix-like reverse engineering framework and cli tools
ptrace-wrap

ptrace on Linux has one major issue: When one process attaches to another, the tracer's pid is associated to the tracee's pid. However, because different threads in the tracer have different tids, which are actually just pids on Linux, only the one thread that started the trace can continue to call ptrace for the tracee.

This small library solves this issue by providing a thin wrapper around ptrace, executing all ptrace calls on a single thread. Thus, it is possible to create multi-threaded applications that use ptrace.

Usage

Initialize a ptrace-wrap instance:

if (r != 0) {
// fail
}
#define r
Definition: crypto_rc6.c:12
int ptrace_wrap_instance_start(ptrace_wrap_instance *inst)
Definition: ptrace_wrap.c:14

Then, simply use the ptrace_wrap() function instead of ptrace() for your calls.

long pr = ptrace_wrap (&inst, <request>, <pid>, <addr>, <data>);
if (pr < 0) {
perror ("ptrace");
}
static static sync static getppid static getegid const char static filename request
Definition: sflib.h:62
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc pid
Definition: sflib.h:64
long ptrace_wrap(ptrace_wrap_instance *inst, ptrace_wrap_ptrace_request request, pid_t pid, void *addr, void *data)
Definition: ptrace_wrap.c:90
static int addr
Definition: z80asm.c:58

If a child process should be spawned with fork() and traced by calling PTRACE_TRACEME from inside of it, ptrace_wrap_fork() has to be used instead of plain fork(). The behaviour in the child process is slightly different then. Instead of returning 0, ptrace_wrap_fork() calls a callback in the child process. Example:

void child_callback(void *user) {
const char *file = user;
execl (file, file, NULL);
perror ("execl");
exit (1);
}
// ...
pid_t pid = ptrace_wrap_fork (&inst, child_callback, "<path to an executable>");
if (pid < 0) {
perror ("fork");
}
#define NULL
Definition: cris-opc.c:27
static static fork const void static count static fd const char static mode const char static pathname const char static path const char static dev const char static group static getpid static getuid ptrace
Definition: sflib.h:57
pid_t ptrace_wrap_fork(ptrace_wrap_instance *inst, void(*child_callback)(void *), void *child_callback_user)
Definition: ptrace_wrap.c:108
int pid_t
Definition: sftypes.h:38
@ PTRACE_TRACEME
Definition: sftypes.h:556
Definition: gzappend.c:170

Finally, stop the ptrace-wrap instance:

void ptrace_wrap_instance_stop(ptrace_wrap_instance *inst)
Definition: ptrace_wrap.c:36

Please note that ptrace-wrap is absolutely not thread-safe! It is only a solution for specific the pid/tid issue of ptrace. If you need thread-safety, you must synchronize the access yourself.

About

Created by Florian Märkl for rizin and Cutter.

ptrace-wrap is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ptrace-wrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ptrace-wrap. If not, see <http://www.gnu.org/licenses/>.