I am trying to change a calling from one syscall to a different one on linux arm64 using ptrace.
From what i understand the syscall number is in x8, reading the registers of the syscalls confirms this. im changing this number and calling SETREGSET and the old syscall is called and not the new one. when i check the registers on the return from the syscall x8 is set to the syscall I gave him as it should be.
Changing other parameters for the same syscall works.
I saw some places using PTRACE_SET_SYSCALL but i couldn't find a lot about it, I tried to use it but it seems like its not supported in this arch, it isnt defined and writing the number instead failed for not existing.
What am i doing wrong? why doesn't it work?
Here is the code, i removed prints and validations for simplicity, for this example im only trying to stop the write syscall:
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
int status;
waitpid(pid, &status, 0);
while (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == 0)
{
waitpid(pid, &status, 0);
struct user_pt_regs regs;
struct iovec io;
io.iov_base = ®s;
io.iov_len = sizeof(regs);
ptrace(PTRACE_GETREGSET, pid, (void*)NT_PRSTATUS, &io);
// reg[7] is 0 before syscall and 1 after
if (regs.regs[7] == 0)
{
// Change write syscall
if (regs.regs[8] == 64)
{
// Change the syscall to getpid (doesn't matter)
regs.regs[8] = 172;
ptrace(PTRACE_SETREGSET, pid, (void*)NT_PRSTATUS, &io);
}
}
}
As for the tracee, a simple hello world with write with sleep;
char buf[] = "hello world\n";
while(1)
{
write(1, buf, sizeof(buf));
sleep(5);
}
Although the program runs with no errors and prints the right registers the syscall doesnt change and it keep printing hello world