ptrace options not working at all

1k views Asked by At

I couldn't trace fork / exec events when I attached to another process, the status returned from waitpid was always zero (after right shift of 16 times).

I've successfully attached to bash shell, but whatever commands I ran, the status was always zero, so I didn't catch any fork or exec events:

#define PALL PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE \
    | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXIT

int main(int argc, char **argv)
{ 
    pid_t child = 0;
    int status = 0;

    if (argc != 2) { ... }
    child = atoi (argv[1]);
    if (ptrace (PTRACE_ATTACH, child, 0, 0) < 0) { ... }

    ptrace(PTRACE_SETOPTIONS, child, NULL, PALL);
    ptrace(PTRACE_SYSCALL, child, NULL, NULL);
    ptrace(PTRACE_CONT, child, NULL, NULL);

    while(1) {
        waitpid(child, &status, 0);
        if(WIFEXITED(status))
            break;

        status >>= 16;
        if (status != 0)
            printf ("Status: %d\n", status >> 16);

        ptrace(PTRACE_SYSCALL, child, NULL, NULL);
    }

    ptrace(PTRACE_DETACH, child, NULL, NULL);
    return 0;
}
1

There are 1 answers

0
Armali On

the status returned from waitpid was always zero (after right shift of 16 times).

You shifted the status bits not 16, but 32 times by doing status >>= 16 first and then printf (…, status >> 16); so always 0 was printed. Also there are some less obvious flaws:

  • For the sake of completeness, we should handle the case WIFSIGNALED.
  • We should pass signals to the child; it might need them to function correctly.
  • Since we also trace the child's children, we have to wait for and process them as well.
  • PTRACE_SYSCALL immediately preceding PTRACE_CONT doesn't make sense.
  • PTRACE_SETOPTIONS may fail if PTRACE_ATTACH has not yet completed, thus we must wait.

Considering all this, the program could look like

#include <stdio.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#define PALL PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE \
           | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXIT
int main(int argc, char **argv)
{ 
    pid_t child, pid;
    int status;
    if (argc != 2) { return 2; }
    child = atoi(argv[1]);
    if (ptrace(PTRACE_ATTACH, child, 0, 0) < 0) { return 1; }
    wait(NULL); // PTRACE_SETOPTIONS may work only after this
    ptrace(PTRACE_SETOPTIONS, child, NULL, PALL);
    ptrace(PTRACE_CONT, child, NULL, NULL);
    while (pid = wait(&status), pid > 0)
    {
        if (WIFEXITED(status) || WIFSIGNALED(status))
        {
            if (pid == child) break;
            printf("grandchild %d exited\n", pid);
            continue;
        }
        if (status>>16)
            printf("[%d] Status: %x\n", pid, status);
        int signal = WSTOPSIG(status);
        if (signal == SIGTRAP)
        {   // system call or ptrace event
            signal = 0;
            if (status>>16)
                printf("ptrace event: %d\n", status>>16);
        }
        ptrace(PTRACE_CONT, pid, 0, signal);
    }
    ptrace(PTRACE_DETACH, child, NULL, NULL);
    return 0;
}