Reputation: 129
We have a school project where we need to re-code "strace".
We have to only intercept syscall like write and read, but we cannot use PTRACE_SYSCALL
. I'm looking for a way to do that using PTRACE_SINGLESTEP
, I've already coded a way to print the syscall and when I'm using PTRACE_SYSCALL
it works fine, but when I use PTRACE_SINGLESTEP
I can't find a way to only print the syscalls.
Here is the code I use, maybe someone can help me figure out what's wrong with it:
pid_t child;
long orig_eax;
user_regs_struct regs;
child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, 0, 0);
execve("/home/architek/a.out", {"/home/architek/a.out", NULL}, envp);
} else {
waitpid(child, &status, 0);
while (WIFSTOPPED(status)) {
orig_eax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
ptrace(PTRACE_GETREGS, child, NULL, ®s);
call_printer(®s, child);
ptrace(PTRACE_SINGLESTEP, child, 0, 0);
waitpid(child, &status, 0);
}
}
Upvotes: 2
Views: 835
Reputation: 69276
If you cannot use PTRACE_SYSCALL
to stop the child right before/after a syscall, then you will have to manually detect when one is about to happen. I doubt that checking the source code of strace
would help, since strace
is most likely using PTRACE_SYSCALL
, no reason to manually decode instructions.
Assuming you are working on x86-64, here's how you can do it:
PTRACE_SINGLESTEP
, keep stepping one instruction at a time.PTRACE_PEEKTEXT
to fetch the next instruction pointed by the instruction pointer.syscall
instruction by comparing the bytes with the opcode of syscall
, which is two bytes: 0x0f 0x05
. Since x86 is little endian, this means checking whether the return value of ptrace(PTRACE_PEEKDATA, ...)
has the two least significant bytes set to 0x050f
.NOTE: If you are on another architecture, or if you also want to detect 32-bit syscalls, you can simply check for different/more values on step 3. On Linux x86-64, there are multiple ways to issue a syscall, with different opcodes. For example, 32-bit syscalls on Linux are done through int 0x80
(opcode 0xcd 0x80
). Check this other answer of mine for a list.
Here's an example:
#include <errno.h>
long opcode;
// ...
waitpid(child, &status, 0);
while (WIFSTOPPED(status)) {
ptrace(PTRACE_GETREGS, child, NULL, ®s);
errno = 0;
opcode = ptrace(PTRACE_PEEKTEXT, child, regs.rip, 0);
if (opcode == -1 && errno != 0) {
perror("ptrace(PTRACE_PEEK_DATA) failed");
exit(1);
}
if (((unsigned long)opcode & 0xffff) == 0x050f) {
// Child about to execute a syscall instruction,
// check the registers to know more...
}
ptrace(PTRACE_SINGLESTEP, child, 0, 0);
waitpid(child, &status, 0);
}
Upvotes: 4