Anonymous
Anonymous

Reputation: 77

How to use ptrace(2) to change behaviour of syscalls? My example is not working, why?

Can you please tell me why child prints -38 instead of 999? I would like to change system call data returned to the caller. So I set 999 to make getpid to return 999 but it returns -38. I compiled and run on Linux Ubuntu x86_64

#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <syscall.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>


int main(int argc, char **argv)
{
    int pid = fork();
    if (pid == 0) { //child
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        kill(getpid(), SIGSTOP);
        int r = getpid();
        printf("child pid %d from child\n", r);
    } else { //parent
        printf("child pid %d from parent\n", pid);
        waitpid(pid, 0, 0);
        ptrace(PTRACE_SYSCALL, pid, 0, 0);
        waitpid(pid, 0, 0);

        struct user_regs_struct uregs;
        ptrace(PTRACE_GETREGS, pid, 0, &uregs);

        if (uregs.orig_rax == SYS_getpid) {
            puts("Yes! It is getpid.");
            uregs.orig_rax = 999;
            ptrace(PTRACE_SETREGS, pid, 0, &uregs);
            ptrace(PTRACE_CONT, pid, 0, 0);
        } else {
            puts("NO!!! It is not getpid.");
        }
    }
    return 0;
}

Upvotes: 1

Views: 387

Answers (1)

Anonymous
Anonymous

Reputation: 77

Thanks to @Alex

I have modified my example to make it work as I want to. And I want to parent to replace return value of getpid() which the child called.

#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <syscall.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>


int main(int argc, char **argv)
{
    int pid = fork();
    if (pid == 0) { //child
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        int r = getpid();
        printf("stopping pid %d\n", r);
        kill(r, SIGSTOP);
        r = getpid();
        printf("child pid %d from child, errno %s\n", r, strerror(errno));
    } else { //parent
        printf("child pid %d from parent\n", pid);
        waitpid(pid, 0, 0);
        //
        // stopped before to call getpid()
        // 
        ptrace(PTRACE_SYSCALL, pid, 0, 0);
        waitpid(pid, 0, 0);

        struct user_regs_struct regs;
        //
        // get registers to check if we are at right systemcall - getpid
        // 
        ptrace(PTRACE_GETREGS, pid, 0, &regs);

        if (regs.orig_rax != SYS_getpid) {
            puts("NO!!! It is not getpid.");
            exit(1);
        }

        // 
        // execute getpid() and stop right after exit of getpid()
        //
        ptrace(PTRACE_SYSCALL, pid, 0, 0);
        waitpid(pid, 0, 0);

        ptrace(PTRACE_GETREGS, pid, 0, &regs);
        //
        // change return value to 999
        // 
        regs.rax = 999;
        ptrace(PTRACE_SETREGS, pid, 0, &regs);
    }
    return 0;
}

Upvotes: 1

Related Questions